Program Java For Mac
Your site is one of the best out there for development. Thanks for what you provide. Java is, and always has been, the source of many woes. And security holes. Lots of security holes. There’s really no reason to have it installed anymore, especially now that Minecraft has its own bundled Java for both OS X and Windows.So today is the day you remove it.
PermalinkJoin GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign up Find file Copy path
Cannot retrieve contributors at this time
Java Program For Mac
// Copyright (c) Microsoft. All rights reserved. |
// Licensed under the MIT license. See License.txt in the project root. |
packagecom.microsoft.alm.gitcredentialmanager; |
importcom.microsoft.alm.authentication.BaseVsoAuthentication; |
importcom.microsoft.alm.authentication.BasicAuthentication; |
importcom.microsoft.alm.authentication.Configuration; |
importcom.microsoft.alm.authentication.DeviceFlowResponse; |
importcom.microsoft.alm.authentication.IAuthentication; |
importcom.microsoft.alm.authentication.ISecureStore; |
importcom.microsoft.alm.authentication.ITokenStore; |
importcom.microsoft.alm.authentication.IVsoAadAuthentication; |
importcom.microsoft.alm.authentication.IVsoMsaAuthentication; |
importcom.microsoft.alm.authentication.SecretStore; |
importcom.microsoft.alm.authentication.SecretStoreAdapter; |
importcom.microsoft.alm.authentication.VsoAadAuthentication; |
importcom.microsoft.alm.authentication.VsoMsaAuthentication; |
importcom.microsoft.alm.authentication.Where; |
importcom.microsoft.alm.helpers.Action; |
importcom.microsoft.alm.helpers.Debug; |
importcom.microsoft.alm.helpers.Environment; |
importcom.microsoft.alm.helpers.Func; |
importcom.microsoft.alm.helpers.Guid; |
importcom.microsoft.alm.helpers.IOHelper; |
importcom.microsoft.alm.helpers.NotImplementedException; |
importcom.microsoft.alm.helpers.Path; |
importcom.microsoft.alm.helpers.StringHelper; |
importcom.microsoft.alm.helpers.Trace; |
importcom.microsoft.alm.helpers.UriHelper; |
importcom.microsoft.alm.oauth2.useragent.Provider; |
importcom.microsoft.alm.oauth2.useragent.Version; |
importcom.microsoft.alm.oauth2.useragent.subprocess.DefaultProcessFactory; |
importcom.microsoft.alm.oauth2.useragent.subprocess.ProcessCoordinator; |
importcom.microsoft.alm.oauth2.useragent.subprocess.TestableProcess; |
importcom.microsoft.alm.oauth2.useragent.subprocess.TestableProcessFactory; |
importcom.microsoft.alm.secret.Credential; |
importcom.microsoft.alm.secret.Secret; |
importcom.microsoft.alm.secret.Token; |
importcom.microsoft.alm.secret.VsoTokenScope; |
importcom.microsoft.alm.storage.StorageProvider; |
importjava.io.BufferedReader; |
importjava.io.File; |
importjava.io.IOException; |
importjava.io.InputStream; |
importjava.io.InputStreamReader; |
importjava.io.PrintStream; |
importjava.io.UnsupportedEncodingException; |
importjava.net.URISyntaxException; |
importjava.net.URL; |
importjava.net.URLDecoder; |
importjava.util.ArrayList; |
importjava.util.Arrays; |
importjava.util.Calendar; |
importjava.util.LinkedHashMap; |
importjava.util.List; |
importjava.util.Map; |
importjava.util.TreeMap; |
importjava.util.concurrent.Callable; |
importjava.util.concurrent.atomic.AtomicReference; |
publicclassProgram |
{ |
privatestaticfinalString ConfigPrefix ='credential'; |
privatestaticfinalString SecretsNamespace ='git'; |
privatestaticfinalString ProgramFolderName ='git-credential-manager'; |
privatestaticfinalVsoTokenScope VsoCredentialScope =VsoTokenScope.CodeWrite; |
privatestaticfinalString AbortAuthenticationProcessResponse ='quit=true'; |
privatestaticfinalString CredentialHelperSection ='credential.helper'; |
privatestaticfinalString CredentialHelperValueRegex ='git-credential-manager-[0-9]+.[0-9]+.[0-9]+(-SNAPSHOT)?.jar'; |
privatestaticfinalString CanFallbackToInsecureStore ='canFallBackToInsecureStore'; |
privatestaticfinalDefaultFileChecker DefaultFileCheckerSingleton =newDefaultFileChecker(); |
privatefinalInputStream standardIn; |
privatefinalPrintStream standardOut; |
privatefinalIComponentFactory componentFactory; |
privatestaticfinalAction<DeviceFlowResponse>DEVICE_FLOW_CALLBACK=newAction<DeviceFlowResponse>() |
{ |
@Overridepublicvoid call(finalDeviceFlowResponse deviceFlowResponse) |
{ |
System.err.println('------------------------------------'); |
System.err.println('OAuth 2.0 Device Flow authentication'); |
System.err.println('------------------------------------'); |
System.err.println('To complete the authentication process, please open a web browser and visit the following URI:'); |
System.err.println(deviceFlowResponse.getVerificationUri()); |
System.err.println('When prompted, enter the following code:'); |
System.err.println(deviceFlowResponse.getUserCode()); |
System.err.println('Once authenticated and authorized, execution will continue.'); |
} |
}; |
// https://stackoverflow.com/a/6773868/ |
staticStringgetVersion() |
{ |
if (_version null) |
{ |
_version =Program.class.getPackage().getImplementationVersion(); |
} |
return _version; |
} |
privatestaticString _version; |
staticStringgetTitle() |
{ |
if (_title null) |
{ |
_title =Program.class.getPackage().getImplementationTitle(); |
} |
return _title; |
} |
privatestaticString _title; |
publicstaticvoidmain(finalString[] args) |
{ |
try |
{ |
enableDebugTrace(); |
finalProgram program =newProgram(System.in, System.out, newComponentFactory()); |
program.innerMain(args); |
} |
catch (finalThrowable throwable) |
{ |
if (Debug.IsDebug) |
{ |
System.err.println('Fatal error encountered. Details:'); |
throwable.printStackTrace(System.err); |
} |
else |
{ |
System.err.println('Fatal: '+ throwable.getClass().getName() +' encountered. Details:'); |
System.err.println(throwable.getMessage()); |
} |
logEvent(throwable.getMessage(), 'EventLogEntryType.Error'); |
// notice the lack of a new line; Git needs it that way |
System.out.print(AbortAuthenticationProcessResponse); |
} |
Trace.flush(); |
} |
staticFiledetermineParentFolder() |
{ |
return findFirstValidFolder( |
Environment.SpecialFolder.LocalApplicationData, |
Environment.SpecialFolder.ApplicationData, |
Environment.SpecialFolder.UserProfile); |
} |
staticFilefindFirstValidFolder(finalEnvironment.SpecialFolder... candidates) |
{ |
for (finalEnvironment.SpecialFolder candidate : candidates) |
{ |
finalString path =Environment.getFolderPath(candidate); |
if (path null) |
continue; |
finalFile result =newFile(path); |
if (result.isDirectory()) |
{ |
return result; |
} |
} |
finalString path =System.getenv('HOME'); |
finalFile result =newFile(path); |
return result; |
} |
voidinnerMain(String[] args) throwsException |
{ |
if (args.length 0|| args[0].contains('?')) |
{ |
printHelpMessage(); |
return; |
} |
// list of arg => method associations (case-insensitive) |
finalMap<String, Callable<Void>> actions =newTreeMap<String, Callable<Void>>(String.CASE_INSENSITIVE_ORDER); |
actions.put('approve', Store); |
actions.put('erase', Erase); |
actions.put('fill', Get); |
actions.put('get', Get); |
actions.put('reject', Erase); |
actions.put('store', Store); |
actions.put('version', PrintVersion); |
actions.put('install', Install); |
actions.put('uninstall', Uninstall); |
for (finalString arg : args) |
{ |
if (actions.containsKey(arg)) |
{ |
actions.get(arg).call(); |
} |
} |
} |
publicProgram(finalInputStreamstandardIn, finalPrintStreamstandardOut, finalIComponentFactorycomponentFactory) |
{ |
this.standardIn = standardIn; |
this.standardOut = standardOut; |
this.componentFactory = componentFactory; |
} |
privatevoidprintHelpMessage() |
{ |
Trace.writeLine('Program::printHelpMessage'); |
standardOut.println('usage: git credential <command> [<args>]'); |
standardOut.println(); |
standardOut.println(' authority Defines the type of authentication to be used.'); |
standardOut.println(' Supports Auto, Basic, AAD, MSA, and Integrated.'); |
standardOut.println(' Default is Auto.'); |
standardOut.println(); |
standardOut.println(' `git config --global credential.microsoft.visualstudio.com.authority AAD`'); |
standardOut.println(); |
standardOut.println(' eraseosxkeychain Enables a workaround when running on Mac OS X'); |
standardOut.println(' and using a version of Git which includes the osxkeychain'); |
standardOut.println(' credential helper, hardcoded before all other helpers).'); |
standardOut.println(' The problem is osxkeychain may return expired or'); |
standardOut.println(' revoked credentials, aborting the Git operation.'); |
standardOut.println(' The workaround is to preemptively erase from osxkeychain'); |
standardOut.println(' any Git credentials that can be refreshed or re-acquired'); |
standardOut.println(' by this credential helper.'); |
standardOut.println(' Defaults to TRUE. Ignored by Basic authority.'); |
standardOut.println(' Does nothing if osxkeychain on Mac OS X isn't detected.'); |
standardOut.println(); |
standardOut.println(' `git config --global credential.microsoft.visualstudio.com.eraseosxkeychain false`'); |
standardOut.println(); |
standardOut.println(' interactive Specifies if user can be prompted for credentials or not.'); |
standardOut.println(' Supports Auto, Always, or Never. Defaults to Auto.'); |
standardOut.println(' Only used by AAD and MSA authority.'); |
standardOut.println(); |
standardOut.println(' `git config --global credential.microsoft.visualstudio.com.interactive never`'); |
standardOut.println(); |
standardOut.println(' validate Causes validation of credentials before supplying them'); |
standardOut.println(' to Git. Invalid credentials get a refresh attempt'); |
standardOut.println(' before failing. Incurs some minor overhead.'); |
standardOut.println(' Defaults to TRUE. Ignored by Basic authority.'); |
standardOut.println(); |
standardOut.println(' `git config --global credential.microsoft.visualstudio.com.validate false`'); |
standardOut.println(); |
standardOut.println(' writelog Enables trace logging of all activities. Logs are written to'); |
standardOut.println(' the .git/ folder at the root of the repository.'); |
standardOut.println(' Defaults to FALSE.'); |
standardOut.println(); |
standardOut.println(' `git config --global credential.writelog true`'); |
standardOut.println(); |
standardOut.println('Sample Configuration:'); |
standardOut.println(' [credential 'microsoft.visualstudio.com']'); |
standardOut.println(' authority = AAD'); |
standardOut.println(' [credential 'visualstudio.com']'); |
standardOut.println(' authority = MSA'); |
standardOut.println(' [credential]'); |
standardOut.println(' helper = manager'); |
} |
privatefinalCallable<Void> Erase =newCallable<Void>() |
{ |
@OverridepublicVoid call() throws IOException, URISyntaxException |
{ |
erase(); |
returnnull; |
} |
}; |
privatevoiderase() throwsIOException, URISyntaxException |
{ |
finalAtomicReference<OperationArguments> operationArgumentsRef =newAtomicReference<OperationArguments>(); |
finalAtomicReference<IAuthentication> authenticationRef =newAtomicReference<IAuthentication>(); |
initialize('erase', operationArgumentsRef, authenticationRef); |
erase(operationArgumentsRef.get(), authenticationRef.get()); |
} |
publicstaticvoiderase(finalOperationArgumentsoperationArguments, finalIAuthenticationauthentication) |
{ |
authentication.deleteCredentials(operationArguments.TargetUri); |
} |
privatefinalCallable<Void> Get =newCallable<Void>() |
{ |
@OverridepublicVoid call() throws IOException, URISyntaxException |
{ |
get(); |
returnnull; |
} |
}; |
privatevoidget() throwsIOException, URISyntaxException |
{ |
finalAtomicReference<OperationArguments> operationArgumentsRef =newAtomicReference<OperationArguments>(); |
finalAtomicReference<IAuthentication> authenticationRef =newAtomicReference<IAuthentication>(); |
initialize('get', operationArgumentsRef, authenticationRef); |
finalString result = get(operationArgumentsRef.get(), authenticationRef.get(), DEVICE_FLOW_CALLBACK); |
standardOut.print(result); |
} |
publicstaticStringget(finalOperationArgumentsoperationArguments, finalIAuthenticationauthentication, finalAction<DeviceFlowResponse>deviceFlowCallback) |
{ |
finalStringAuthFailureMessage='Logon failed, aborting authentication process.'; |
finalAtomicReference<Credential> credentials =newAtomicReference<Credential>(); |
switch (operationArguments.Authority) |
{ |
default: |
caseBasic: |
if (authentication.getCredentials(operationArguments.TargetUri, credentials)) |
{ |
Trace.writeLine(' credentials found'); |
operationArguments.setCredentials(credentials.get()); |
} |
break; |
caseAzureDirectory: |
finalIVsoAadAuthentication aadAuth = (IVsoAadAuthentication) authentication; |
// attempt to get cached creds -> refresh creds -> non-interactive logon -> interactive logon |
// note that AAD 'credentials' are always scoped access tokens |
if ( |
(operationArguments.Interactivity!=Interactivity.Always |
&& aadAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| aadAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
|| (operationArguments.Interactivity!=Interactivity.Always |
&& aadAuth.refreshCredentials(operationArguments.TargetUri, true) |
&& aadAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| aadAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
|| (operationArguments.Interactivity!=Interactivity.Never |
&& aadAuth.interactiveLogon(operationArguments.TargetUri, true) |
&& aadAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| aadAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
|| (operationArguments.Interactivity!=Interactivity.Never |
&& aadAuth.deviceLogon(operationArguments.TargetUri, true, deviceFlowCallback) |
&& aadAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| aadAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
) |
{ |
Trace.writeLine(' credentials found'); |
operationArguments.setCredentials(credentials.get()); |
logEvent('Azure Directory credentials for '+operationArguments.TargetUri+' successfully retrieved.', 'SuccessAudit'); |
} |
else |
{ |
System.err.println(AuthFailureMessage); |
logEvent('Failed to retrieve Azure Directory credentials for '+operationArguments.TargetUri+'.', 'FailureAudit'); |
returnAbortAuthenticationProcessResponse; |
} |
break; |
caseMicrosoftAccount: |
finalIVsoMsaAuthentication msaAuth = (IVsoMsaAuthentication) authentication; |
// attempt to get cached creds -> refresh creds -> interactive logon |
// note that MSA 'credentials' are always scoped access tokens |
if ( |
(operationArguments.Interactivity!=Interactivity.Always |
&& msaAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| msaAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
|| (operationArguments.Interactivity!=Interactivity.Always |
&& msaAuth.refreshCredentials(operationArguments.TargetUri, true) |
&& msaAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| msaAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
|| (operationArguments.Interactivity!=Interactivity.Never |
&& msaAuth.interactiveLogon(operationArguments.TargetUri, true) |
&& msaAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| msaAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
|| (operationArguments.Interactivity!=Interactivity.Never |
&& msaAuth.deviceLogon(operationArguments.TargetUri, true, deviceFlowCallback) |
&& msaAuth.getCredentials(operationArguments.TargetUri, credentials) |
&& (!operationArguments.ValidateCredentials |
|| msaAuth.validateCredentials(operationArguments.TargetUri, credentials.get()))) |
) |
{ |
Trace.writeLine(' credentials found'); |
operationArguments.setCredentials(credentials.get()); |
logEvent('Microsoft Live credentials for '+operationArguments.TargetUri+' successfully retrieved.', 'SuccessAudit'); |
} |
else |
{ |
System.err.println(AuthFailureMessage); |
logEvent('Failed to retrieve Microsoft Live credentials for '+operationArguments.TargetUri+'.', 'FailureAudit'); |
returnAbortAuthenticationProcessResponse; |
} |
break; |
caseGitHub: |
thrownewNotImplementedException(449515); |
caseIntegrated: |
credentials.set(newCredential(StringHelper.Empty, StringHelper.Empty)); |
operationArguments.setCredentials(credentials.get()); |
break; |
} |
return operationArguments.toString(); |
} |
privatefinalCallable<Void> Store =newCallable<Void>() |
{ |
@OverridepublicVoid call() throws IOException, URISyntaxException |
{ |
store(); |
returnnull; |
} |
}; |
privatevoidstore() throwsIOException, URISyntaxException |
{ |
finalAtomicReference<OperationArguments> operationArgumentsRef =newAtomicReference<OperationArguments>(); |
finalAtomicReference<IAuthentication> authenticationRef =newAtomicReference<IAuthentication>(); |
initialize('store', operationArgumentsRef, authenticationRef); |
finalString osName =System.getProperty('os.name'); |
finalTestableProcessFactory processFactory =newDefaultProcessFactory(); |
finalString pathString =System.getenv('PATH'); |
finalString pathSeparator =File.pathSeparator; |
store(operationArgumentsRef.get(), authenticationRef.get(), osName, processFactory, DefaultFileCheckerSingleton, pathString, pathSeparator); |
} |
publicstaticvoidstore(finalOperationArgumentsoperationArguments, finalIAuthenticationauthentication, finalStringosName, finalTestableProcessFactoryprocessFactory, finalFunc<File, Boolean>fileChecker, finalStringpathString, finalStringpathSeparator) |
{ |
Debug.Assert(operationArguments.getUserName() !=null, 'The operationArguments.Username is null'); |
finalCredential credentials =newCredential(operationArguments.getUserName(), operationArguments.getPassword()); |
if (authentication instanceofBasicAuthentication) |
{ |
authentication.setCredentials(operationArguments.TargetUri, credentials); |
} |
else |
{ |
if (operationArguments.EraseOsxKeyChain&&Provider.isMac(osName)) |
{ |
// check for the presence of git-credential-osxkeychain by scanning PATH |
finalFile osxkeychainFile = findProgram(pathString, pathSeparator, 'git-credential-osxkeychain', fileChecker); |
if (osxkeychainFile !=null) |
{ |
// erase these credentials from osxkeychain |
try |
{ |
finalString program = osxkeychainFile.getAbsolutePath(); |
finalTestableProcess process = processFactory.create(program, 'erase'); |
finalProcessCoordinator coordinator =newProcessCoordinator(process); |
coordinator.print(operationArguments.toString()); |
coordinator.waitFor(); |
} |
catch (finalIOException e) |
{ |
thrownewError(e); |
} |
catch (finalInterruptedException e) |
{ |
thrownewError(e); |
} |
} |
} |
} |
} |
privatefinalCallable<Void> PrintVersion =newCallable<Void>() |
{ |
@OverridepublicVoid call() |
{ |
printVersion(); |
returnnull; |
} |
}; |
privatevoidprintVersion() |
{ |
Trace.writeLine('Program::printVersion'); |
standardOut.println(String.format('%1$s version %2$s', getTitle(), getVersion())); |
} |
privatefinalCallable<Void> Install =newCallable<Void>() |
{ |
@OverridepublicVoid call() |
{ |
install(); |
returnnull; |
} |
}; |
privatevoidinstall() |
{ |
finalString osName =System.getProperty('os.name'); |
finalString osVersion =System.getProperty('os.version'); |
finalList<Provider> providers =Provider.PROVIDERS; |
finalTestableProcessFactory processFactory =newDefaultProcessFactory(); |
install(osName, osVersion, standardOut, providers, processFactory); |
} |
staticvoidinstall(finalStringosName, finalStringosVersion, finalPrintStreamstandardOut, finalList<Provider>providers, finalTestableProcessFactoryprocessFactory) |
{ |
List<String> missedRequirements =newArrayList<String>(); |
missedRequirements.addAll(checkGitRequirements(processFactory)); |
missedRequirements.addAll(checkOsRequirements(osName, osVersion)); |
if (missedRequirements.isEmpty()) |
{ |
try |
{ |
// TODO: 457304: Add option to configure for global or system |
finalString configLocation ='global'; |
uninstall(processFactory); |
configureGit(processFactory, configLocation); |
} |
catch (finalIOException e) |
{ |
thrownewError(e); |
} |
catch (finalInterruptedException e) |
{ |
thrownewError(e); |
} |
} |
else |
{ |
standardOut.println('Installation failed due to the following unmet requirements:'); |
for (String msg : missedRequirements) |
{ |
standardOut.println(msg); |
} |
standardOut.println(); |
standardOut.println('If you think we are excluding many users with one or more of these requirements, please let us know.'); |
} |
} |
staticvoidconfigureGit(finalTestableProcessFactoryprocessFactory, finalStringconfigLocation) throwsIOException, InterruptedException |
{ |
finalURL resourceURL =Program.class.getResource(''); |
finalString javaHome =System.getProperty('java.home'); |
finalFile javaExecutable =newFile(javaHome, 'bin/java'); |
finalString pathToJava = javaExecutable.getAbsolutePath(); |
finalString pathToJar = determinePathToJar(resourceURL); |
finalboolean isDebug =Debug.IsDebug; |
configureGit(processFactory, configLocation, pathToJava, pathToJar, isDebug); |
} |
staticvoidconfigureGit(finalTestableProcessFactoryprocessFactory, finalStringconfigLocation, finalStringpathToJava, finalStringpathToJar, finalbooleanisDebug) throwsIOException, InterruptedException |
{ |
finalStringBuilder sb =newStringBuilder(); |
// escape spaces (if any) in paths to java and path to JAR |
// i.e. !/usr/bin/jre 1.6/bin/java -Ddebug=false -jar /home/example/with spaces/gcm.jar |
sb.append('!').append(escapeSpaces(pathToJava)); |
sb.append(' -Ddebug=').append(isDebug); |
sb.append(' -Djava.net.useSystemProxies=true'); |
sb.append(' -jar ').append(escapeSpaces(pathToJar)); |
finalString gcmCommandLine = sb.toString(); |
finalString[] command = |
{ |
'git', |
'config', |
'--'+ configLocation, |
'--add', |
CredentialHelperSection, |
gcmCommandLine, |
}; |
finalTestableProcess process = processFactory.create(command); |
finalProcessCoordinator coordinator =newProcessCoordinator(process); |
finalint exitCode = coordinator.waitFor(); |
checkGitConfigExitCode(configLocation, exitCode); |
} |
staticStringescapeSpaces(finalStringinput) |
{ |
return input.replace('', ''); |
} |
privatefinalCallable<Void> Uninstall =newCallable<Void>() |
{ |
@OverridepublicVoid call() |
{ |
uninstall(); |
returnnull; |
} |
}; |
privatevoiduninstall() |
{ |
finalTestableProcessFactory processFactory =newDefaultProcessFactory(); |
uninstall(processFactory); |
} |
staticvoiduninstall(finalTestableProcessFactoryprocessFactory) |
{ |
try |
{ |
finalString configLocation ='global'; |
// TODO: 457304: unconfigure from both global and system (if we can!), to be sure |
if (isGitConfigured(processFactory, configLocation)) |
{ |
unconfigureGit(processFactory, configLocation); |
} |
} |
catch (finalIOException e) |
{ |
thrownewError(e); |
} |
catch (finalInterruptedException e) |
{ |
thrownewError(e); |
} |
} |
staticbooleanisGitConfigured(finalTestableProcessFactoryprocessFactory, finalStringconfigLocation) throwsIOException, InterruptedException |
{ |
finalString[] command = |
{ |
'git', |
'config', |
'--'+ configLocation, |
'--get', |
CredentialHelperSection, |
CredentialHelperValueRegex, |
}; |
finalTestableProcess process = processFactory.create(command); |
finalProcessCoordinator coordinator =newProcessCoordinator(process); |
coordinator.waitFor(); |
finalString stdOut = coordinator.getStdOut(); |
finalboolean result = stdOut.length() >0; |
return result; |
} |
staticvoidunconfigureGit(finalTestableProcessFactoryprocessFactory, finalStringconfigLocation) throwsIOException, InterruptedException |
{ |
finalString[] command = |
{ |
'git', |
'config', |
'--'+ configLocation, |
'--unset', |
CredentialHelperSection, |
CredentialHelperValueRegex, |
}; |
finalTestableProcess process = processFactory.create(command); |
finalProcessCoordinator coordinator =newProcessCoordinator(process); |
finalint exitCode = coordinator.waitFor(); |
checkGitConfigExitCode(configLocation, exitCode); |
} |
staticvoidcheckGitConfigExitCode(finalStringconfigLocation, finalintexitCode) |
{ |
String message; |
switch (exitCode) |
{ |
case0: |
message =null; |
break; |
case3: |
message ='The ''+ configLocation +'' Git config file is invalid.'; |
break; |
case4: |
message ='Can not write to the ''+ configLocation +'' Git config file.'; |
break; |
default: |
message ='Unexpected exit code ''+ exitCode +'' received from `git config`.'; |
break; |
} |
if (message !=null) |
{ |
thrownewError(message); |
} |
} |
/** |
* Determines the path of the JAR, given a URL to a resource inside the current JAR. |
* |
*/ |
staticStringdeterminePathToJar(finalURLresourceURL) |
{ |
finalString packageName =Program.class.getPackage().getName(); |
finalString resourcePath = resourceURL.getPath(); |
finalString decodedResourcePath; |
try |
{ |
decodedResourcePath =URLDecoder.decode(resourcePath, UriHelper.UTF_8); |
} |
catch (finalUnsupportedEncodingException e) |
{ |
thrownewError(e); |
} |
finalString packagePath = packageName.replace('.', '/'); |
finalString resourceSuffix ='!/'+ packagePath +'/'; |
String jarPath = decodedResourcePath.replace(resourceSuffix, ''); |
jarPath = jarPath.replace('file:', ''); |
return jarPath; |
} |
/** |
* Checks if git version can be found and if it is the correct version |
* |
* @return if git requirements are met |
*/ |
staticList<String>checkGitRequirements(finalTestableProcessFactoryprocessFactory) |
{ |
finalString trimmedResponse = fetchGitVersion(processFactory); |
return isValidGitVersion(trimmedResponse); |
} |
staticStringfetchGitVersion(finalTestableProcessFactoryprocessFactory) |
{ |
try |
{ |
// finding git version via commandline |
finalTestableProcess gitProcess = processFactory.create('git', '--version'); |
finalProcessCoordinator coordinator =newProcessCoordinator(gitProcess); |
coordinator.waitFor(); |
finalString gitResponse = coordinator.getStdOut(); |
return gitResponse.trim(); |
} |
catch (finalIOException e) |
{ |
thrownewError(e); |
} |
catch (finalInterruptedException e) |
{ |
thrownewError(e); |
} |
} |
privatestaticclassDefaultFileCheckerimplementsFunc<File, Boolean> |
{ |
@OverridepublicBooleancall(finalFilefile) |
{ |
return file.isFile(); |
} |
} |
staticFilefindProgram(finalStringpathString, finalStringpathSeparator, finalStringexecutableName, finalFunc<File, Boolean>fileChecker) |
{ |
finalString[] partArray = pathString.split(pathSeparator); |
finalList<String> parts =Arrays.asList(partArray); |
return findProgram(parts, executableName, fileChecker); |
} |
staticFilefindProgram(finalList<String>directories, finalStringexecutableName, finalFunc<File, Boolean>fileChecker) |
{ |
for (finalString directoryString : directories) |
{ |
finalFile directory =newFile(directoryString); |
finalFile executableFile =newFile(directory, executableName); |
if (fileChecker.call(executableFile)) |
{ |
return executableFile; |
} |
} |
returnnull; |
} |
/** |
* Parses git version response for major and minor version and checks if it's 1.9 or above |
* |
* @param gitResponse the output from 'git --version' |
* @return if the git version meets the requirement |
*/ |
protectedstaticList<String>isValidGitVersion(finalStringgitResponse) |
{ |
Trace.writeLine('Program::isValidGitVersion'); |
Trace.writeLine(' gitResponse:'+ gitResponse); |
finalStringGitNotFound='Git is a requirement for installation and cannot be found. Please check that Git is installed and is added to your PATH'; |
finalList<String> result =newArrayList<String>(); |
// if git responded with a version then parse it for the version number |
if (gitResponse !=null) |
{ |
// TODO: 450002: Detect 'Apple Git' and warn the user |
// git version numbers are in the form of x.y.z and we only need x.y to ensure the requirements are met |
Version version =null; |
try |
{ |
version =Version.parseVersion(gitResponse); |
} |
catch (finalIllegalArgumentException ignored) |
{ |
Trace.writeLine(''+ ignored.getMessage()); |
result.add(GitNotFound); |
} |
if (version !=null) |
{ |
if (version.getMajor() <1 |
|| (version.getMajor() 1&& version.getMinor() <9)) |
{ |
result.add('Git version '+ version.getMajor() +'.'+ version.getMinor() +' was found but version 1.9 or above is required.'); |
} |
} |
} |
else |
{ |
result.add(GitNotFound); |
} |
return result; |
} |
/** |
* Checks if the OS meets the requirements to run installation |
* |
* @param osName the name of the operating system, as retrieved from the os.name property. |
* @param osVersion the version of the operating system, as retrieved from the os.version property. |
* @return a list of strings representing unmet requirements. |
*/ |
protectedstaticList<String>checkOsRequirements(finalStringosName, finalStringosVersion) |
{ |
finalArrayList<String> result =newArrayList<String>(); |
if (Provider.isMac(osName)) |
{ |
finalVersion version =Version.parseVersion(osVersion); |
finalString badVersionMessage ='The version of Mac OS X running is '+ version.getMajor() +'.'+ version.getMinor() +'.'+ version.getPatch() + |
' which does not meet the minimum version of 10.9.5 needed for installation. Please upgrade to Mac OS X 10.9.5 or above to proceed.'; |
if (version.getMajor() <10) |
{ |
result.add(badVersionMessage); |
} |
elseif (version.getMajor() 10) |
{ |
if (version.getMinor() <9) |
{ |
result.add(badVersionMessage); |
} |
elseif (version.getMinor() 9) |
{ |
if (version.getPatch() <5) |
{ |
result.add(badVersionMessage); |
} |
} |
} |
} |
elseif (Provider.isLinux(osName)) |
{ |
// TODO: check for supported major distributions and versions (Ubuntu 14+, Fedora 22+, etc.) |
} |
elseif (Provider.isWindows(osName)) |
{ |
result.add('It looks like you are running on Windows, please consider using the Git Credential Manager for Windows: https://github.com/Microsoft/Git-Credential-Manager-for-Windows'); |
} |
else |
{ |
result.add('The Git Credential Manager for Mac and Linux is only supported on, well, Mac OS X and Linux. The operating system detected is '+ osName +', which is not supported.'); |
} |
return result; |
} |
privatevoidinitialize( |
finalStringmethodName, |
finalAtomicReference<OperationArguments>operationArgumentsRef, |
finalAtomicReference<IAuthentication>authenticationRef |
) throwsIOException, URISyntaxException |
{ |
// parse the operations arguments from stdin (this is how git sends commands) |
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html |
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html |
finalOperationArguments operationArguments; |
finalBufferedReader reader =newBufferedReader(newInputStreamReader(standardIn)); |
try |
{ |
operationArguments =newOperationArguments(reader); |
} |
finally |
{ |
IOHelper.closeQuietly(reader); |
} |
Debug.Assert(operationArguments.TargetUri!=null, 'The operationArguments.TargetUri is null'); |
finalConfiguration config = componentFactory.createConfiguration(); |
loadOperationArguments(operationArguments, config); |
enableTraceLogging(operationArguments); |
Trace.writeLine('Program::'+ methodName); |
Trace.writeLine(' targetUri = '+operationArguments.TargetUri); |
finalISecureStore secureStore = componentFactory.createSecureStore(operationArguments); |
finalIAuthentication authentication = componentFactory.createAuthentication(operationArguments, secureStore); |
operationArgumentsRef.set(operationArguments); |
authenticationRef.set(authentication); |
} |
staticIAuthenticationcreateAuthentication(finalOperationArgumentsoperationArguments, finalISecureStoresecureStore) |
{ |
Debug.Assert(operationArguments !=null, 'The operationArguments is null'); |
Trace.writeLine('Program::createAuthentication'); |
finalString osName =System.getProperty('os.name'); |
finalSecret.IUriNameConversion iUriNameConversion = |
Provider.isMac(osName) |
/* |
* Adds a prefix to the target name to avoid a collision |
* with the built-in git-credential-osxkeychain. |
* This is because the built-in helper will not validate the credentials first, |
* leading to a poor user experience if the token is no longer valid. |
*/ |
?newSecret.PrefixedUriNameConversion('gcm4ml:') |
:Secret.DefaultUriNameConversion; |
finalSecretStore secrets =newSecretStore(secureStore, SecretsNamespace, null, null, iUriNameConversion); |
finalAtomicReference<IAuthentication> authorityRef =newAtomicReference<IAuthentication>(); |
finalITokenStore adaRefreshTokenStore =null; |
if (operationArguments.AuthorityAuthorityType.Auto) |
{ |
Trace.writeLine(' detecting authority type'); |
// detect the authority |
if (BaseVsoAuthentication.getAuthentication(operationArguments.TargetUri, |
VsoCredentialScope, |
secrets, |
adaRefreshTokenStore, |
authorityRef) |
/* TODO: 449515: add GitHub support |
|| GithubAuthentication.GetAuthentication(operationArguments.TargetUri, |
GithubCredentialScope, |
secrets, |
authorityRef)*/) |
{ |
// set the authority type based on the returned value |
if (authorityRef.get() instanceofVsoMsaAuthentication) |
{ |
operationArguments.Authority=AuthorityType.MicrosoftAccount; |
} |
elseif (authorityRef.get() instanceofVsoAadAuthentication) |
{ |
operationArguments.Authority=AuthorityType.AzureDirectory; |
} |
/* TODO: 449515: add GitHub support |
else if (authorityRef instanceof GithubAuthentication) |
{ |
operationArguments.Authority = AuthorityType.GitHub; |
} |
*/ |
} |
else |
{ |
operationArguments.Authority=AuthorityType.Basic; |
} |
} |
switch (operationArguments.Authority) |
{ |
caseAzureDirectory: |
Trace.writeLine(' authority is Azure Directory'); |
// return the allocated authority or a generic AAD backed VSO authentication object |
return authorityRef.get() !=null? authorityRef.get() :newVsoAadAuthentication(Guid.Empty, VsoCredentialScope, secrets, adaRefreshTokenStore); |
caseBasic: |
default: |
Trace.writeLine(' authority is basic'); |
// return a generic username + password authentication object |
return authorityRef.get() !=null? authorityRef.get() :newBasicAuthentication(secrets); |
/* TODO: 449515: add GitHub support |
case GitHub: |
Trace.writeLine(' authority it GitHub'); |
// return a GitHub authentication object |
return new GithubAuthentication(GithubCredentialScope, secrets); |
*/ |
caseMicrosoftAccount: |
Trace.writeLine(' authority is Microsoft Live'); |
// return the allocated authority or a generic MSA backed VSO authentication object |
return authorityRef.get() !=null? authorityRef.get() :newVsoMsaAuthentication(VsoCredentialScope, secrets, adaRefreshTokenStore); |
} |
} |
privatestaticvoidloadOperationArguments(finalOperationArgumentsoperationArguments, finalConfigurationconfig) throwsIOException |
{ |
Debug.Assert(operationArguments !=null, 'The operationsArguments parameter is null.'); |
Trace.writeLine('Program::loadOperationArguments'); |
finalAtomicReference<Configuration.Entry> entryRef =newAtomicReference<Configuration.Entry>(); |
if (config.tryGetEntry(ConfigPrefix, operationArguments.TargetUri, 'authority', entryRef)) |
{ |
Trace.writeLine(' authority = '+ entryRef.get().Value); |
if ('MSA'.equalsIgnoreCase(entryRef.get().Value) |
||'Microsoft'.equalsIgnoreCase(entryRef.get().Value) |
||'MicrosoftAccount'.equalsIgnoreCase(entryRef.get().Value) |
||'Live'.equalsIgnoreCase(entryRef.get().Value) |
||'LiveConnect'.equalsIgnoreCase(entryRef.get().Value) |
||'LiveID'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.Authority=AuthorityType.MicrosoftAccount; |
} |
elseif ('AAD'.equalsIgnoreCase(entryRef.get().Value) |
||'Azure'.equalsIgnoreCase(entryRef.get().Value) |
||'AzureDirectory'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.Authority=AuthorityType.AzureDirectory; |
} |
elseif ('Integrated'.equalsIgnoreCase(entryRef.get().Value) |
||'NTLM'.equalsIgnoreCase(entryRef.get().Value) |
||'Kerberos'.equalsIgnoreCase(entryRef.get().Value) |
||'SSO'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.Authority=AuthorityType.Integrated; |
} |
else |
{ |
operationArguments.Authority=AuthorityType.Basic; |
} |
} |
if (config.tryGetEntry(ConfigPrefix, operationArguments.TargetUri, 'interactive', entryRef)) |
{ |
Trace.writeLine(' interactive = '+ entryRef.get().Value); |
if ('always'.equalsIgnoreCase(entryRef.get().Value) |
||'true'.equalsIgnoreCase(entryRef.get().Value) |
||'force'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.Interactivity=Interactivity.Always; |
} |
elseif ('never'.equalsIgnoreCase(entryRef.get().Value) |
||'false'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.Interactivity=Interactivity.Never; |
} |
} |
if (config.tryGetEntry(ConfigPrefix, operationArguments.TargetUri, 'validate', entryRef)) |
{ |
Trace.writeLine(' validate = '+ entryRef.get().Value); |
if ('true'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.ValidateCredentials=true; |
} |
elseif ('false'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.ValidateCredentials=false; |
} |
} |
if (config.tryGetEntry(ConfigPrefix, operationArguments.TargetUri, 'writelog', entryRef)) |
{ |
Trace.writeLine(' writelog = '+ entryRef.get().Value); |
if ('true'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.WriteLog=true; |
} |
elseif ('false'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.WriteLog=false; |
} |
} |
if (config.tryGetEntry(ConfigPrefix, operationArguments.TargetUri, 'eraseosxkeychain', entryRef)) |
{ |
Trace.writeLine(' eraseosxkeychain = '+ entryRef.get().Value); |
if ('true'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.EraseOsxKeyChain=true; |
} |
elseif ('false'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.EraseOsxKeyChain=false; |
} |
} |
if (config.tryGetEntry(ConfigPrefix, operationArguments.TargetUri, CanFallbackToInsecureStore, entryRef)) |
{ |
Trace.writeLine(''+CanFallbackToInsecureStore+' = '+ entryRef.get().Value); |
if ('true'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.CanFallbackToInsecureStore=true; |
} |
elseif ('false'.equalsIgnoreCase(entryRef.get().Value)) |
{ |
operationArguments.CanFallbackToInsecureStore=false; |
} |
} |
} |
privatestaticvoidlogEvent(finalStringmessage, finalObjecteventType) |
{ |
System.out.println(message); |
} |
privatestaticvoidenableTraceLogging(finalOperationArgumentsoperationArguments) throwsIOException |
{ |
finalintLogFileMaxLength=8*1024*1024; // 8 MB |
Trace.writeLine('Program::EnableTraceLogging'); |
if (operationArguments.WriteLog) |
{ |
Trace.writeLine(' trace logging enabled'); |
finalAtomicReference<String> gitConfigPath =newAtomicReference<String>(); |
if (Where.gitLocalConfig(gitConfigPath)) |
{ |
Trace.writeLine(' git local config found at '+ gitConfigPath.get()); |
finalString dotGitPath =Path.getDirectoryName(gitConfigPath.get()); |
finalString logFilePath =Path.combine(dotGitPath, Path.changeExtension(ConfigPrefix, '.log')); |
finalString logFileName =operationArguments.TargetUri.toString(); |
finalFile logFileInfo =newFile(logFilePath); |
if (logFileInfo.exists() && logFileInfo.length() >LogFileMaxLength) |
{ |
for (int i =1; i <Integer.MAX_VALUE; i++) |
{ |
finalString moveName =String.format('%1$s%2$03d.log', ConfigPrefix, i); |
finalString movePath =Path.combine(dotGitPath, moveName); |
finalFile moveFile =newFile(movePath); |
if (!moveFile.isFile()) |
{ |
logFileInfo.renameTo(moveFile); |
break; |
} |
} |
} |
Trace.writeLine(' trace log destination is '+ logFilePath); |
finalPrintStream listener =newPrintStream(logFilePath); |
Trace.getListeners().add(listener); |
// write a small header to help with identifying new log entries |
listener.println(Environment.NewLine); |
listener.println(String.format('Log Start (%1$tFT%1$tT%1$tZ)', Calendar.getInstance())); |
listener.println(String.format('%1$s version %2$s', getTitle(), getVersion())); |
} |
} |
} |
privatestaticvoidenableDebugTrace() |
{ |
if (Debug.IsDebug) |
{ |
// use the stderr stream for the trace as stdout is used in the cross-process communications protocol |
Trace.getListeners().add(System.err); |
} |
} |
staticclassComponentFactoryimplementsIComponentFactory |
{ |
@OverridepublicIAuthenticationcreateAuthentication(finalOperationArgumentsoperationArguments, finalISecureStoresecureStore) |
{ |
returnProgram.createAuthentication(operationArguments, secureStore); |
} |
@OverridepublicConfigurationcreateConfiguration() throwsIOException |
{ |
returnnewConfiguration(); |
} |
@OverridepublicISecureStorecreateSecureStore(finalOperationArgumentsoperationArguments) |
{ |
Trace.writeLine('Program::ComponentFactory::createSecureStore'); |
finalboolean canFallbackToInsecureStore =operationArguments.CanFallbackToInsecureStore; |
finalStorageProvider.SecureOption secureOption = |
canFallbackToInsecureStore |
?StorageProvider.SecureOption.PREFER |
:StorageProvider.SecureOption.MUST; |
finalcom.microsoft.alm.storage.SecretStore<Token> tokenSecretStore =StorageProvider.getTokenStorage(true, secureOption); |
if (tokenSecretStore null) { |
thrownewRuntimeException('Secure credential storage is not available on this operating system. '+ |
'You may opt-in to store credentials in an unencrypted file under your user home directory by running '+ |
''git config --global credential.canFallBackToInsecureStore true'.'); |
} |
finalcom.microsoft.alm.storage.SecretStore<Credential> credentialSecretStore =StorageProvider.getCredentialStorage(true, secureOption); |
finalISecureStore secureStore =newSecretStoreAdapter(tokenSecretStore, credentialSecretStore); |
finalFile parentFolder = determineParentFolder(); |
finalFile programFolder =newFile(parentFolder, ProgramFolderName); |
finalFile insecureFile =newFile(programFolder, 'insecureStore.xml'); |
if (insecureFile.isFile()) |
{ |
Trace.writeLine(' InsecureStore file found, migrating...'); |
finalInsecureStore insecureStore =newInsecureStore(insecureFile); |
insecureStore.migrateAndDisable(secureStore); |
Trace.writeLine(' InsecureStore file migrated and disabled.'); |
} |
return secureStore; |
} |
} |
} |
Download Java For Mac Os X
Copy lines Copy permalink