1 /* 2 * Copyright Andrej Mitrovic 2013. 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 module git.credentials; 8 9 import std.conv; 10 import std.exception; 11 import std..string; 12 import std.typecons; 13 14 import deimos.git2.transport; 15 16 import git.exception; 17 import git.util; 18 import git.version_; 19 20 version (GIT_SSH) 21 { 22 static assert(0, "dlibgit does not support SSH yet."); 23 } 24 25 static if (targetLibGitVersion == VersionInfo(0, 19, 0)): 26 27 /* The base structure for all credential types. */ 28 struct GitCred 29 { 30 // internal 31 private this(git_cred* cred) 32 { 33 _data = Data(cred); 34 } 35 36 /** 37 Return the actual credential type of this credential. 38 Use the $(D get) template to cast this type to the 39 type tagged as $(D credType). 40 */ 41 @property GitCredType credType() 42 { 43 return cast(GitCredType)_data._payload.credtype; 44 } 45 46 /** Throw if the target credential type is not equal to credType that's stored. */ 47 void verifyTypeMatch(GitCredType target) 48 { 49 enforceEx!GitException(credType == target, 50 format("Tried to cast GitCred of type '%s' to type '%s'", 51 credType, target)); 52 } 53 54 /** 55 Cast this credential to the structure type matching 56 the $(D cred) enum tag. 57 58 If the underlying type does not match the target type, 59 a $(D GitException) is thrown. 60 */ 61 auto get(GitCredType cred)() 62 { 63 return get!(_credToType!cred); 64 } 65 66 /** 67 Cast this credential to a specific credential type $(D T). 68 69 If the underlying type does not match the target type, 70 a $(D GitException) is thrown. 71 */ 72 T get(T)() if (isGitCredential!T) 73 { 74 verifyTypeMatch(T.credType); 75 return getImpl!T; 76 } 77 78 package: 79 /** 80 * The internal libgit2 handle for this object. 81 * 82 * Care should be taken not to escape the reference outside a scope where 83 * a GitCred encapsulating the handle is kept alive. 84 */ 85 @property git_cred* cHandle() 86 { 87 return _data._payload; 88 } 89 90 private: 91 92 T getImpl(T : GitCred_PlainText)() 93 { 94 auto cred = cast(T.c_cred_struct*)_data._payload; 95 96 T result; 97 result.parent = this; 98 result.username = to!string(cred.username); 99 result.password = to!string(cred.password); 100 return result; 101 } 102 103 version (GIT_SSH) 104 { 105 static assert(0, "dlibgit does not support SSH yet."); 106 107 T getImpl(T : GitCred_KeyFilePassPhrase)() 108 { 109 auto cred = cast(T.c_cred_struct*)_data._payload; 110 111 T result; 112 result.parent = this; 113 result.publickey = to!string(cred.publickey); 114 result.privatekey = to!string(cred.privatekey); 115 result.passphrase = to!string(cred.passphrase); 116 return result; 117 } 118 119 T getImpl(T : GitCred_PublicKey)() 120 { 121 auto cred = cast(T.c_cred_struct*)_data._payload; 122 123 T result; 124 result.parent = this; 125 result.publickey = cred.publickey[0 .. cred.publickey_len]; 126 result.sign_callback = cred.sign_callback; 127 result.sign_data = cred.sign_data; 128 return result; 129 } 130 } 131 132 /** Payload for the $(D git_cred) object which should be refcounted. */ 133 struct Payload 134 { 135 this(git_cred* payload) 136 { 137 _payload = payload; 138 } 139 140 ~this() 141 { 142 //~ writefln("- %s", __FUNCTION__); 143 144 if (_payload !is null) 145 { 146 _payload.free(_payload); 147 _payload = null; 148 } 149 } 150 151 /// Should never perform copy 152 @disable this(this); 153 154 /// Should never perform assign 155 @disable void opAssign(typeof(this)); 156 157 git_cred* _payload; 158 } 159 160 alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; 161 Data _data; 162 } 163 164 /// 165 enum GitCredType 166 { 167 /// 168 plaintext = GIT_CREDTYPE_USERPASS_PLAINTEXT, 169 170 /// 171 passphrase = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE, 172 173 /// 174 publickey = GIT_CREDTYPE_SSH_PUBLICKEY, 175 } 176 177 /* A plaintext username and password. */ 178 struct GitCred_PlainText 179 { 180 /// 181 enum credType = GitCredType.plaintext; 182 183 /// 184 GitCred parent; 185 186 /// 187 string username; 188 189 /// 190 string password; 191 192 private alias c_cred_struct = git_cred_userpass_plaintext; 193 } 194 195 version (GIT_SSH) 196 { 197 static assert(0, "dlibgit does not support SSH yet."); 198 199 /* A ssh key file and passphrase. */ 200 struct GitCred_KeyFilePassPhrase 201 { 202 /// 203 enum credType = GitCredType.passphrase; 204 205 /// 206 GitCred parent; 207 208 /// 209 string publicKey; 210 211 /// 212 string privateKey; 213 214 /// 215 string passPhrase; 216 217 private alias c_cred_struct = git_cred_ssh_keyfile_passphrase; 218 } 219 220 /* A ssh public key and authentication callback. */ 221 struct GitCred_PublicKey 222 { 223 /// 224 enum credType = GitCredType.publickey; 225 226 /// 227 GitCred parent; 228 229 /// 230 ubyte[] publicKey; 231 232 /// 233 void* signCallback; 234 235 /// 236 void* signData; 237 238 private alias c_cred_struct = git_cred_ssh_publickey; 239 } 240 } 241 242 /** Check if type $(D T) is one of the supported git credential types. */ 243 template isGitCredential(T) 244 { 245 version (GIT_SSH) 246 { 247 static assert(0, "dlibgit does not support SSH yet."); 248 249 enum bool isGitCredential = is(T == GitCred_PlainText) || 250 is(T == GitCred_KeyFilePassPhrase) || 251 is(T == GitCred_PublicKey); 252 } 253 else 254 { 255 enum bool isGitCredential = is(T == GitCred_PlainText); 256 } 257 } 258 259 // helper 260 private template _credToType(GitCredType credType) 261 { 262 static if (credType == GitCredType.plaintext) 263 alias _credToType = GitCred_PlainText; 264 else 265 version (GIT_SSH) 266 { 267 static assert(0, "dlibgit does not support SSH yet."); 268 269 static if (credType == GitCredType.passphrase) 270 alias _credToType = GitCred_KeyFilePassPhrase; 271 else 272 static if (credType == GitCredType.publickey) 273 alias _credToType = GitCred_PublicKey; 274 else 275 static assert(0); 276 } 277 else 278 static assert(0); 279 } 280 281 /** 282 Creates a new plain-text username and password credential object. 283 The supplied credential parameter will be internally duplicated. 284 */ 285 GitCred getCredPlainText(string username, string password) 286 { 287 git_cred* _git_cred; 288 require(git_cred_userpass_plaintext_new(&_git_cred, username.gitStr, password.gitStr) == 0); 289 return GitCred(_git_cred); 290 } 291 292 /// 293 unittest 294 { 295 auto cred = getCredPlainText("user", "pass"); 296 297 switch (cred.credType) with (GitCredType) 298 { 299 case plaintext: 300 { 301 version (GIT_SSH) 302 { 303 static assert(0, "dlibgit does not support SSH yet."); 304 305 // throw when trying to cast to an inappropriate type 306 assertThrown!GitException(cred.get!passphrase); 307 308 // ditto 309 assertThrown!GitException(cred.get!GitCred_KeyFilePassPhrase); 310 } 311 312 // use enum for the get template 313 auto cred1 = cred.get!plaintext; 314 assert(cred1.username == "user"); 315 assert(cred1.password == "pass"); 316 317 // or use a type 318 auto cred2 = cred.get!GitCred_PlainText; 319 assert(cred2.username == "user"); 320 assert(cred2.password == "pass"); 321 322 break; 323 } 324 325 default: assert(0, text(cred.credType)); 326 } 327 } 328 329 version (GIT_SSH) 330 { 331 static assert(0, "dlibgit does not support SSH yet."); 332 333 /** 334 Creates a new ssh key file and passphrase credential object. 335 The supplied credential parameter will be internally duplicated. 336 337 Params: 338 339 - $(D publicKey): The path to the public key of the credential. 340 - $(D privateKey): The path to the private key of the credential. 341 - $(D passPhrase): The passphrase of the credential. 342 */ 343 GitCred getCredKeyFilePassPhrase(string publicKey, string privateKey, string passPhrase) 344 { 345 git_cred* _git_cred; 346 require(git_cred_ssh_keyfile_passphrase_new(&_git_cred, publicKey.gitStr, privateKey.gitStr, passPhrase.gitStr) == 0); 347 return GitCred(_git_cred); 348 } 349 350 /// 351 unittest 352 { 353 auto cred = getCredKeyFilePassPhrase("public", "private", "passphrase"); 354 355 switch (cred.credType) with (GitCredType) 356 { 357 case passphrase: 358 { 359 // throw when trying to cast to an inappropriate type 360 assertThrown!GitException(cred.get!plaintext); 361 362 // ditto 363 assertThrown!GitException(cred.get!GitCred_PlainText); 364 365 // use enum for the get template 366 auto cred1 = cred.get!passphrase; 367 assert(cred1.publicKey == "public"); 368 assert(cred1.privateKey == "private"); 369 assert(cred1.passPhrase == "passphrase"); 370 371 // or use a type 372 auto cred2 = cred.get!GitCred_KeyFilePassPhrase; 373 assert(cred2.publicKey == "public"); 374 assert(cred2.privateKey == "private"); 375 assert(cred2.passPhrase == "passphrase"); 376 377 break; 378 } 379 380 default: assert(0, text(cred.credType)); 381 } 382 } 383 384 /** 385 Creates a new ssh public key credential object. 386 The supplied credential parameter will be internally duplicated. 387 388 Params: 389 390 - $(D publicKey): The bytes of the public key. 391 - $(D signCallback): The callback method for authenticating. 392 - $(D signData): The abstract data sent to the $(D signCallback) method. 393 */ 394 GitCred getCredPublicKey(ubyte[] publicKey, void* signCallback, void* signData) 395 { 396 git_cred* _git_cred; 397 require(git_cred_ssh_publickey_new(&_git_cred, publicKey.ptr, publicKey.length, signCallback, signData) == 0); 398 return GitCred(_git_cred); 399 } 400 401 /// 402 unittest 403 { 404 auto cred = getCredPublicKey([], null, null); 405 406 switch (cred.credType) with (GitCredType) 407 { 408 case publickey: 409 { 410 // throw when trying to cast to an inappropriate type 411 assertThrown!GitException(cred.get!plaintext); 412 413 // ditto 414 assertThrown!GitException(cred.get!GitCred_PlainText); 415 416 // use enum for the get template 417 auto cred1 = cred.get!publickey; 418 assert(cred1.publicKey == []); 419 assert(cred1.signCallback is null); 420 assert(cred1.signData is null); 421 422 // or use a type 423 auto cred2 = cred.get!GitCred_PublicKey; 424 assert(cred2.publicKey == []); 425 assert(cred2.signCallback is null); 426 assert(cred2.signData is null); 427 428 break; 429 } 430 431 default: assert(0, text(cred.credType)); 432 } 433 } 434 } 435 436 alias GitCredAcquireDelegate = GitCred delegate( 437 in char[] url, 438 in char[] usernameFromURL, 439 uint allowedTypes);