001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.repository; 020 021import java.nio.charset.StandardCharsets; 022import java.security.MessageDigest; 023import java.security.NoSuchAlgorithmException; 024 025import org.eclipse.aether.RepositorySystemSession; 026 027/** 028 * A helper to calculate a fingerprint/digest for the authentication data of a repository/proxy. Such a fingerprint can 029 * be used to detect changes in the authentication data across JVM restarts without exposing sensitive information. 030 */ 031public final class AuthenticationDigest { 032 033 private final MessageDigest digest; 034 035 private final RepositorySystemSession session; 036 037 private final RemoteRepository repository; 038 039 private final Proxy proxy; 040 041 /** 042 * Gets the fingerprint for the authentication of the specified repository. 043 * 044 * @param session The repository system session during which the fingerprint is requested, must not be {@code null}. 045 * @param repository The repository whose authentication is to be fingerprinted, must not be {@code null}. 046 * @return The fingerprint of the repository authentication or an empty string if no authentication is configured, 047 * never {@code null}. 048 */ 049 public static String forRepository(RepositorySystemSession session, RemoteRepository repository) { 050 String digest = ""; 051 Authentication auth = repository.getAuthentication(); 052 if (auth != null) { 053 AuthenticationDigest authDigest = new AuthenticationDigest(session, repository, null); 054 auth.digest(authDigest); 055 digest = authDigest.digest(); 056 } 057 return digest; 058 } 059 060 /** 061 * Gets the fingerprint for the authentication of the specified repository's proxy. 062 * 063 * @param session The repository system session during which the fingerprint is requested, must not be {@code null}. 064 * @param repository The repository whose proxy authentication is to be fingerprinted, must not be {@code null}. 065 * @return The fingerprint of the proxy authentication or an empty string if no proxy is present or if no proxy 066 * authentication is configured, never {@code null}. 067 */ 068 public static String forProxy(RepositorySystemSession session, RemoteRepository repository) { 069 String digest = ""; 070 Proxy proxy = repository.getProxy(); 071 if (proxy != null) { 072 Authentication auth = proxy.getAuthentication(); 073 if (auth != null) { 074 AuthenticationDigest authDigest = new AuthenticationDigest(session, repository, proxy); 075 auth.digest(authDigest); 076 digest = authDigest.digest(); 077 } 078 } 079 return digest; 080 } 081 082 private AuthenticationDigest(RepositorySystemSession session, RemoteRepository repository, Proxy proxy) { 083 this.session = session; 084 this.repository = repository; 085 this.proxy = proxy; 086 digest = newDigest(); 087 } 088 089 private static MessageDigest newDigest() { 090 try { 091 return MessageDigest.getInstance("SHA-1"); 092 } catch (NoSuchAlgorithmException e) { 093 try { 094 return MessageDigest.getInstance("MD5"); 095 } catch (NoSuchAlgorithmException ne) { 096 throw new IllegalStateException(ne); 097 } 098 } 099 } 100 101 /** 102 * Gets the repository system session during which the authentication fingerprint is calculated. 103 * 104 * @return The repository system session, never {@code null}. 105 */ 106 public RepositorySystemSession getSession() { 107 return session; 108 } 109 110 /** 111 * Gets the repository requiring authentication. If {@link #getProxy()} is not {@code null}, the data gathered by 112 * this authentication digest does not apply to the repository's host but rather the proxy. 113 * 114 * @return The repository to be contacted, never {@code null}. 115 */ 116 public RemoteRepository getRepository() { 117 return repository; 118 } 119 120 /** 121 * Gets the proxy (if any) to be authenticated with. 122 * 123 * @return The proxy or {@code null} if authenticating directly with the repository's host. 124 */ 125 public Proxy getProxy() { 126 return proxy; 127 } 128 129 /** 130 * Updates the digest with the specified strings. 131 * 132 * @param strings The strings to update the digest with, may be {@code null} or contain {@code null} elements. 133 */ 134 public void update(String... strings) { 135 if (strings != null) { 136 for (String string : strings) { 137 if (string != null) { 138 digest.update(string.getBytes(StandardCharsets.UTF_8)); 139 } 140 } 141 } 142 } 143 144 /** 145 * Updates the digest with the specified characters. 146 * 147 * @param chars The characters to update the digest with, may be {@code null}. 148 */ 149 @SuppressWarnings("checkstyle:magicnumber") 150 public void update(char... chars) { 151 if (chars != null) { 152 for (char c : chars) { 153 digest.update((byte) (c >> 8)); 154 digest.update((byte) (c & 0xFF)); 155 } 156 } 157 } 158 159 /** 160 * Updates the digest with the specified bytes. 161 * 162 * @param bytes The bytes to update the digest with, may be {@code null}. 163 */ 164 public void update(byte... bytes) { 165 if (bytes != null) { 166 digest.update(bytes); 167 } 168 } 169 170 @SuppressWarnings("checkstyle:magicnumber") 171 private String digest() { 172 byte[] bytes = digest.digest(); 173 StringBuilder buffer = new StringBuilder(bytes.length * 2); 174 for (byte aByte : bytes) { 175 int b = aByte & 0xFF; 176 if (b < 0x10) { 177 buffer.append('0'); 178 } 179 buffer.append(Integer.toHexString(b)); 180 } 181 return buffer.toString(); 182 } 183}