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.impl.auth;
28  
29  import java.io.Serializable;
30  import java.security.Principal;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Locale;
34  import java.util.Map;
35  
36  import org.apache.hc.client5.http.auth.AuthChallenge;
37  import org.apache.hc.client5.http.auth.AuthScheme;
38  import org.apache.hc.client5.http.auth.AuthScope;
39  import org.apache.hc.client5.http.auth.AuthStateCacheable;
40  import org.apache.hc.client5.http.auth.AuthenticationException;
41  import org.apache.hc.client5.http.auth.BearerToken;
42  import org.apache.hc.client5.http.auth.Credentials;
43  import org.apache.hc.client5.http.auth.CredentialsProvider;
44  import org.apache.hc.client5.http.auth.MalformedChallengeException;
45  import org.apache.hc.client5.http.auth.StandardAuthScheme;
46  import org.apache.hc.client5.http.impl.StateHolder;
47  import org.apache.hc.client5.http.protocol.HttpClientContext;
48  import org.apache.hc.core5.annotation.Internal;
49  import org.apache.hc.core5.http.HttpHost;
50  import org.apache.hc.core5.http.HttpRequest;
51  import org.apache.hc.core5.http.NameValuePair;
52  import org.apache.hc.core5.http.protocol.HttpContext;
53  import org.apache.hc.core5.util.Args;
54  import org.apache.hc.core5.util.Asserts;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  /**
59   * Bearer authentication scheme.
60   *
61   * @since 5.3
62   */
63  @AuthStateCacheable
64  public class BearerScheme implements AuthScheme, StateHolder<BearerScheme.State>, Serializable {
65  
66      private static final Logger LOG = LoggerFactory.getLogger(BearerScheme.class);
67  
68      private final Map<String, String> paramMap;
69      private boolean complete;
70  
71      private BearerToken bearerToken;
72  
73      public BearerScheme() {
74          this.paramMap = new HashMap<>();
75          this.complete = false;
76      }
77  
78      @Override
79      public String getName() {
80          return StandardAuthScheme.BEARER;
81      }
82  
83      @Override
84      public boolean isConnectionBased() {
85          return false;
86      }
87  
88      @Override
89      public String getRealm() {
90          return this.paramMap.get("realm");
91      }
92  
93      @Override
94      public void processChallenge(
95              final AuthChallenge authChallenge,
96              final HttpContext context) throws MalformedChallengeException {
97          this.paramMap.clear();
98          final List<NameValuePair> params = authChallenge.getParams();
99          if (params != null) {
100             for (final NameValuePair param: params) {
101                 this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue());
102             }
103             if (LOG.isDebugEnabled()) {
104                 final String error = paramMap.get("error");
105                 if (error != null) {
106                     final StringBuilder buf = new StringBuilder();
107                     buf.append(error);
108                     final String desc = paramMap.get("error_description");
109                     final String uri = paramMap.get("error_uri");
110                     if (desc != null || uri != null) {
111                         buf.append(" (");
112                         buf.append(desc).append("; ").append(uri);
113                         buf.append(")");
114                     }
115                     LOG.debug(buf.toString());
116                 }
117             }
118         }
119         this.complete = true;
120     }
121 
122     @Override
123     public boolean isChallengeComplete() {
124         return this.complete;
125     }
126 
127     @Override
128     public boolean isResponseReady(
129             final HttpHost host,
130             final CredentialsProvider credentialsProvider,
131             final HttpContext context) throws AuthenticationException {
132 
133         Args.notNull(host, "Auth host");
134         Args.notNull(credentialsProvider, "Credentials provider");
135 
136         final AuthScope authScope = new AuthScope(host, getRealm(), getName());
137         final Credentials credentials = credentialsProvider.getCredentials(authScope, context);
138         if (credentials instanceof BearerToken) {
139             this.bearerToken = (BearerToken) credentials;
140             return true;
141         }
142 
143         if (LOG.isDebugEnabled()) {
144             final HttpClientContext clientContext = HttpClientContext.cast(context);
145             final String exchangeId = clientContext.getExchangeId();
146             LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope);
147         }
148         this.bearerToken = null;
149         return false;
150     }
151 
152     @Override
153     public Principal getPrincipal() {
154         return null;
155     }
156 
157     @Override
158     public String generateAuthResponse(
159             final HttpHost host,
160             final HttpRequest request,
161             final HttpContext context) throws AuthenticationException {
162         Asserts.notNull(bearerToken, "Bearer token");
163         return StandardAuthScheme.BEARER + " " + bearerToken.getToken();
164     }
165 
166     @Override
167     public State store() {
168         if (complete) {
169             return new State(new HashMap<>(paramMap), bearerToken);
170         } else {
171             return null;
172         }
173     }
174 
175     @Override
176     public void restore(final State state) {
177         if (state != null) {
178             paramMap.clear();
179             paramMap.putAll(state.params);
180             bearerToken = state.bearerToken;
181             complete = true;
182         }
183     }
184 
185     @Override
186     public String toString() {
187         return getName() + this.paramMap;
188     }
189 
190     @Internal
191     public static class State {
192 
193         final Map<String, String> params;
194         final BearerToken bearerToken;
195 
196         State(final Map<String, String> params, final BearerToken bearerToken) {
197             this.params = params;
198             this.bearerToken = bearerToken;
199         }
200 
201         @Override
202         public String toString() {
203             return "State{" +
204                     "params=" + params +
205                     ", bearerToken=" + bearerToken +
206                     '}';
207         }
208 
209     }
210 
211 }