test(credentials): add more test cases

This commit is contained in:
Aleksey Kulikov 2021-10-21 12:40:45 +03:00
parent 26812ffe9c
commit 4948bba773
4 changed files with 162 additions and 120 deletions

View file

@ -1,34 +1,9 @@
import 'dart:ffi'; import 'dart:ffi';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import '../error.dart';
import '../util.dart'; import '../util.dart';
import 'libgit2_bindings.dart'; import 'libgit2_bindings.dart';
/// Create a credential to specify a username.
///
/// This is used with ssh authentication to query for the username if none is
/// specified in the url.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_credential> username(String username) {
final out = calloc<Pointer<git_credential>>();
final usernameC = username.toNativeUtf8().cast<Int8>();
final error = libgit2.git_credential_username_new(out, usernameC);
calloc.free(usernameC);
if (error < 0) {
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Create a new plain-text username and password credential object. /// Create a new plain-text username and password credential object.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_credential> userPass({ Pointer<git_credential> userPass({
required String username, required String username,
required String password, required String password,
@ -37,26 +12,15 @@ Pointer<git_credential> userPass({
final usernameC = username.toNativeUtf8().cast<Int8>(); final usernameC = username.toNativeUtf8().cast<Int8>();
final passwordC = password.toNativeUtf8().cast<Int8>(); final passwordC = password.toNativeUtf8().cast<Int8>();
final error = libgit2.git_credential_userpass_plaintext_new( libgit2.git_credential_userpass_plaintext_new(out, usernameC, passwordC);
out,
usernameC,
passwordC,
);
calloc.free(usernameC); calloc.free(usernameC);
calloc.free(passwordC); calloc.free(passwordC);
if (error < 0) { return out.value;
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
} }
/// Create a new passphrase-protected ssh key credential object. /// Create a new passphrase-protected ssh key credential object.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_credential> sshKey({ Pointer<git_credential> sshKey({
required String username, required String username,
required String publicKey, required String publicKey,
@ -69,7 +33,7 @@ Pointer<git_credential> sshKey({
final privateKeyC = privateKey.toNativeUtf8().cast<Int8>(); final privateKeyC = privateKey.toNativeUtf8().cast<Int8>();
final passPhraseC = passPhrase.toNativeUtf8().cast<Int8>(); final passPhraseC = passPhrase.toNativeUtf8().cast<Int8>();
final error = libgit2.git_credential_ssh_key_new( libgit2.git_credential_ssh_key_new(
out, out,
usernameC, usernameC,
publicKeyC, publicKeyC,
@ -82,36 +46,22 @@ Pointer<git_credential> sshKey({
calloc.free(privateKeyC); calloc.free(privateKeyC);
calloc.free(passPhraseC); calloc.free(passPhraseC);
if (error < 0) { return out.value;
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
} }
/// Create a new ssh key credential object used for querying an ssh-agent. /// Create a new ssh key credential object used for querying an ssh-agent.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_credential> sshKeyFromAgent(String username) { Pointer<git_credential> sshKeyFromAgent(String username) {
final out = calloc<Pointer<git_credential>>(); final out = calloc<Pointer<git_credential>>();
final usernameC = username.toNativeUtf8().cast<Int8>(); final usernameC = username.toNativeUtf8().cast<Int8>();
final error = libgit2.git_credential_ssh_key_from_agent(out, usernameC); libgit2.git_credential_ssh_key_from_agent(out, usernameC);
calloc.free(usernameC); calloc.free(usernameC);
if (error < 0) { return out.value;
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
} }
/// Create a new ssh key credential object reading the keys from memory. /// Create a new ssh key credential object reading the keys from memory.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_credential> sshKeyFromMemory({ Pointer<git_credential> sshKeyFromMemory({
required String username, required String username,
required String publicKey, required String publicKey,
@ -124,7 +74,7 @@ Pointer<git_credential> sshKeyFromMemory({
final privateKeyC = privateKey.toNativeUtf8().cast<Int8>(); final privateKeyC = privateKey.toNativeUtf8().cast<Int8>();
final passPhraseC = passPhrase.toNativeUtf8().cast<Int8>(); final passPhraseC = passPhrase.toNativeUtf8().cast<Int8>();
final error = libgit2.git_credential_ssh_key_memory_new( libgit2.git_credential_ssh_key_memory_new(
out, out,
usernameC, usernameC,
publicKeyC, publicKeyC,
@ -137,10 +87,5 @@ Pointer<git_credential> sshKeyFromMemory({
calloc.free(privateKeyC); calloc.free(privateKeyC);
calloc.free(passPhraseC); calloc.free(passPhraseC);
if (error < 0) { return out.value;
calloc.free(out);
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
} }

View file

@ -1,6 +1,7 @@
import 'dart:ffi'; import 'dart:ffi';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/libgit2dart.dart';
import 'package:libgit2dart/src/util.dart';
import '../credentials.dart'; import '../credentials.dart';
import '../callbacks.dart'; import '../callbacks.dart';
import '../repository.dart'; import '../repository.dart';
@ -120,21 +121,34 @@ class RemoteCallbacks {
int allowedTypes, int allowedTypes,
Pointer<Void> payload, Pointer<Void> payload,
) { ) {
final credentialType = credentials!.credentialType; if (payload.cast<Int8>().value == 2) {
if (allowedTypes & credentialType.value != credentialType.value) { libgit2.git_error_set_str(
throw ArgumentError('Invalid credential type $credentialType'); git_error_t.GIT_ERROR_INVALID,
'Incorrect credentials.'.toNativeUtf8().cast<Int8>(),
);
throw LibGit2Error(libgit2.git_error_last());
} }
if (credentials is Username) { final credentialType = credentials!.credentialType;
final cred = credentials as Username;
credPointer[0] = credentials_bindings.username(cred.username); if (allowedTypes & credentialType.value != credentialType.value) {
} else if (credentials is UserPass) { libgit2.git_error_set_str(
git_error_t.GIT_ERROR_INVALID,
'Invalid credential type $credentialType'.toNativeUtf8().cast<Int8>(),
);
throw LibGit2Error(libgit2.git_error_last());
}
if (credentials is UserPass) {
final cred = credentials as UserPass; final cred = credentials as UserPass;
credPointer[0] = credentials_bindings.userPass( credPointer[0] = credentials_bindings.userPass(
username: cred.username, username: cred.username,
password: cred.password, password: cred.password,
); );
} else if (credentials is Keypair) { payload.cast<Int8>().value++;
}
if (credentials is Keypair) {
final cred = credentials as Keypair; final cred = credentials as Keypair;
credPointer[0] = credentials_bindings.sshKey( credPointer[0] = credentials_bindings.sshKey(
username: cred.username, username: cred.username,
@ -142,10 +156,16 @@ class RemoteCallbacks {
privateKey: cred.privateKey, privateKey: cred.privateKey,
passPhrase: cred.passPhrase, passPhrase: cred.passPhrase,
); );
} else if (credentials is KeypairFromAgent) { payload.cast<Int8>().value++;
}
if (credentials is KeypairFromAgent) {
final cred = credentials as KeypairFromAgent; final cred = credentials as KeypairFromAgent;
credPointer[0] = credentials_bindings.sshKeyFromAgent(cred.username); credPointer[0] = credentials_bindings.sshKeyFromAgent(cred.username);
} else if (credentials is KeypairFromMemory) { payload.cast<Int8>().value++;
}
if (credentials is KeypairFromMemory) {
final cred = credentials as KeypairFromMemory; final cred = credentials as KeypairFromMemory;
credPointer[0] = credentials_bindings.sshKeyFromMemory( credPointer[0] = credentials_bindings.sshKeyFromMemory(
username: cred.username, username: cred.username,
@ -153,6 +173,7 @@ class RemoteCallbacks {
privateKey: cred.privateKey, privateKey: cred.privateKey,
passPhrase: cred.passPhrase, passPhrase: cred.passPhrase,
); );
payload.cast<Int8>().value++;
} }
return 0; return 0;
@ -199,6 +220,8 @@ class RemoteCallbacks {
if (callbacks.credentials != null) { if (callbacks.credentials != null) {
credentials = callbacks.credentials; credentials = callbacks.credentials;
final payload = calloc<Int8>()..value = 1;
callbacksOptions.payload = payload.cast();
callbacksOptions.credentials = Pointer.fromFunction( callbacksOptions.credentials = Pointer.fromFunction(
credentialsCb, credentialsCb,
except, except,

View file

@ -5,20 +5,6 @@ abstract class Credentials {
GitCredential get credentialType; GitCredential get credentialType;
} }
/// Credential with specific username.
class Username implements Credentials {
const Username(this.username);
/// The username to authenticate with.
final String username;
@override
GitCredential get credentialType => GitCredential.username;
@override
String toString() => 'Username{username: $username}';
}
/// Plain-text username and password credential. /// Plain-text username and password credential.
class UserPass implements Credentials { class UserPass implements Credentials {
const UserPass({required this.username, required this.password}); const UserPass({required this.username, required this.password});

View file

@ -17,15 +17,6 @@ void main() {
} }
}); });
group('Credentials', () { group('Credentials', () {
test('successfully initializes username credentials', () {
final credentials = const Username('user');
expect(credentials, isA<Credentials>());
expect(credentials.username, 'user');
expect(credentials.credentialType, GitCredential.username);
expect(credentials.toString(), contains('Username{'));
});
test('successfully initializes username/password credentials', () { test('successfully initializes username/password credentials', () {
final credentials = const UserPass( final credentials = const UserPass(
username: 'user', username: 'user',
@ -82,18 +73,28 @@ void main() {
expect(credentials.toString(), contains('KeypairFromAgent{')); expect(credentials.toString(), contains('KeypairFromAgent{'));
}); });
test('sucessfully clones repository with provided username', () { test('throws when provided username and password are incorrect', () {
final callbacks = const Callbacks(credentials: Username('git')); final callbacks = const Callbacks(
credentials: UserPass(
final repo = Repository.clone( username: 'libgit2',
url: 'https://git@github.com/libgit2/TestGitRepository', password: 'libgit2',
localPath: cloneDir.path, ),
callbacks: callbacks,
); );
expect(repo.isEmpty, false); expect(
() => Repository.clone(
repo.free(); url: 'https://github.com/github/github',
localPath: cloneDir.path,
callbacks: callbacks,
),
throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"Incorrect credentials.",
),
),
);
}); });
test('sucessfully clones repository with provided keypair', () { test('sucessfully clones repository with provided keypair', () {
@ -122,7 +123,13 @@ void main() {
url: 'ssh://git@github.com/libgit2/TestGitRepository', url: 'ssh://git@github.com/libgit2/TestGitRepository',
localPath: cloneDir.path, localPath: cloneDir.path,
), ),
throwsA(isA<LibGit2Error>()), throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"authentication required but no callback set",
),
),
); );
}); });
@ -141,7 +148,62 @@ void main() {
localPath: cloneDir.path, localPath: cloneDir.path,
callbacks: callbacks, callbacks: callbacks,
), ),
throwsA(isA<LibGit2Error>()), throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"Failed to authenticate SSH session: Unable to open public key file",
),
),
);
});
test('throws when provided keypair is incorrect', () {
final keypair = const Keypair(
username: 'git',
pubKey: 'test/assets/keys/id_rsa.pub',
privateKey: 'incorrect',
passPhrase: 'empty',
);
final callbacks = Callbacks(credentials: keypair);
expect(
() => Repository.clone(
url: 'ssh://git@github.com/libgit2/TestGitRepository',
localPath: cloneDir.path,
callbacks: callbacks,
),
throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"Incorrect credentials.",
),
),
);
});
test('throws when provided credential type is invalid', () {
final callbacks = const Callbacks(
credentials: UserPass(
username: 'libgit2',
password: 'libgit2',
),
);
expect(
() => Repository.clone(
url: 'ssh://git@github.com/libgit2/TestGitRepository',
localPath: cloneDir.path,
callbacks: callbacks,
),
throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"Invalid credential type GitCredential.userPassPlainText",
),
),
); );
}); });
@ -167,23 +229,49 @@ void main() {
repo.free(); repo.free();
}); });
test('sucessfully clones repository with provided username and password', test('throws when provided keypair from memory is incorrect', () {
() { final pubKey = File('test/assets/keys/id_rsa.pub').readAsStringSync();
final userPass = const UserPass( final keypair = KeypairFromMemory(
username: 'libgit2', username: 'git',
password: 'libgit2', pubKey: pubKey,
privateKey: 'incorrect',
passPhrase: 'empty',
); );
final callbacks = Callbacks(credentials: userPass); final callbacks = Callbacks(credentials: keypair);
final repo = Repository.clone( expect(
url: 'https://github.com/libgit2/TestGitRepository', () => Repository.clone(
localPath: cloneDir.path, url: 'ssh://git@github.com/libgit2/TestGitRepository',
callbacks: callbacks, localPath: cloneDir.path,
callbacks: callbacks,
),
throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"Incorrect credentials.",
),
),
); );
});
expect(repo.isEmpty, false); test('throws when provided keypair from agent is incorrect', () {
final callbacks = const Callbacks(credentials: KeypairFromAgent('git'));
repo.free(); expect(
() => Repository.clone(
url: 'ssh://git@github.com/libgit2/TestGitRepository',
localPath: cloneDir.path,
callbacks: callbacks,
),
throwsA(
isA<LibGit2Error>().having(
(e) => e.toString(),
'error',
"Incorrect credentials.",
),
),
);
}); });
}); });
} }