View Javadoc
1   package org.eclipse.aether.repository;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   * 
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   * 
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.nio.charset.StandardCharsets;
23  import java.security.MessageDigest;
24  import java.security.NoSuchAlgorithmException;
25  
26  import org.eclipse.aether.RepositorySystemSession;
27  
28  /**
29   * A helper to calculate a fingerprint/digest for the authentication data of a repository/proxy. Such a fingerprint can
30   * be used to detect changes in the authentication data across JVM restarts without exposing sensitive information.
31   */
32  public final class AuthenticationDigest
33  {
34  
35      private final MessageDigest digest;
36  
37      private final RepositorySystemSession session;
38  
39      private final RemoteRepository repository;
40  
41      private final Proxy proxy;
42  
43      /**
44       * Gets the fingerprint for the authentication of the specified repository.
45       * 
46       * @param session The repository system session during which the fingerprint is requested, must not be {@code null}.
47       * @param repository The repository whose authentication is to be fingerprinted, must not be {@code null}.
48       * @return The fingerprint of the repository authentication or an empty string if no authentication is configured,
49       *         never {@code null}.
50       */
51      public static String forRepository( RepositorySystemSession session, RemoteRepository repository )
52      {
53          String digest = "";
54          Authentication auth = repository.getAuthentication();
55          if ( auth != null )
56          {
57              AuthenticationDigest authDigest = new AuthenticationDigest( session, repository, null );
58              auth.digest( authDigest );
59              digest = authDigest.digest();
60          }
61          return digest;
62      }
63  
64      /**
65       * Gets the fingerprint for the authentication of the specified repository's proxy.
66       * 
67       * @param session The repository system session during which the fingerprint is requested, must not be {@code null}.
68       * @param repository The repository whose proxy authentication is to be fingerprinted, must not be {@code null}.
69       * @return The fingerprint of the proxy authentication or an empty string if no proxy is present or if no proxy
70       *         authentication is configured, never {@code null}.
71       */
72      public static String forProxy( RepositorySystemSession session, RemoteRepository repository )
73      {
74          String digest = "";
75          Proxy proxy = repository.getProxy();
76          if ( proxy != null )
77          {
78              Authentication auth = proxy.getAuthentication();
79              if ( auth != null )
80              {
81                  AuthenticationDigest authDigest = new AuthenticationDigest( session, repository, proxy );
82                  auth.digest( authDigest );
83                  digest = authDigest.digest();
84              }
85          }
86          return digest;
87      }
88  
89      private AuthenticationDigest( RepositorySystemSession session, RemoteRepository repository, Proxy proxy )
90      {
91          this.session = session;
92          this.repository = repository;
93          this.proxy = proxy;
94          digest = newDigest();
95      }
96  
97      private static MessageDigest newDigest()
98      {
99          try
100         {
101             return MessageDigest.getInstance( "SHA-1" );
102         }
103         catch ( NoSuchAlgorithmException e )
104         {
105             try
106             {
107                 return MessageDigest.getInstance( "MD5" );
108             }
109             catch ( NoSuchAlgorithmException ne )
110             {
111                 throw new IllegalStateException( ne );
112             }
113         }
114     }
115 
116     /**
117      * Gets the repository system session during which the authentication fingerprint is calculated.
118      * 
119      * @return The repository system session, never {@code null}.
120      */
121     public RepositorySystemSession getSession()
122     {
123         return session;
124     }
125 
126     /**
127      * Gets the repository requiring authentication. If {@link #getProxy()} is not {@code null}, the data gathered by
128      * this authentication digest does not apply to the repository's host but rather the proxy.
129      * 
130      * @return The repository to be contacted, never {@code null}.
131      */
132     public RemoteRepository getRepository()
133     {
134         return repository;
135     }
136 
137     /**
138      * Gets the proxy (if any) to be authenticated with.
139      * 
140      * @return The proxy or {@code null} if authenticating directly with the repository's host.
141      */
142     public Proxy getProxy()
143     {
144         return proxy;
145     }
146 
147     /**
148      * Updates the digest with the specified strings.
149      * 
150      * @param strings The strings to update the digest with, may be {@code null} or contain {@code null} elements.
151      */
152     public void update( String... strings )
153     {
154         if ( strings != null )
155         {
156             for ( String string : strings )
157             {
158                 if ( string != null )
159                 {
160                     digest.update( string.getBytes( StandardCharsets.UTF_8 ) );
161                 }
162             }
163         }
164     }
165 
166     /**
167      * Updates the digest with the specified characters.
168      * 
169      * @param chars The characters to update the digest with, may be {@code null}.
170      */
171     @SuppressWarnings( "checkstyle:magicnumber" )
172     public void update( char... chars )
173     {
174         if ( chars != null )
175         {
176             for ( char c : chars )
177             {
178                 digest.update( (byte) ( c >> 8 ) );
179                 digest.update( (byte) ( c & 0xFF ) );
180             }
181         }
182     }
183 
184     /**
185      * Updates the digest with the specified bytes.
186      * 
187      * @param bytes The bytes to update the digest with, may be {@code null}.
188      */
189     public void update( byte... bytes )
190     {
191         if ( bytes != null )
192         {
193             digest.update( bytes );
194         }
195     }
196 
197     @SuppressWarnings( "checkstyle:magicnumber" )
198     private String digest()
199     {
200         byte[] bytes = digest.digest();
201         StringBuilder buffer = new StringBuilder( bytes.length * 2 );
202         for ( byte aByte : bytes )
203         {
204             int b = aByte & 0xFF;
205             if ( b < 0x10 )
206             {
207                 buffer.append( '0' );
208             }
209             buffer.append( Integer.toHexString( b ) );
210         }
211         return buffer.toString();
212     }
213 
214 }