Skip to content

Commit ebdf2b3

Browse files
committed
Merge pull request libgit2#254 from libgit2/git-cred
Add a GTCred wrapper.
2 parents 0aa4e3f + 4272f6c commit ebdf2b3

File tree

6 files changed

+275
-15
lines changed

6 files changed

+275
-15
lines changed

Classes/GTCredential+Private.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// GTCredential+Private.h
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 10/09/13.
6+
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import "GTCredential.h"
10+
11+
// If you need to authenticate an operation in libgit2, you'll have to have
12+
// a `GTCredentialProvider` handy, like a parameter in the method you're writing,
13+
// setup a GTCredentialAcquireCallbackInfo struct on the stack, and pass both as
14+
// the arguments to the operation you're attempting.
15+
//
16+
// Example: ```
17+
// struct GTCredentialAcquireCallbackInfo info = { .credProvider = myProvider }
18+
// git_remote_set_cred_acquire_cb(&git_remote, GTCredentialAcquireCallback, &payload);
19+
// ```
20+
//
21+
// `GTCredentialAcquireCallback` will act as a trampoline, and will ask the
22+
// `GTCredentialProvider` for a `GTCredential` object corresponding to the
23+
// information requested by `libgit2`. It is the providers's responsibility
24+
// to check the auth type and initialize its `GTCredential` object accordingly.
25+
26+
typedef struct {
27+
__unsafe_unretained GTCredentialProvider *credProvider;
28+
} GTCredentialAcquireCallbackInfo;
29+
30+
int GTCredentialAcquireCallback(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload);

Classes/GTCredential.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//
2+
// GTCredential.h
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 10/09/13.
6+
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import "git2.h"
10+
11+
// An enum describing the data needed for authentication.
12+
// See `git_credtype_t`.
13+
typedef enum {
14+
GTCredentialTypeUserPassPlaintext = GIT_CREDTYPE_USERPASS_PLAINTEXT,
15+
GTCredentialTypeSSHKeyFilePassPhrase = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
16+
GTCredentialTypeSSHPublicKey = GIT_CREDTYPE_SSH_PUBLICKEY,
17+
} GTCredentialType;
18+
19+
@class GTCredential;
20+
21+
// The GTCredentialProvider acts as a proxy for GTCredential requests.
22+
//
23+
// The default implementation is used through `+providerWithBlock:`,
24+
// passing your own block that will build a GTCredential object.
25+
// But you're allowed to subclass it and handle more complex workflows.
26+
@interface GTCredentialProvider : NSObject
27+
28+
// Creates a provider from a block.
29+
//
30+
// credentialBlock - a block that will be called when credentials are requested.
31+
+ (instancetype)providerWithBlock:(GTCredential *(^)(GTCredentialType type, NSString *URL, NSString *userName))credentialBlock;
32+
33+
// Default credential provider method.
34+
//
35+
// This method will get called when an operation requests credentials from the
36+
// provider.
37+
//
38+
// The default implementation calls through the `providedBlock` passed
39+
// in `providerWithBlock:` above, but your subclass is expected to override it
40+
// to do its specific work.
41+
//
42+
// type - the credential types allowed by the operation.
43+
// URL - the URL the operation is authenticating against.
44+
// userName - the user name provided by the operation. Can be nil, and might be ignored.
45+
- (GTCredential *)credentialForType:(GTCredentialType)type URL:(NSString *)URL userName:(NSString *)userName;
46+
@end
47+
48+
// The GTCredential class is used to provide authentication data.
49+
// It acts as a wrapper around `git_cred` objects.
50+
@interface GTCredential : NSObject
51+
52+
// Create a credential object from a username/password pair.
53+
//
54+
// userName - The username to authenticate as.
55+
// password - The password belonging to that user.
56+
// error - If not NULL, set to any errors that occur.
57+
//
58+
// Return a new GTCredential instance, or nil if an error occurred
59+
+ (instancetype)credentialWithUserName:(NSString *)userName password:(NSString *)password error:(NSError **)error;
60+
61+
// Create a credential object from a SSH keyfile
62+
//
63+
// userName - The username to authenticate as.
64+
// publicKeyURL - The URL to the public key for that user.
65+
// Can be omitted to reconstruct the public key from the private key.
66+
// privateKeyURL - The URL to the private key for that user.
67+
// passphrase - The passPhrase for the private key. Optional if the private key has no password.
68+
// error - If not NULL, set to any errors that occur.
69+
//
70+
// Return a new GTCredential instance, or nil if an error occurred
71+
+ (instancetype)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL *)publicKeyURL privateKeyURL:(NSURL *)privateKeyURL passphrase:(NSString *)passphrase error:(NSError **)error;
72+
73+
// The underlying `git_cred` object.
74+
- (git_cred *)git_cred __attribute__((objc_returns_inner_pointer));
75+
76+
@end

