]> git.basschouten.com Git - openhab-addons.git/blob
bc40e815b16250b8082d9ba1b2f399da6881190a
[openhab-addons.git] /
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved.
5  *
6  * The contents of this file are subject to the terms of either the GNU
7  * General Public License Version 2 only ("GPL") or the Common Development
8  * and Distribution License("CDDL") (collectively, the "License").  You
9  * may not use this file except in compliance with the License.  You can
10  * obtain a copy of the License at
11  * http://glassfish.java.net/public/CDDL+GPL_1_1.html
12  * or packager/legal/LICENSE.txt.  See the License for the specific
13  * language governing permissions and limitations under the License.
14  *
15  * When distributing the software, include this License Header Notice in each
16  * file and include the License file at packager/legal/LICENSE.txt.
17  *
18  * GPL Classpath Exception:
19  * Oracle designates this particular file as subject to the "Classpath"
20  * exception as provided by Oracle in the GPL Version 2 section of the License
21  * file that accompanied this code.
22  *
23  * Modifications:
24  * If applicable, add the following below the License Header, with the fields
25  * enclosed by brackets [] replaced by your own identifying information:
26  * "Portions Copyright [year] [name of copyright owner]"
27  *
28  * Contributor(s):
29  * If you wish your version of this file to be governed by only the CDDL or
30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
31  * elects to include this software in this distribution under the [CDDL or GPL
32  * Version 2] license."  If you don't indicate a single choice of license, a
33  * recipient has the option to distribute your version of this file under
34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
35  * its licensees as provided above.  However, if you add GPL Version 2 code
36  * and therefore, elected the GPL Version 2 license, then the option applies
37  * only if the new code is made subject to such option by the copyright
38  * holder.
39  */
40 package org.openhab.binding.lametrictime.internal.api.authentication;
41
42 import javax.ws.rs.core.Feature;
43 import javax.ws.rs.core.FeatureContext;
44
45 /**
46  * Features that provides Http Basic and Digest client authentication (based on RFC 2617).
47  * <p>
48  * The feature can work in following modes:
49  * <ul>
50  * <li><b>BASIC:</b> Basic preemptive authentication. In preemptive mode the authentication information
51  * is send always with each HTTP request. This mode is more usual than the following non-preemptive mode
52  * (if you require BASIC authentication you will probably use this preemptive mode). This mode must
53  * be combined with usage of SSL/TLS as the password is send only BASE64 encoded.</li>
54  * <li><i>BASIC NON-PREEMPTIVE:</i> Basic non-preemptive authentication. In non-preemptive mode the
55  * authentication information is added only when server refuses the request with {@code 401} status code and
56  * then the request is repeated with authentication information. This mode has negative impact on the performance.
57  * The advantage is that it does not send credentials when they are not needed. This mode must
58  * be combined with usage of SSL/TLS as the password is send only BASE64 encoded.
59  * </li>
60  * <li><b>DIGEST:</b> Http digest authentication. Does not require usage of SSL/TLS.</li>
61  * <li><b>UNIVERSAL:</b> Combination of basic and digest authentication. The feature works in non-preemptive
62  * mode which means that it sends requests without authentication information. If {@code 401} status
63  * code is returned, the request is repeated and an appropriate authentication is used based on the
64  * authentication requested in the response (defined in {@code WWW-Authenticate} HTTP header. The feature
65  * remembers which authentication requests were successful for given URI and next time tries to preemptively
66  * authenticate against this URI with latest successful authentication method.
67  * </li>
68  * </ul>
69  * </p>
70  * <p>
71  * To initialize the feature use static method of this feature.
72  * </p>
73  * <p>
74  * Example of building the feature in
75  * Basic authentication mode:
76  *
77  * <pre>
78  * HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("user", "superSecretPassword");
79  * </pre>
80  * </p>
81  * <p>
82  * Example of building the feature in basic non-preemptive mode:
83  *
84  * <pre>
85  * HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().nonPreemptive()
86  *         .credentials("user", "superSecretPassword").build();
87  * </pre>
88  * </p>
89  * <p>
90  * Example of building the feature in universal mode:
91  *
92  * <pre>
93  * HttpAuthenticationFeature feature = HttpAuthenticationFeature.universal("user", "superSecretPassword");
94  * </pre>
95  * </p>
96  * <p>
97  * Example of building the feature in universal mode with different credentials for basic and digest:
98  *
99  * <pre>
100  * HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder()
101  *         .credentialsForBasic("user", "123456").credentials("adminuser", "hello").build();
102  * </pre>
103  * </p>
104  * Example of building the feature in basic preemptive mode with no default credentials. Credentials will have
105  * to be supplied with each request using request properties (see below):
106  *
107  * <pre>
108  * HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder().build();
109  * </pre>
110  * </p>
111  * <p>
112  * Once the feature is built it needs to be registered into the {@link javax.ws.rs.client.Client},
113  * {@link javax.ws.rs.client.WebTarget} or other client configurable object. Example:
114  *
115  * <pre>
116  * final Client client = ClientBuilder.newClient();
117  * client.register(feature);
118  * </pre>
119  * </p>
120  *
121  * Then you invoke requests as usual and authentication will be handled by the feature.
122  * You can change the credentials for each request using properties
123  *
124  * <pre>
125  * final Response response = client.target("http://localhost:8080/rest/homer/contact").request()
126  *         .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "homer")
127  *         .property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745").get();
128  * </pre>
129  * <p>
130  * This class also contains property key definitions for overriding only specific basic or digest credentials:
131  * </p>
132  *
133  * @author Miroslav Fuksa
134  *
135  * @since 2.5
136  */
137 public class HttpAuthenticationFeature implements Feature {
138
139     /**
140      * Feature authentication mode.
141      */
142     static enum Mode {
143         /**
144          * Basic preemptive.
145          **/
146         BASIC_PREEMPTIVE,
147         /**
148          * Basic non preemptive
149          */
150         BASIC_NON_PREEMPTIVE,
151         /**
152          * Digest.
153          */
154         DIGEST,
155         /**
156          * Universal.
157          */
158         UNIVERSAL
159     }
160
161     /**
162      * Builder that creates instances of {@link HttpAuthenticationFeature}.
163      */
164     public static interface Builder {
165
166         /**
167          * Set credentials.
168          *
169          * @param username Username.
170          * @param password Password as byte array.
171          * @return This builder.
172          */
173         public Builder credentials(String username, byte[] password);
174
175         /**
176          * Set credentials.
177          *
178          * @param username Username.
179          * @param password Password as {@link String}.
180          * @return This builder.
181          */
182         public Builder credentials(String username, String password);
183
184         /**
185          * Build the feature.
186          *
187          * @return Http authentication feature configured from this builder.
188          */
189         public HttpAuthenticationFeature build();
190     }
191
192     /**
193      * Extension of {@link org.glassfish.jersey.client.authentication.HttpAuthenticationFeature.Builder}
194      * that builds the http authentication feature configured for basic authentication.
195      */
196     public static interface BasicBuilder extends Builder {
197
198         /**
199          * Configure the builder to create features in non-preemptive basic authentication mode.
200          *
201          * @return This builder.
202          */
203         public BasicBuilder nonPreemptive();
204     }
205
206     /**
207      * that builds the http authentication feature configured in universal mode that supports
208      * basic and digest authentication.
209      */
210     public static interface UniversalBuilder extends Builder {
211
212         /**
213          * Set credentials that will be used for basic authentication only.
214          *
215          * @param username Username.
216          * @param password Password as {@link String}.
217          * @return This builder.
218          */
219         public UniversalBuilder credentialsForBasic(String username, String password);
220
221         /**
222          * Set credentials that will be used for basic authentication only.
223          *
224          * @param username Username.
225          * @param password Password as {@code byte array}.
226          * @return This builder.
227          */
228         public UniversalBuilder credentialsForBasic(String username, byte[] password);
229
230         /**
231          * Set credentials that will be used for digest authentication only.
232          *
233          * @param username Username.
234          * @param password Password as {@link String}.
235          * @return This builder.
236          */
237         public UniversalBuilder credentialsForDigest(String username, String password);
238
239         /**
240          * Set credentials that will be used for digest authentication only.
241          *
242          * @param username Username.
243          * @param password Password as {@code byte array}.
244          * @return This builder.
245          */
246         public UniversalBuilder credentialsForDigest(String username, byte[] password);
247     }
248
249     /**
250      * Implementation of all authentication builders.
251      */
252     static class BuilderImpl implements UniversalBuilder, BasicBuilder {
253
254         private String usernameBasic;
255         private byte[] passwordBasic;
256         private String usernameDigest;
257         private byte[] passwordDigest;
258         private Mode mode;
259
260         /**
261          * Create a new builder.
262          *
263          * @param mode Mode in which the final authentication feature should work.
264          */
265         public BuilderImpl(Mode mode) {
266             this.mode = mode;
267         }
268
269         @Override
270         public Builder credentials(String username, String password) {
271             return credentials(username,
272                     password == null ? null : password.getBytes(HttpAuthenticationFilter.CHARACTER_SET));
273         }
274
275         @Override
276         public Builder credentials(String username, byte[] password) {
277             credentialsForBasic(username, password);
278             credentialsForDigest(username, password);
279             return this;
280         }
281
282         @Override
283         public UniversalBuilder credentialsForBasic(String username, String password) {
284             return credentialsForBasic(username,
285                     password == null ? null : password.getBytes(HttpAuthenticationFilter.CHARACTER_SET));
286         }
287
288         @Override
289         public UniversalBuilder credentialsForBasic(String username, byte[] password) {
290             this.usernameBasic = username;
291             this.passwordBasic = password;
292             return this;
293         }
294
295         @Override
296         public UniversalBuilder credentialsForDigest(String username, String password) {
297             return credentialsForDigest(username,
298                     password == null ? null : password.getBytes(HttpAuthenticationFilter.CHARACTER_SET));
299         }
300
301         @Override
302         public UniversalBuilder credentialsForDigest(String username, byte[] password) {
303             this.usernameDigest = username;
304             this.passwordDigest = password;
305             return this;
306         }
307
308         @Override
309         public HttpAuthenticationFeature build() {
310             return new HttpAuthenticationFeature(mode,
311                     usernameBasic == null ? null
312                             : new HttpAuthenticationFilter.Credentials(usernameBasic, passwordBasic),
313                     usernameDigest == null ? null
314                             : new HttpAuthenticationFilter.Credentials(usernameDigest, passwordDigest));
315         }
316
317         @Override
318         public BasicBuilder nonPreemptive() {
319             if (mode == Mode.BASIC_PREEMPTIVE) {
320                 this.mode = Mode.BASIC_NON_PREEMPTIVE;
321             }
322             return this;
323         }
324     }
325
326     /**
327      * Key of the property that can be set into the {@link javax.ws.rs.client.ClientRequestContext client request}
328      * using {@link javax.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
329      * the username for http authentication feature for the request.
330      * <p>
331      * Example:
332      *
333      * <pre>
334      * Response response = client.target("http://localhost:8080/rest/joe/orders").request()
335      *         .property(HTTP_AUTHENTICATION_USERNAME, "joe").property(HTTP_AUTHENTICATION_PASSWORD, "p1swd745").get();
336      * </pre>
337      * </p>
338      * The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
339      * (as shown in the example). This property pair overrides all password settings of the authentication
340      * feature for the current request.
341      * <p>
342      * The default value must be instance of {@link String}.
343      * </p>
344      * <p>
345      * The name of the configuration property is <tt>{@value}</tt>.
346      * </p>
347      */
348     public static final String HTTP_AUTHENTICATION_USERNAME = "jersey.config.client.http.auth.username";
349     /**
350      * Key of the property that can be set into the {@link javax.ws.rs.client.ClientRequestContext client request}
351      * using {@link javax.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
352      * the password for http authentication feature for the request.
353      * <p>
354      * Example:
355      *
356      * <pre>
357      * Response response = client.target("http://localhost:8080/rest/joe/orders").request()
358      *         .property(HTTP_AUTHENTICATION_USERNAME, "joe").property(HTTP_AUTHENTICATION_PASSWORD, "p1swd745").get();
359      * </pre>
360      * </p>
361      * The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_USERNAME} property
362      * (as shown in the example). This property pair overrides all password settings of the authentication
363      * feature for the current request.
364      * <p>
365      * The value must be instance of {@link String} or {@code byte} array ({@code byte[]}).
366      * </p>
367      * <p>
368      * The name of the configuration property is <tt>{@value}</tt>.
369      * </p>
370      */
371     public static final String HTTP_AUTHENTICATION_PASSWORD = "jersey.config.client.http.auth.password";
372
373     /**
374      * Key of the property that can be set into the {@link javax.ws.rs.client.ClientRequestContext client request}
375      * using {@link javax.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
376      * the username for http basic authentication feature for the request.
377      * <p>
378      * Example:
379      *
380      * <pre>
381      * Response response = client.target("http://localhost:8080/rest/joe/orders").request()
382      *         .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "joe").property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745")
383      *         .get();
384      * </pre>
385      * </p>
386      * The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
387      * (as shown in the example). The property pair influence only credentials used during basic authentication.
388      *
389      * <p>
390      * The value must be instance of {@link String}.
391      * </p>
392      * <p>
393      * The name of the configuration property is <tt>{@value}</tt>.
394      * </p>
395      *
396      */
397     public static final String HTTP_AUTHENTICATION_BASIC_USERNAME = "jersey.config.client.http.auth.basic.username";
398
399     /**
400      * Key of the property that can be set into the {@link javax.ws.rs.client.ClientRequestContext client request}
401      * using {@link javax.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
402      * the password for http basic authentication feature for the request.
403      * <p>
404      * Example:
405      *
406      * <pre>
407      * Response response = client.target("http://localhost:8080/rest/joe/orders").request()
408      *         .property(HTTP_AUTHENTICATION_BASIC_USERNAME, "joe").property(HTTP_AUTHENTICATION_BASIC_PASSWORD, "p1swd745")
409      *         .get();
410      * </pre>
411      * </p>
412      * The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_USERNAME} property
413      * (as shown in the example). The property pair influence only credentials used during basic authentication.
414      * <p>
415      * The value must be instance of {@link String} or {@code byte} array ({@code byte[]}).
416      * </p>
417      * <p>
418      * The name of the configuration property is <tt>{@value}</tt>.
419      * </p>
420      */
421     public static final String HTTP_AUTHENTICATION_BASIC_PASSWORD = "jersey.config.client.http.auth.basic.password";
422
423     /**
424      * Key of the property that can be set into the {@link javax.ws.rs.client.ClientRequestContext client request}
425      * using {@link javax.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
426      * the username for http digest authentication feature for the request.
427      * <p>
428      * Example:
429      *
430      * <pre>
431      * Response response = client.target("http://localhost:8080/rest/joe/orders").request()
432      *         .property(HTTP_AUTHENTICATION_DIGEST_USERNAME, "joe")
433      *         .property(HTTP_AUTHENTICATION_DIGEST_PASSWORD, "p1swd745").get();
434      * </pre>
435      * </p>
436      * The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
437      * (as shown in the example). The property pair influence only credentials used during digest authentication.
438      * <p>
439      * The value must be instance of {@link String}.
440      * </p>
441      * <p>
442      * The name of the configuration property is <tt>{@value}</tt>.
443      * </p>
444      */
445     public static final String HTTP_AUTHENTICATION_DIGEST_USERNAME = "jersey.config.client.http.auth.digest.username";
446
447     /**
448      * Key of the property that can be set into the {@link javax.ws.rs.client.ClientRequestContext client request}
449      * using {@link javax.ws.rs.client.ClientRequestContext#setProperty(String, Object)} in order to override
450      * the password for http digest authentication feature for the request.
451      * <p>
452      * Example:
453      *
454      * <pre>
455      * Response response = client.target("http://localhost:8080/rest/joe/orders").request()
456      *         .property(HTTP_AUTHENTICATION_DIGEST_USERNAME, "joe")
457      *         .property(HTTP_AUTHENTICATION_DIGEST_PASSWORD, "p1swd745").get();
458      * </pre>
459      * </p>
460      * The property must be always combined with configuration of {@link #HTTP_AUTHENTICATION_PASSWORD} property
461      * (as shown in the example). The property pair influence only credentials used during digest authentication.
462      * <p>
463      * The value must be instance of {@link String} or {@code byte} array ({@code byte[]}).
464      * </p>
465      * <p>
466      * The name of the configuration property is <tt>{@value}</tt>.
467      * </p>
468      */
469     public static final String HTTP_AUTHENTICATION_DIGEST_PASSWORD = "jersey.config.client.http.auth.digest.password";
470
471     /**
472      * Create the builder of the http authentication feature working in basic authentication mode. The builder
473      * can build preemptive and non-preemptive basic authentication features.
474      *
475      * @return Basic http authentication builder.
476      */
477     public static BasicBuilder basicBuilder() {
478         return new BuilderImpl(Mode.BASIC_PREEMPTIVE);
479     }
480
481     /**
482      * Create the http authentication feature in basic preemptive authentication mode initialized with credentials.
483      *
484      * @param username Username.
485      * @param password Password as {@code byte array}.
486      * @return Http authentication feature configured in basic mode.
487      */
488     public static HttpAuthenticationFeature basic(String username, byte[] password) {
489         return build(Mode.BASIC_PREEMPTIVE, username, password);
490     }
491
492     /**
493      * Create the http authentication feature in basic preemptive authentication mode initialized with credentials.
494      *
495      * @param username Username.
496      * @param password Password as {@link String}.
497      * @return Http authentication feature configured in basic mode.
498      */
499     public static HttpAuthenticationFeature basic(String username, String password) {
500         return build(Mode.BASIC_PREEMPTIVE, username, password);
501     }
502
503     /**
504      * Create the http authentication feature in digest authentication mode initialized without default
505      * credentials. Credentials will have to be supplied using request properties for each request.
506      *
507      * @return Http authentication feature configured in digest mode.
508      */
509     public static HttpAuthenticationFeature digest() {
510         return build(Mode.DIGEST);
511     }
512
513     /**
514      * Create the http authentication feature in digest authentication mode initialized with credentials.
515      *
516      * @param username Username.
517      * @param password Password as {@code byte array}.
518      * @return Http authentication feature configured in digest mode.
519      */
520     public static HttpAuthenticationFeature digest(String username, byte[] password) {
521         return build(Mode.DIGEST, username, password);
522     }
523
524     /**
525      * Create the http authentication feature in digest authentication mode initialized with credentials.
526      *
527      * @param username Username.
528      * @param password Password as {@link String}.
529      * @return Http authentication feature configured in digest mode.
530      */
531     public static HttpAuthenticationFeature digest(String username, String password) {
532         return build(Mode.DIGEST, username, password);
533     }
534
535     /**
536      * Create the builder that builds http authentication feature in combined mode supporting both,
537      * basic and digest authentication.
538      *
539      * @return Universal builder.
540      */
541     public static UniversalBuilder universalBuilder() {
542         return new BuilderImpl(Mode.UNIVERSAL);
543     }
544
545     /**
546      * Create the http authentication feature in combined mode supporting both,
547      * basic and digest authentication.
548      *
549      * @param username Username.
550      * @param password Password as {@code byte array}.
551      * @return Http authentication feature configured in digest mode.
552      */
553     public static HttpAuthenticationFeature universal(String username, byte[] password) {
554         return build(Mode.UNIVERSAL, username, password);
555     }
556
557     /**
558      * Create the http authentication feature in combined mode supporting both,
559      * basic and digest authentication.
560      *
561      * @param username Username.
562      * @param password Password as {@link String}.
563      * @return Http authentication feature configured in digest mode.
564      */
565     public static HttpAuthenticationFeature universal(String username, String password) {
566         return build(Mode.UNIVERSAL, username, password);
567     }
568
569     private static HttpAuthenticationFeature build(Mode mode) {
570         return new BuilderImpl(mode).build();
571     }
572
573     private static HttpAuthenticationFeature build(Mode mode, String username, byte[] password) {
574         return new BuilderImpl(mode).credentials(username, password).build();
575     }
576
577     private static HttpAuthenticationFeature build(Mode mode, String username, String password) {
578         return new BuilderImpl(mode).credentials(username, password).build();
579     }
580
581     private final Mode mode;
582     private final HttpAuthenticationFilter.Credentials basicCredentials;
583
584     private HttpAuthenticationFeature(Mode mode, HttpAuthenticationFilter.Credentials basicCredentials,
585             HttpAuthenticationFilter.Credentials digestCredentials) {
586         this.mode = mode;
587         this.basicCredentials = basicCredentials;
588     }
589
590     @Override
591     public boolean configure(FeatureContext context) {
592         context.register(new HttpAuthenticationFilter(mode, basicCredentials, context.getConfiguration()));
593         return true;
594     }
595 }