-
Notifications
You must be signed in to change notification settings - Fork 281
Add a GTCred wrapper. #254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c9d19c4
4767309
7d58522
e8c3dde
c472898
cff6753
23b4253
2823345
6cc9a66
64c0c6d
05c21a1
4b5b35a
194543b
f3db7d0
3ad8518
47e0426
4b53195
7617ee7
b66ba1a
f21c87a
4832625
cc2670f
29f132b
3fb9a46
0f9b7c9
9d69126
e29d347
32c00c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// GTCredential+Private.h | ||
// ObjectiveGitFramework | ||
// | ||
// Created by Etienne on 10/09/13. | ||
// Copyright (c) 2013 GitHub, Inc. All rights reserved. | ||
// | ||
|
||
#import "GTCredential.h" | ||
|
||
// If you need to authenticate an operation in libgit2, you'll have to have | ||
// a `GTCredentialProvider` handy, like a parameter in the method you're writing, | ||
// setup a GTCredentialAcquireCallbackInfo struct on the stack, and pass both as | ||
// the arguments to the operation you're attempting. | ||
// | ||
// Example: ``` | ||
// struct GTCredentialAcquireCallbackInfo info = { .credProvider = myProvider } | ||
// git_remote_set_cred_acquire_cb(&git_remote, GTCredentialAcquireCallback, &payload); | ||
// ``` | ||
// | ||
// `GTCredentialAcquireCallback` will act as a trampoline, and will ask the | ||
// `GTCredentialProvider` for a `GTCredential` object corresponding to the | ||
// information requested by `libgit2`. It is the providers's responsibility | ||
// to check the auth type and initialize its `GTCredential` object accordingly. | ||
|
||
typedef struct { | ||
__unsafe_unretained GTCredentialProvider *credProvider; | ||
} GTCredentialAcquireCallbackInfo; | ||
|
||
int GTCredentialAcquireCallback(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload); | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// | ||
// GTCredential.h | ||
// ObjectiveGitFramework | ||
// | ||
// Created by Etienne on 10/09/13. | ||
// Copyright (c) 2013 GitHub, Inc. All rights reserved. | ||
// | ||
|
||
#import "git2.h" | ||
|
||
// An enum describing the data needed for authentication. | ||
// See `git_credtype_t`. | ||
typedef enum { | ||
GTCredentialTypeUserPassPlaintext = GIT_CREDTYPE_USERPASS_PLAINTEXT, | ||
GTCredentialTypeSSHKeyFilePassPhrase = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE, | ||
GTCredentialTypeSSHPublicKey = GIT_CREDTYPE_SSH_PUBLICKEY, | ||
} GTCredentialType; | ||
|
||
@class GTCredential; | ||
|
||
// The GTCredentialProvider acts as a proxy for GTCredential requests. | ||
// | ||
// The default implementation is used through `+providerWithBlock:`, | ||
// passing your own block that will build a GTCredential object. | ||
// But you're allowed to subclass it and handle more complex workflows. | ||
@interface GTCredentialProvider : NSObject | ||
|
||
// Creates a provider from a block. | ||
// | ||
// credentialBlock - a block that will be called when credentials are requested. | ||
+ (instancetype)providerWithBlock:(GTCredential *(^)(GTCredentialType type, NSString *URL, NSString *userName))credentialBlock; | ||
|
||
// Default credential provider method. | ||
// | ||
// This method will get called when an operation requests credentials from the | ||
// provider. | ||
// | ||
// The default implementation calls through the `providedBlock` passed | ||
// in `providerWithBlock:` above, but your subclass is expected to override it | ||
// to do its specific work. | ||
// | ||
// type - the credential types allowed by the operation. | ||
// URL - the URL the operation is authenticating against. | ||
// userName - the user name provided by the operation. Can be nil, and might be ignored. | ||
- (GTCredential *)credentialForType:(GTCredentialType)type URL:(NSString *)URL userName:(NSString *)userName; | ||
@end | ||
|
||
// The GTCredential class is used to provide authentication data. | ||
// It acts as a wrapper around `git_cred` objects. | ||
@interface GTCredential : NSObject | ||
|
||
// Create a credential object from a username/password pair. | ||
// | ||
// userName - The username to authenticate as. | ||
// password - The password belonging to that user. | ||
// error - If not NULL, set to any errors that occur. | ||
// | ||
// Return a new GTCredential instance, or nil if an error occurred | ||
+ (instancetype)credentialWithUserName:(NSString *)userName password:(NSString *)password error:(NSError **)error; | ||
|
||
// Create a credential object from a SSH keyfile | ||
// | ||
// userName - The username to authenticate as. | ||
// publicKeyURL - The URL to the public key for that user. | ||
// Can be omitted to reconstruct the public key from the private key. | ||
// privateKeyURL - The URL to the private key for that user. | ||
// passphrase - The passPhrase for the private key. Optional if the private key has no password. | ||
// error - If not NULL, set to any errors that occur. | ||
// | ||
// Return a new GTCredential instance, or nil if an error occurred | ||
+ (instancetype)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL *)publicKeyURL privateKeyURL:(NSURL *)privateKeyURL passphrase:(NSString *)passphrase error:(NSError **)error; | ||
|
||
// The underlying `git_cred` object. | ||
- (git_cred *)git_cred __attribute__((objc_returns_inner_pointer)); | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// | ||
// GTCredential.m | ||
// ObjectiveGitFramework | ||
// | ||
// Created by Etienne on 10/09/13. | ||
// Copyright (c) 2013 GitHub, Inc. All rights reserved. | ||
// | ||
|
||
#import <ObjectiveGit/NSError+Git.h> | ||
#import "GTCredential.h" | ||
#import "GTCredential+Private.h" | ||
#import <libssh2.h> | ||
|
||
typedef GTCredential *(^GTCredentialProviderBlock)(GTCredentialType allowedTypes, NSString *URL, NSString *userName); | ||
|
||
@interface GTCredentialProvider () | ||
@property (nonatomic, readonly, copy) GTCredentialProviderBlock credBlock; | ||
@end | ||
|
||
@implementation GTCredentialProvider | ||
+ (instancetype)providerWithBlock:(GTCredentialProviderBlock)credentialBlock { | ||
NSParameterAssert(credentialBlock != nil); | ||
|
||
GTCredentialProvider *provider = [[self alloc] init]; | ||
|
||
provider->_credBlock = [credentialBlock copy]; | ||
|
||
return provider; | ||
} | ||
|
||
- (GTCredential *)credentialForType:(GTCredentialType)type URL:(NSString *)URL userName:(NSString *)userName { | ||
NSAssert(self.credBlock != nil, @"Provider asked for credentials without block being set."); | ||
|
||
return self.credBlock(type, URL, userName); | ||
} | ||
|
||
@end | ||
|
||
@interface GTCredential () | ||
@property (nonatomic, assign, readonly) git_cred *git_cred; | ||
@end | ||
|
||
@implementation GTCredential | ||
|
||
+ (instancetype)credentialWithUserName:(NSString *)userName password:(NSString *)password error:(NSError **)error { | ||
git_cred *cred; | ||
int gitError = git_cred_userpass_plaintext_new(&cred, userName.UTF8String, password.UTF8String); | ||
if (gitError != GIT_OK) { | ||
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to create credentials object" failureReason:@"There was an error creating a credential object for username %@.", userName]; | ||
return nil; | ||
} | ||
|
||
return [[self alloc] initWithGitCred:cred]; | ||
} | ||
|
||
+ (instancetype)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL *)publicKeyURL privateKeyURL:(NSURL *)privateKeyURL passphrase:(NSString *)passphrase error:(NSError **)error { | ||
NSParameterAssert(privateKeyURL != nil); | ||
NSString *publicKeyPath = publicKeyURL.filePathURL.path; | ||
NSString *privateKeyPath = privateKeyURL.filePathURL.path; | ||
NSAssert(privateKeyPath != nil, @"Invalid file URL passed: %@", privateKeyURL); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see two problems ;-). Maybe it's better to assert because it forces the developer to validate the path before blindly giving it to us (it's gonna get used while the operation is in progress, which means a network operation that is doomed to fail if you don't check before. The other problem is that our current NSError machinery isn't really adapted to non- There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. About the "yet" part, would you prefer me to rename the category and make it our own There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine for now, I guess. Your points about validation are good. |
||
|
||
git_cred *cred; | ||
int gitError = git_cred_ssh_keyfile_passphrase_new(&cred, userName.UTF8String, publicKeyPath.fileSystemRepresentation, privateKeyPath.fileSystemRepresentation, passphrase.UTF8String); | ||
if (gitError != GIT_OK) { | ||
if (error) *error = [NSError git_errorFor:gitError description:@"Failed to create credentials object" failureReason:@"There was an error creating a credential object for username %@ with the provided public/private key pair.\nPublic key: %@\nPrivate key: %@", userName, publicKeyURL, privateKeyURL]; | ||
return nil; | ||
} | ||
|
||
return [[self alloc] initWithGitCred:cred]; | ||
} | ||
|
||
- (instancetype)initWithGitCred:(git_cred *)cred { | ||
NSParameterAssert(cred != nil); | ||
self = [self init]; | ||
|
||
if (self == nil) return nil; | ||
|
||
_git_cred = cred; | ||
|
||
return self; | ||
} | ||
|
||
@end | ||
|
||
int GTCredentialAcquireCallback(git_cred **git_cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload) { | ||
NSCParameterAssert(git_cred != NULL); | ||
NSCParameterAssert(payload != NULL); | ||
|
||
GTCredentialAcquireCallbackInfo *info = payload; | ||
GTCredentialProvider *provider = info->credProvider; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weird indentation in this function. |
||
|
||
if (provider == nil) { | ||
giterr_set_str(GIT_EUSER, "No GTCredentialProvider set, but authentication was requested."); | ||
return GIT_ERROR; | ||
} | ||
|
||
NSString *URL = (url != NULL ? @(url) : nil); | ||
NSString *userName = (username_from_url != NULL ? @(username_from_url) : nil); | ||
|
||
GTCredential *cred = [provider credentialForType:(GTCredentialType)allowed_types URL:URL userName:userName]; | ||
if (cred == nil) { | ||
giterr_set_str(GIT_EUSER, "GTCredentialProvider failed to provide credentials."); | ||
return GIT_ERROR; | ||
} | ||
|
||
*git_cred = cred.git_cred; | ||
return GIT_OK; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be done with a method on
GTCredentialProvider
now.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh ? That's the C trampoline that calls
GTCredentialProvider
, so no, I can't do that. You're supposed to pass it to libgit2 APIs that take agit_cred_aquire_cb
(as in the fetch example I've given above).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, you're right.