Classes/GTCredential.m

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//
2+
// GTCredential.m
3+
// ObjectiveGitFramework
4+
//
5+
// Created by Etienne on 10/09/13.
6+
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
7+
//
8+
9+
#import <ObjectiveGit/NSError+Git.h>
10+
#import "GTCredential.h"
11+
#import "GTCredential+Private.h"
12+
#import <libssh2.h>
13+
14+
typedef GTCredential *(^GTCredentialProviderBlock)(GTCredentialType allowedTypes, NSString *URL, NSString *userName);
15+
16+
@interface GTCredentialProvider ()
17+
@property (nonatomic, readonly, copy) GTCredentialProviderBlock credBlock;
18+
@end
19+
20+
@implementation GTCredentialProvider
21+
+ (instancetype)providerWithBlock:(GTCredentialProviderBlock)credentialBlock {
22+
NSParameterAssert(credentialBlock != nil);
23+
24+
GTCredentialProvider *provider = [[self alloc] init];
25+
26+
provider->_credBlock = [credentialBlock copy];
27+
28+
return provider;
29+
}
30+
31+
- (GTCredential *)credentialForType:(GTCredentialType)type URL:(NSString *)URL userName:(NSString *)userName {
32+
NSAssert(self.credBlock != nil, @"Provider asked for credentials without block being set.");
33+
34+
return self.credBlock(type, URL, userName);
35+
}
36+
37+
@end
38+
39+
@interface GTCredential ()
40+
@property (nonatomic, assign, readonly) git_cred *git_cred;
41+
@end
42+
43+
@implementation GTCredential
44+
45+
+ (instancetype)credentialWithUserName:(NSString *)userName password:(NSString *)password error:(NSError **)error {
46+
git_cred *cred;
47+
int gitError = git_cred_userpass_plaintext_new(&cred, userName.UTF8String, password.UTF8String);
48+
if (gitError != GIT_OK) {
49+
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];
50+
return nil;
51+
}
52+
53+
return [[self alloc] initWithGitCred:cred];
54+
}
55+
56+
+ (instancetype)credentialWithUserName:(NSString *)userName publicKeyURL:(NSURL *)publicKeyURL privateKeyURL:(NSURL *)privateKeyURL passphrase:(NSString *)passphrase error:(NSError **)error {
57+
NSParameterAssert(privateKeyURL != nil);
58+
NSString *publicKeyPath = publicKeyURL.filePathURL.path;
59+
NSString *privateKeyPath = privateKeyURL.filePathURL.path;
60+
NSAssert(privateKeyPath != nil, @"Invalid file URL passed: %@", privateKeyURL);
61+
62+
git_cred *cred;
63+
int gitError = git_cred_ssh_keyfile_passphrase_new(&cred, userName.UTF8String, publicKeyPath.fileSystemRepresentation, privateKeyPath.fileSystemRepresentation, passphrase.UTF8String);
64+
if (gitError != GIT_OK) {
65+
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];
66+
return nil;
67+
}
68+
69+
return [[self alloc] initWithGitCred:cred];
70+
}
71+
72+
- (instancetype)initWithGitCred:(git_cred *)cred {
73+
NSParameterAssert(cred != nil);
74+
self = [self init];
75+
76+
if (self == nil) return nil;
77+
78+
_git_cred = cred;
79+
80+
return self;
81+
}
82+
83+
@end
84+
85+
int GTCredentialAcquireCallback(git_cred **git_cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload) {
86+
NSCParameterAssert(git_cred != NULL);
87+
NSCParameterAssert(payload != NULL);
88+
89+
GTCredentialAcquireCallbackInfo *info = payload;
90+
GTCredentialProvider *provider = info->credProvider;
91+
92+
if (provider == nil) {
93+
giterr_set_str(GIT_EUSER, "No GTCredentialProvider set, but authentication was requested.");
94+
return GIT_ERROR;
95+
}
96+
97+
NSString *URL = (url != NULL ? @(url) : nil);
98+
NSString *userName = (username_from_url != NULL ? @(username_from_url) : nil);
99+
100+
GTCredential *cred = [provider credentialForType:(GTCredentialType)allowed_types URL:URL userName:userName];
101+
if (cred == nil) {
102+
giterr_set_str(GIT_EUSER, "GTCredentialProvider failed to provide credentials.");
103+
return GIT_ERROR;
104+
}
105+
106+
*git_cred = cred.git_cred;
107+
return GIT_OK;
108+
}

