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.testing.sync;
28  
29  import java.io.IOException;
30  import java.security.Principal;
31  
32  import org.apache.hc.client5.http.SystemDefaultDnsResolver;
33  import org.apache.hc.client5.http.auth.AuthScheme;
34  import org.apache.hc.client5.http.auth.AuthSchemeFactory;
35  import org.apache.hc.client5.http.auth.AuthScope;
36  import org.apache.hc.client5.http.auth.Credentials;
37  import org.apache.hc.client5.http.auth.KerberosConfig;
38  import org.apache.hc.client5.http.classic.methods.HttpGet;
39  import org.apache.hc.client5.http.auth.StandardAuthScheme;
40  import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
41  import org.apache.hc.client5.http.impl.auth.SPNegoScheme;
42  import org.apache.hc.client5.http.impl.classic.HttpClients;
43  import org.apache.hc.core5.http.ClassicHttpRequest;
44  import org.apache.hc.core5.http.ClassicHttpResponse;
45  import org.apache.hc.core5.http.HttpException;
46  import org.apache.hc.core5.http.HttpHost;
47  import org.apache.hc.core5.http.HttpStatus;
48  import org.apache.hc.core5.http.config.Registry;
49  import org.apache.hc.core5.http.config.RegistryBuilder;
50  import org.apache.hc.core5.http.io.HttpRequestHandler;
51  import org.apache.hc.core5.http.io.entity.EntityUtils;
52  import org.apache.hc.core5.http.io.entity.StringEntity;
53  import org.apache.hc.core5.http.message.BasicHeader;
54  import org.apache.hc.core5.http.protocol.HttpContext;
55  import org.ietf.jgss.GSSContext;
56  import org.ietf.jgss.GSSCredential;
57  import org.ietf.jgss.GSSManager;
58  import org.ietf.jgss.GSSName;
59  import org.ietf.jgss.Oid;
60  import org.junit.Assert;
61  import org.junit.Test;
62  import org.mockito.ArgumentMatchers;
63  import org.mockito.Mockito;
64  
65  /**
66   * Tests for {@link SPNegoScheme}.
67   */
68  public class TestSPNegoScheme extends LocalServerTestBase {
69  
70      /**
71       * This service will continue to ask for authentication.
72       */
73      private static class PleaseNegotiateService implements HttpRequestHandler {
74  
75          @Override
76          public void handle(
77                  final ClassicHttpRequest request,
78                  final ClassicHttpResponse response,
79                  final HttpContext context) throws HttpException, IOException {
80              response.setCode(HttpStatus.SC_UNAUTHORIZED);
81              response.addHeader(new BasicHeader("WWW-Authenticate", StandardAuthScheme.SPNEGO + " blablabla"));
82              response.addHeader(new BasicHeader("Connection", "Keep-Alive"));
83              response.setEntity(new StringEntity("auth required "));
84          }
85      }
86  
87      /**
88       * NegotatieScheme with a custom GSSManager that does not require any Jaas or
89       * Kerberos configuration.
90       *
91       */
92      private static class NegotiateSchemeWithMockGssManager extends SPNegoScheme {
93  
94          final GSSManager manager = Mockito.mock(GSSManager.class);
95          final GSSName name = Mockito.mock(GSSName.class);
96          final GSSContext context = Mockito.mock(GSSContext.class);
97  
98          NegotiateSchemeWithMockGssManager() throws Exception {
99              super(KerberosConfig.DEFAULT, SystemDefaultDnsResolver.INSTANCE);
100             Mockito.when(context.initSecContext(
101                     ArgumentMatchers.<byte[]>any(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
102                     .thenReturn("12345678".getBytes());
103             Mockito.when(manager.createName(
104                     ArgumentMatchers.anyString(), ArgumentMatchers.<Oid>any()))
105                     .thenReturn(name);
106             Mockito.when(manager.createContext(
107                     ArgumentMatchers.<GSSName>any(), ArgumentMatchers.<Oid>any(),
108                     ArgumentMatchers.<GSSCredential>any(), ArgumentMatchers.anyInt()))
109                     .thenReturn(context);
110         }
111 
112         @Override
113         protected GSSManager getManager() {
114             return manager;
115         }
116 
117     }
118 
119     private static class UseJaasCredentials implements Credentials {
120 
121         @Override
122         public char[] getPassword() {
123             return null;
124         }
125 
126         @Override
127         public Principal getUserPrincipal() {
128             return null;
129         }
130 
131     }
132 
133     private static class NegotiateSchemeFactoryWithMockGssManager implements AuthSchemeFactory {
134 
135         NegotiateSchemeWithMockGssManager scheme;
136 
137         NegotiateSchemeFactoryWithMockGssManager() throws Exception {
138             scheme = new NegotiateSchemeWithMockGssManager();
139         }
140 
141         @Override
142         public AuthScheme create(final HttpContext context) {
143             return scheme;
144         }
145 
146     }
147 
148     /**
149      * Tests that the client will stop connecting to the server if
150      * the server still keep asking for a valid ticket.
151      */
152     @Test
153     public void testDontTryToAuthenticateEndlessly() throws Exception {
154         this.server.registerHandler("*", new PleaseNegotiateService());
155         final HttpHost target = start();
156 
157         final AuthSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager();
158         final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
159         final Credentials use_jaas_creds = new UseJaasCredentials();
160         credentialsProvider.setCredentials(new AuthScope(null, null, -1, null, null), use_jaas_creds);
161 
162         final Registry<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()
163             .register(StandardAuthScheme.SPNEGO, nsf)
164             .build();
165         this.httpclient = HttpClients.custom()
166             .setDefaultAuthSchemeRegistry(authSchemeRegistry)
167             .setDefaultCredentialsProvider(credentialsProvider)
168             .build();
169 
170         final String s = "/path";
171         final HttpGet httpget = new HttpGet(s);
172         final ClassicHttpResponse response = this.httpclient.execute(target, httpget);
173         EntityUtils.consume(response.getEntity());
174 
175         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
176     }
177 
178     /**
179      * Javadoc specifies that {@link GSSContext#initSecContext(byte[], int, int)} can return null
180      * if no token is generated. Client should be able to deal with this response.
181      */
182     @Test
183     public void testNoTokenGeneratedError() throws Exception {
184         this.server.registerHandler("*", new PleaseNegotiateService());
185         final HttpHost target = start();
186 
187         final AuthSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager();
188 
189         final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
190         final Credentials use_jaas_creds = new UseJaasCredentials();
191         credentialsProvider.setCredentials(new AuthScope(null, null, -1, null, null), use_jaas_creds);
192 
193         final Registry<AuthSchemeFactory> authSchemeRegistry = RegistryBuilder.<AuthSchemeFactory>create()
194             .register(StandardAuthScheme.SPNEGO, nsf)
195             .build();
196         this.httpclient = HttpClients.custom()
197             .setDefaultAuthSchemeRegistry(authSchemeRegistry)
198             .setDefaultCredentialsProvider(credentialsProvider)
199             .build();
200 
201         final String s = "/path";
202         final HttpGet httpget = new HttpGet(s);
203         final ClassicHttpResponse response = this.httpclient.execute(target, httpget);
204         EntityUtils.consume(response.getEntity());
205 
206         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
207     }
208 
209 }