1 /*
2  * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.net.httpserver;
27 
28 import java.nio.charset.Charset;
29 import java.util.Base64;
30 import java.util.Objects;
31 
32 import static java.nio.charset.StandardCharsets.UTF_8;
33 
34 /**
35  * BasicAuthenticator provides an implementation of HTTP Basic
36  * authentication. It is an abstract class and must be extended
37  * to provide an implementation of {@link #checkCredentials(String,String)}
38  * which is called to verify each incoming request.
39  */
40 public abstract class BasicAuthenticator extends Authenticator {
41 
42     protected final String realm;
43     private final Charset charset;
44     private final boolean isUTF8;
45 
46     /**
47      * Creates a BasicAuthenticator for the given HTTP realm.
48      * The Basic authentication credentials (username and password) are decoded
49      * using the platform's {@link Charset#defaultCharset() default character set}.
50      *
51      * @param realm The HTTP Basic authentication realm
52      * @throws NullPointerException if realm is {@code null}
53      * @throws IllegalArgumentException if realm is an empty string
54      */
BasicAuthenticator(String realm)55     public BasicAuthenticator (String realm) {
56         this(realm, Charset.defaultCharset());
57     }
58 
59     /**
60      * Creates a BasicAuthenticator for the given HTTP realm and using the
61      * given {@link Charset} to decode the Basic authentication credentials
62      * (username and password).
63      *
64      * @apiNote {@code UTF-8} is the recommended charset because its usage is
65      * communicated to the client, and therefore more likely to be used also
66      * by the client.
67      *
68      * @param realm The HTTP Basic authentication realm
69      * @param charset The Charset to decode incoming credentials from the client
70      * @throws NullPointerException if realm or charset are {@code null}
71      * @throws IllegalArgumentException if realm is an empty string
72      */
BasicAuthenticator(String realm, Charset charset)73     public BasicAuthenticator (String realm, Charset charset) {
74         Objects.requireNonNull(charset);
75         if (realm.isEmpty()) // implicit NPE check
76             throw new IllegalArgumentException("realm must not be empty");
77         this.realm = realm;
78         this.charset = charset;
79         this.isUTF8 = charset.equals(UTF_8);
80     }
81 
82     /**
83      * returns the realm this BasicAuthenticator was created with
84      * @return the authenticator's realm string.
85      */
getRealm()86     public String getRealm () {
87         return realm;
88     }
89 
authenticate(HttpExchange t)90     public Result authenticate (HttpExchange t)
91     {
92         Headers rmap = t.getRequestHeaders();
93         /*
94          * look for auth token
95          */
96         String auth = rmap.getFirst ("Authorization");
97         if (auth == null) {
98             setAuthHeader(t);
99             return new Authenticator.Retry (401);
100         }
101         int sp = auth.indexOf (' ');
102         if (sp == -1 || !auth.substring(0, sp).equals ("Basic")) {
103             return new Authenticator.Failure (401);
104         }
105         byte[] b = Base64.getDecoder().decode(auth.substring(sp+1));
106         String userpass = new String (b, charset);
107         int colon = userpass.indexOf (':');
108         String uname = userpass.substring (0, colon);
109         String pass = userpass.substring (colon+1);
110 
111         if (checkCredentials (uname, pass)) {
112             return new Authenticator.Success (
113                 new HttpPrincipal (
114                     uname, realm
115                 )
116             );
117         } else {
118             /* reject the request again with 401 */
119             setAuthHeader(t);
120             return new Authenticator.Failure(401);
121         }
122     }
123 
setAuthHeader(HttpExchange t)124     private void setAuthHeader(HttpExchange t) {
125         Headers map = t.getResponseHeaders();
126         var authString = "Basic realm=" + "\"" + realm + "\"" +
127             (isUTF8 ? ", charset=\"UTF-8\"" : "");
128         map.set ("WWW-Authenticate", authString);
129     }
130 
131     /**
132      * called for each incoming request to verify the
133      * given name and password in the context of this
134      * Authenticator's realm. Any caching of credentials
135      * must be done by the implementation of this method
136      * @param username the username from the request
137      * @param password the password from the request
138      * @return <code>true</code> if the credentials are valid,
139      *    <code>false</code> otherwise.
140      */
checkCredentials(String username, String password)141     public abstract boolean checkCredentials (String username, String password);
142 }
143 
144