import { Credentials } from "../credentials" ; import { Logger } from "../../utils/logger" ; import { OsxKeychain } from "./osx-keychain-access" ; import { inject , injectable } from "inversify" ; import { Constants , TYPES } from "../../utils/constants" ; import { CredentialsStore } from "../credentialsstore" ; import { Utils } from "../../utils/utils" ; @ injectable ( ) export class OsxKeychainApi implements CredentialsStore { public static separator : string = "|" ; private osxKeychain : OsxKeychain ; constructor ( @ inject ( TYPES . OsxKeychain ) osxKeychain ) { this . osxKeychain = osxKeychain ; this . osxKeychain . setPrefix ( Constants . SERVICE_PREFIX ) ; } public async getCredentials ( ) { let credentials ; const credentialsList = await this . listCredentials ( ) ; if ( credentialsList . length > 0 ) { credentials = credentialsList [ 0 ] ; } if ( credentialsList . length > 1 ) { Logger . logWarning ( "[OsxKeychainApi::getCredential] there are more then one suitable credentials for the user! " + "The first will be taken." ) ; } if ( credentials ) { const password = await this . getPasswordFromKeyChain ( credentials ) ; return new Credentials ( credentials . serverURL , credentials . user , password , undefined , undefined ) ; } } private getPasswordFromKeyChain ( credentials ) { const targetName = OsxKeychainApi . createTargetName ( credentials . serverURL , credentials . user ) ; return this . osxKeychain . getPasswordForUser ( targetName ) ; } public setCredentials ( credentials ) { const targetName = OsxKeychainApi . createTargetName ( credentials . serverURL , credentials . user ) ; return this . osxKeychain . set ( targetName , "" , credentials . password ) ; } public async removeCredentials ( ) { const credentialsList = await this . listCredentials ( ) ; if ( credentialsList && credentialsList . length > 0 ) { for ( let i = 0 ; i < credentialsList . length ; i ++ ) { const credentials = credentialsList [ i ] ; const targetName = OsxKeychainApi . createTargetName ( credentials . serverURL , credentials . user ) ; await this . removeCredentialByName ( targetName ) ; } } } public async removeCredentialByName ( targetName ) { const description = "" ; try { await this . osxKeychain . remove ( targetName , description ) ; } catch ( err ) { const CREDENTIALS_NOT_FOUND_CODE = 44 ; if ( err . code && err . code !== CREDENTIALS_NOT_FOUND_CODE ) { throw err ; } else { Logger . logError ( `OsxKeychainApi#removeCredentialByName: ${ err }` ) ; } } } private listCredentials ( ) { const credentialsList = [ ] ; const stream = this . osxKeychain . getCredentialsWithoutPasswordsListStream ( ) ; stream . on ( "data" , ( cred ) => { if ( cred . svce && cred . svce . indexOf ( Constants . SERVICE_PREFIX ) === 0 ) { try { const credentials = OsxKeychainApi . createCredentialsWithoutPassword ( cred . acct ) ; credentialsList . push ( credentials ) ; } catch ( err ) { Logger . logError ( "[OsxKeychainApi::listCredentials] could not collect credentials. with err: " + Utils . formatErrorMessage ( err ) ) ; } } } ) ; return new Promise < Credentials [ ] > ( ( resolve , reject ) => { stream . on ( "end" , ( ) => { resolve ( credentialsList ) ; } ) ; stream . on ( "error" , ( error ) => { console . log ( error ) ; reject ( error ) ; } ) ; } ) ; } private static createCredentialsWithoutPassword ( targetName ) { const segments = targetName . split ( OsxKeychainApi . separator ) ; const url = new Buffer ( segments [ 0 ] , "hex" ) . toString ( "utf8" ) ; const username = new Buffer ( segments [ 1 ] , "hex" ) . toString ( "utf8" ) ; return new Credentials ( url , username , undefined , undefined , undefined ) ; } private static createTargetName ( url , username ) { const encryptedUrl = new Buffer ( url , "utf8" ) . toString ( "hex" ) ; const encryptedUsername = new Buffer ( username , "utf8" ) . toString ( "hex" ) ; return encryptedUrl + OsxKeychainApi . separator + encryptedUsername ; } getCredentialsSilently ( ) { throw new Error ( "Method not supported." ) ; } }