View Javadoc
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 }