Classes/GTRepository.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ extern NSString *const GTRepositoryCloneOptionsBare;
114114
// Default value is `YES`.
115115
extern NSString *const GTRepositoryCloneOptionsCheckout;
116116

117+
// A `GTCredentialProvider`, that will be used to authenticate against the remote.
118+
extern NSString *const GTRepositoryCloneOptionsCredentialProvider;
119+
117120
typedef void (^GTRepositoryStatusBlock)(NSURL *fileURL, GTRepositoryFileStatus status, BOOL *stop);
118121

119122
@interface GTRepository : NSObject

Classes/GTRepository.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,13 @@
4343
#import "NSError+Git.h"
4444
#import "NSString+Git.h"
4545
#import "GTDiffFile.h"
46+
#import "GTCredential.h"
47+
#import "GTCredential+Private.h"
4648

4749
NSString *const GTRepositoryCloneOptionsBare = @"GTRepositoryCloneOptionsBare";
4850
NSString *const GTRepositoryCloneOptionsCheckout = @"GTRepositoryCloneOptionsCheckout";
4951
NSString *const GTRepositoryCloneOptionsTransportFlags = @"GTRepositoryCloneOptionsTransportFlags";
52+
NSString *const GTRepositoryCloneOptionsCredentialProvider = @"GTRepositoryCloneOptionsCredentialProvider";
5053

5154
typedef void (^GTRepositorySubmoduleEnumerationBlock)(GTSubmodule *submodule, BOOL *stop);
5255
typedef void (^GTRepositoryTagEnumerationBlock)(GTTag *tag, BOOL *stop);
@@ -190,6 +193,13 @@ + (id)cloneFromURL:(NSURL *)originURL toWorkingDirectory:(NSURL *)workdirURL opt
190193
cloneOptions.checkout_opts = checkoutOptions;
191194
}
192195

196+
GTCredentialProvider *provider = options[GTRepositoryCloneOptionsCredentialProvider];
197+
if (provider) {
198+
GTCredentialAcquireCallbackInfo info = { .credProvider = provider };
199+
cloneOptions.cred_acquire_cb = GTCredentialAcquireCallback;
200+
cloneOptions.cred_acquire_payload = &info;
201+
}
202+
193203
cloneOptions.fetch_progress_cb = transferProgressCallback;
194204
cloneOptions.fetch_progress_payload = (__bridge void *)transferProgressBlock;
195205

0 commit comments

Comments
 (0)