1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.eclipse.aether.repository;
20
21 import java.nio.charset.StandardCharsets;
22 import java.security.MessageDigest;
23 import java.security.NoSuchAlgorithmException;
24
25 import org.eclipse.aether.RepositorySystemSession;
26
27 /**
28 * A helper to calculate a fingerprint/digest for the authentication data of a repository/proxy. Such a fingerprint can
29 * be used to detect changes in the authentication data across JVM restarts without exposing sensitive information.
30 */
31 public final class AuthenticationDigest {
32
33 private final MessageDigest digest;
34
35 private final RepositorySystemSession session;
36
37 private final RemoteRepository repository;
38
39 private final Proxy proxy;
40
41 /**
42 * Gets the fingerprint for the authentication of the specified repository.
43 *
44 * @param session The repository system session during which the fingerprint is requested, must not be {@code null}.
45 * @param repository The repository whose authentication is to be fingerprinted, must not be {@code null}.
46 * @return The fingerprint of the repository authentication or an empty string if no authentication is configured,
47 * never {@code null}.
48 */
49 public static String forRepository(RepositorySystemSession session, RemoteRepository repository) {
50 String digest = "";
51 Authentication auth = repository.getAuthentication();
52 if (auth != null) {
53 AuthenticationDigest authDigest = new AuthenticationDigest(session, repository, null);
54 auth.digest(authDigest);
55 digest = authDigest.digest();
56 }
57 return digest;
58 }
59
60 /**
61 * Gets the fingerprint for the authentication of the specified repository's proxy.
62 *
63 * @param session The repository system session during which the fingerprint is requested, must not be {@code null}.
64 * @param repository The repository whose proxy authentication is to be fingerprinted, must not be {@code null}.
65 * @return The fingerprint of the proxy authentication or an empty string if no proxy is present or if no proxy
66 * authentication is configured, never {@code null}.
67 */
68 public static String forProxy(RepositorySystemSession session, RemoteRepository repository) {
69 String digest = "";
70 Proxy proxy = repository.getProxy();
71 if (proxy != null) {
72 Authentication auth = proxy.getAuthentication();
73 if (auth != null) {
74 AuthenticationDigest authDigest = new AuthenticationDigest(session, repository, proxy);
75 auth.digest(authDigest);
76 digest = authDigest.digest();
77 }
78 }
79 return digest;
80 }
81
82 private AuthenticationDigest(RepositorySystemSession session, RemoteRepository repository, Proxy proxy) {
83 this.session = session;
84 this.repository = repository;
85 this.proxy = proxy;
86 digest = newDigest();
87 }
88
89 private static MessageDigest newDigest() {
90 try {
91 return MessageDigest.getInstance("SHA-1");
92 } catch (NoSuchAlgorithmException e) {
93 try {
94 return MessageDigest.getInstance("MD5");
95 } catch (NoSuchAlgorithmException ne) {
96 throw new IllegalStateException(ne);
97 }
98 }
99 }
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 }