View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.auth;
28  
29  import java.util.Locale;
30  import java.util.Objects;
31  
32  import org.apache.hc.core5.annotation.Contract;
33  import org.apache.hc.core5.annotation.ThreadingBehavior;
34  import org.apache.hc.core5.http.HttpHost;
35  import org.apache.hc.core5.util.Args;
36  import org.apache.hc.core5.util.LangUtils;
37  
38  /**
39   * {@code AuthScope} represents an authentication scope consisting of
40   * an application protocol, a host name, a port number, a realm name
41   * and an authentication scheme name.
42   *
43   * @since 4.0
44   */
45  @Contract(threading = ThreadingBehavior.IMMUTABLE)
46  public class AuthScope {
47  
48      private final String protocol;
49      private final String host;
50      private final int port;
51      private final String realm;
52      private final String schemeName;
53  
54      /**
55       * Defines auth scope with the given {@code protocol}, {@code host}, {@code port},
56       * {@code realm}, and {@code schemeName}.
57       *
58       * @param protocol application protocol. May be {@code null} if applies
59       *   to any protocol.
60       * @param host authentication host. May be {@code null} if applies
61       *   to any host.
62       * @param port authentication port. May be {@code -1} if applies
63       *   to any port of the host.
64       * @param realm authentication realm. May be {@code null} if applies
65       *   to any realm on the host.
66       * @param schemeName authentication scheme name. May be {@code null} if applies
67       *   to any auth scheme supported by the host.
68       */
69      public AuthScope(
70              final String protocol,
71              final String host,
72              final int port,
73              final String realm,
74              final String schemeName) {
75          this.protocol = protocol != null ? protocol.toLowerCase(Locale.ROOT) : null;
76          this.host = host != null ? host.toLowerCase(Locale.ROOT) : null;
77          this.port = port >= 0 ? port: -1;
78          this.realm = realm;
79          this.schemeName = schemeName;
80      }
81  
82      /**
83       * Defines auth scope for a specific host of origin.
84       *
85       * @param origin host of origin
86       * @param realm authentication realm. May be {@code null} if applies
87       *   to any realm on the host.
88       * @param schemeName authentication scheme name. May be {@code null} if applies
89       *   to any auth scheme supported by the host.
90       *
91       * @since 4.2
92       */
93      public AuthScope(
94              final HttpHost origin,
95              final String realm,
96              final String schemeName) {
97          Args.notNull(origin, "Host");
98          this.protocol = origin.getSchemeName().toLowerCase(Locale.ROOT);
99          this.host = origin.getHostName().toLowerCase(Locale.ROOT);
100         this.port = origin.getPort() >= 0 ? origin.getPort() : -1;
101         this.realm = realm;
102         this.schemeName = schemeName;
103     }
104 
105     /**
106      * Defines auth scope for a specific host of origin.
107      *
108      * @param origin host of origin
109      *
110      * @since 4.2
111      */
112     public AuthScope(final HttpHost origin) {
113         this(origin, null, null);
114     }
115 
116     /**
117      * Defines auth scope with the given {@code host} and {@code port}.
118      *
119      * @param host authentication host. May be {@code null} if applies
120      *   to any host.
121      * @param port authentication port. May be {@code -1} if applies
122      *   to any port of the host.
123      */
124     public AuthScope(final String host, final int port) {
125         this(null, host, port, null, null);
126     }
127 
128     /**
129      * Creates a copy of the given credentials scope.
130      */
131     public AuthScope(final AuthScope authScope) {
132         super();
133         Args.notNull(authScope, "Scope");
134         this.protocol = authScope.getProtocol();
135         this.host = authScope.getHost();
136         this.port = authScope.getPort();
137         this.realm = authScope.getRealm();
138         this.schemeName = authScope.getSchemeName();
139     }
140 
141     public String getProtocol() {
142         return protocol;
143     }
144 
145     public String getHost() {
146         return this.host;
147     }
148 
149     public int getPort() {
150         return this.port;
151     }
152 
153     public String getRealm() {
154         return this.realm;
155     }
156 
157     public String getSchemeName() {
158         return this.schemeName;
159     }
160 
161     /**
162      * Tests if the authentication scopes match.
163      *
164      * @return the match factor. Negative value signifies no match.
165      *    Non-negative signifies a match. The greater the returned value
166      *    the closer the match.
167      */
168     public int match(final AuthScope that) {
169         int factor = 0;
170         if (Objects.equals(toNullSafeLowerCase(this.schemeName),
171                              toNullSafeLowerCase(that.schemeName))) {
172             factor += 1;
173         } else {
174             if (this.schemeName != null && that.schemeName != null) {
175                 return -1;
176             }
177         }
178         if (Objects.equals(this.realm, that.realm)) {
179             factor += 2;
180         } else {
181             if (this.realm != null && that.realm != null) {
182                 return -1;
183             }
184         }
185         if (this.port == that.port) {
186             factor += 4;
187         } else {
188             if (this.port != -1 && that.port != -1) {
189                 return -1;
190             }
191         }
192         if (Objects.equals(this.protocol, that.protocol)) {
193             factor += 8;
194         } else {
195             if (this.protocol != null && that.protocol != null) {
196                 return -1;
197             }
198         }
199         if (Objects.equals(this.host, that.host)) {
200             factor += 16;
201         } else {
202             if (this.host != null && that.host != null) {
203                 return -1;
204             }
205         }
206         return factor;
207     }
208 
209     @Override
210     public boolean equals(final Object obj) {
211         if (this == obj) {
212             return true;
213         }
214         if (obj instanceof AuthScope) {
215             final AuthScope that = (AuthScope) obj;
216             return Objects.equals(this.protocol, that.protocol)
217                     && Objects.equals(this.host, that.host)
218                     && this.port == that.port
219                     && Objects.equals(this.realm, that.realm)
220                     && Objects.equals(toNullSafeLowerCase(this.schemeName),
221                                         toNullSafeLowerCase(that.schemeName));
222         }
223         return false;
224     }
225 
226     @Override
227     public int hashCode() {
228         int hash = LangUtils.HASH_SEED;
229         hash = LangUtils.hashCode(hash, this.protocol);
230         hash = LangUtils.hashCode(hash, this.host);
231         hash = LangUtils.hashCode(hash, this.port);
232         hash = LangUtils.hashCode(hash, this.realm);
233         hash = LangUtils.hashCode(hash, toNullSafeLowerCase(this.schemeName));
234         return hash;
235     }
236 
237     private String toNullSafeLowerCase(final String str) {
238         return str != null ? str.toLowerCase(Locale.ROOT) : null;
239     }
240 
241     @Override
242     public String toString() {
243         final StringBuilder buffer = new StringBuilder();
244         if (this.schemeName != null) {
245             buffer.append(this.schemeName);
246         } else {
247             buffer.append("<any auth scheme>");
248         }
249         buffer.append(' ');
250         if (this.realm != null) {
251             buffer.append('\'');
252             buffer.append(this.realm);
253             buffer.append('\'');
254         } else {
255             buffer.append("<any realm>");
256         }
257         buffer.append(' ');
258         if (this.protocol != null) {
259             buffer.append(this.protocol);
260         } else {
261             buffer.append("<any protocol>");
262         }
263         buffer.append("://");
264         if (this.host != null) {
265             buffer.append(this.host);
266         } else {
267             buffer.append("<any host>");
268         }
269         buffer.append(':');
270         if (this.port >= 0) {
271             buffer.append(this.port);
272         } else {
273             buffer.append("<any port>");
274         }
275         return buffer.toString();
276     }
277 
278 }