Created
January 19, 2026 12:24
-
-
Save kibotu/c982a0950f395c536b89b5f28aeef810 to your computer and use it in GitHub Desktop.
Gradle Authenticator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| if (!project.hasProperty('artifactory_contextUrl')) throw new GradleException('the "artifactory_contextUrl" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_token')) throw new GradleException('the "artifactory_token" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_reader_1')) throw new GradleException('the "artifactory_reader_1" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_reader_2')) throw new GradleException('the "artifactory_reader_2" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_reader_3')) throw new GradleException('the "artifactory_reader_3" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_reader_token_1')) throw new GradleException('the "artifactory_reader_token_1" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_reader_token_2')) throw new GradleException('the "artifactory_reader_token_2" needs to be set in gradle.properties!') | |
| if (!project.hasProperty('artifactory_reader_token_3')) throw new GradleException('the "artifactory_reader_token_3" needs to be set in gradle.properties!') | |
| Authenticator.setDefault (new Authenticator() { | |
| private def current_user | |
| private def current_token | |
| private def users | |
| private def tokens | |
| private def maxRetryAttempts | |
| private def initialBackoffMs | |
| private def maxBackoffMs | |
| private def authValidityMs | |
| private def lastAuthTime = 0 | |
| private final Object authLock = new Object() | |
| private static final String getBasicAuthenticationHeader(String username, String password) { | |
| String valueToEncode = username + ":" + password; | |
| return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes()) | |
| } | |
| { | |
| // Retry configuration - read from properties with defaults | |
| maxRetryAttempts = project.hasProperty('artifactory_max_retries') ? | |
| Integer.parseInt(project.property('artifactory_max_retries').toString()) : 10 | |
| initialBackoffMs = project.hasProperty('artifactory_initial_backoff') ? | |
| Integer.parseInt(project.property('artifactory_initial_backoff').toString()) : 500 | |
| maxBackoffMs = project.hasProperty('artifactory_max_backoff') ? | |
| Integer.parseInt(project.property('artifactory_max_backoff').toString()) : 30000 | |
| authValidityMs = project.hasProperty('artifactory_auth_validity') ? | |
| Integer.parseInt(project.property('artifactory_auth_validity').toString()) : 60000 | |
| users = ["$artifactory_reader_1", "$artifactory_reader_2", "$artifactory_reader_3"] | |
| tokens = ["$artifactory_reader_token_1", "$artifactory_reader_token_2", "$artifactory_reader_token_3"] | |
| // Initial authentication | |
| authenticateWithRetry() | |
| } | |
| private void authenticateWithRetry() { | |
| def client = java.net.http.HttpClient.newHttpClient() | |
| def authenticated = false | |
| def attempt = 0 | |
| def backoffMs = initialBackoffMs | |
| while (!authenticated && attempt < maxRetryAttempts) { | |
| attempt++ | |
| println("Authentication attempt ${attempt}/${maxRetryAttempts}") | |
| // Shuffle users for each retry attempt to distribute load | |
| def indices = (0..<users.size()).toList().shuffled() | |
| for (i in indices) { | |
| try { | |
| def request = java.net.http.HttpRequest.newBuilder() | |
| .header("Authorization", getBasicAuthenticationHeader(users[i], tokens[i])) | |
| .method("HEAD", java.net.http.HttpRequest.BodyPublishers.noBody()) | |
| .uri(URI.create("$artifactory_contextUrl/libs-release")) | |
| .timeout(java.time.Duration.ofSeconds(30)) | |
| .build() | |
| def response = client.send(request, java.net.http.HttpResponse.BodyHandlers.discarding()) | |
| def statusCode = response.statusCode() | |
| println(" User: ${users[i]} -> Status: ${statusCode}") | |
| if (statusCode == 302) { | |
| current_user = users[i] | |
| current_token = tokens[i] | |
| authenticated = true | |
| lastAuthTime = System.currentTimeMillis() | |
| println("✓ Successfully authenticated with user: ${users[i]}") | |
| break | |
| } else if (statusCode == 401) { | |
| println(" ⚠ User ${users[i]} temporarily blocked (401), trying next user...") | |
| // Continue to next user | |
| } else { | |
| println(" ⚠ Unexpected status code ${statusCode} for user ${users[i]}") | |
| } | |
| } catch (Exception e) { | |
| println(" ✗ Error testing user ${users[i]}: ${e.message}") | |
| // Continue to next user | |
| } | |
| } | |
| // If not authenticated after trying all users, wait before next attempt | |
| if (!authenticated && attempt < maxRetryAttempts) { | |
| println(" All users blocked or unavailable. Waiting ${backoffMs}ms before retry...") | |
| Thread.sleep(backoffMs) | |
| // Exponential backoff with cap | |
| backoffMs = Math.min(backoffMs * 2, maxBackoffMs) | |
| } | |
| } | |
| if (current_user == null) { | |
| throw new Exception("Failed to authenticate after ${attempt} attempts. All artifactory users appear to be blocked or invalid!") | |
| } | |
| } | |
| protected PasswordAuthentication getPasswordAuthentication() { | |
| if (artifactory_contextUrl.contains(this.requestingHost)) { | |
| synchronized (authLock) { | |
| def currentTime = System.currentTimeMillis() | |
| // If authentication is stale, try to re-authenticate | |
| if (currentTime - lastAuthTime > authValidityMs) { | |
| println("⚠ Authentication credentials may be stale, re-authenticating...") | |
| try { | |
| authenticateWithRetry() | |
| } catch (Exception e) { | |
| println("✗ Re-authentication failed: ${e.message}") | |
| // Continue with existing credentials as fallback | |
| } | |
| } | |
| return new PasswordAuthentication(current_user, current_token.toCharArray()) | |
| } | |
| } | |
| return super.getPasswordAuthentication() | |
| } | |
| }) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
settings.gradlegradle.properties