1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module hunt.shiro.authc.UsernamePasswordToken;
20 
21 import hunt.shiro.authc.AuthenticationToken;
22 import hunt.shiro.authc.RememberMeAuthenticationToken;
23 import hunt.shiro.authc.HostAuthenticationToken;
24 
25 import hunt.util.StringBuilder;
26 
27 /**
28  * <p>A simple username/password authentication token to support the most widely-used authentication mechanism.  This
29  * class also : the {@link RememberMeAuthenticationToken RememberMeAuthenticationToken} interface to support
30  * &quot;Remember Me&quot; services across user sessions as well as the
31  * {@link hunt.shiro.authc.HostAuthenticationToken HostAuthenticationToken} interface to retain the host name
32  * or IP address location from where the authentication attempt is occurring.</p>
33  * <p/>
34  * <p>&quot;Remember Me&quot; authentications are disabled by default, but if the application developer wishes to allow
35  * it for a login attempt, all that is necessary is to call {@link #setRememberMe setRememberMe(true)}.  If the underlying
36  * <tt>SecurityManager</tt> implementation also supports <tt>RememberMe</tt> services, the user's identity will be
37  * remembered across sessions.
38  * <p/>
39  * <p>Note that this class stores a password as[] a char instead of a string
40  * (which may seem more logical).  This is because Strings are immutable and their
41  * internal value cannot be overwritten - meaning even a nulled string instance might be accessible in memory at a later
42  * time (e.g. memory dump).  This is not good for sensitive information such as passwords. For more information, see the
43  * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html#PBEEx">
44  * Java Cryptography Extension Reference Guide</a>.</p>
45  * <p/>
46  * <p>To avoid this possibility of later memory access, the application developer should always call
47  * {@link #clear() clear()} after using the token to perform a login attempt.</p>
48  *
49  */
50 class UsernamePasswordToken : HostAuthenticationToken, RememberMeAuthenticationToken {
51 
52     /*--------------------------------------------
53     |             C O N S T A N T S             |
54     ============================================*/
55 
56     /*--------------------------------------------
57     |    I N S T A N C E   V A R I A B L E S    |
58     ============================================*/
59     /**
60      * The username
61      */
62     private string username;
63 
64     /**
65      * The password, in[] char format
66      */
67     private char[] password;
68 
69     /**
70      * Whether or not 'rememberMe' should be enabled for the corresponding login attempt;
71      * default is <code>false</code>
72      */
73     private bool rememberMe = false;
74 
75     /**
76      * The location from where the login attempt occurs, or <code>null</code> if not known or explicitly
77      * omitted.
78      */
79     private string host;
80 
81     private string _name = DEFAULT_AUTH_TOKEN_NAME;
82 
83     /*--------------------------------------------
84     |         C O N S T R U C T O R S           |
85     ============================================*/
86 
87     /**
88      * JavaBeans compatible no-arg constructor.
89      */
90     this() {
91     }
92 
93     /**
94      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted
95      * during an authentication attempt, with a <tt>null</tt> {@link #getHost() host} and a
96      * <tt>rememberMe</tt> default of <tt>false</tt>.
97      *
98      * @param username the username submitted for authentication
99      * @param password the password character array submitted for authentication
100      */
101     this(string username, char[] password) {
102         this(username, password, false, null);
103     }
104 
105     /**
106      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted
107      * during an authentication attempt, with a <tt>null</tt> {@link #getHost() host} and
108      * a <tt>rememberMe</tt> default of <tt>false</tt>
109      * <p/>
110      * <p>This is a convenience constructor and maintains the password internally via a character
111      * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a string
112      * in your code could have possible security implications as noted in the class JavaDoc.</p>
113      *
114      * @param username the username submitted for authentication
115      * @param password the password string submitted for authentication
116      */
117     this(string username, string password) {
118         this(username, password !is null ? password.dup : null, false, cast(string)null);
119     }
120 
121     /**
122      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted, the
123      * inetAddress from where the attempt is occurring, and a default <tt>rememberMe</tt> value of <tt>false</tt>
124      *
125      * @param username the username submitted for authentication
126      * @param password the password string submitted for authentication
127      * @param host     the host name or IP string from where the attempt is occurring
128      */
129     this(string username, char[] password, string host) {
130         this(username, password, false, host);
131     }
132 
133     /**
134      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted, the
135      * inetAddress from where the attempt is occurring, and a default <tt>rememberMe</tt> value of <tt>false</tt>
136      * <p/>
137      * <p>This is a convenience constructor and maintains the password internally via a character
138      * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a string
139      * in your code could have possible security implications as noted in the class JavaDoc.</p>
140      *
141      * @param username the username submitted for authentication
142      * @param password the password string submitted for authentication
143      * @param host     the host name or IP string from where the attempt is occurring
144      */
145     this(string username, string password, string host) {
146         this(username, password !is null ? password.dup : null, false, host);
147     }
148 
149     /**
150      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted, as well as if the user
151      * wishes their identity to be remembered across sessions.
152      *
153      * @param username   the username submitted for authentication
154      * @param password   the password string submitted for authentication
155      * @param rememberMe if the user wishes their identity to be remembered across sessions
156      */
157     this(string username, char[] password, bool rememberMe) {
158         this(username, password, rememberMe, null);
159     }
160 
161     /**
162      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted, as well as if the user
163      * wishes their identity to be remembered across sessions.
164      * <p/>
165      * <p>This is a convenience constructor and maintains the password internally via a character
166      * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a string
167      * in your code could have possible security implications as noted in the class JavaDoc.</p>
168      *
169      * @param username   the username submitted for authentication
170      * @param password   the password string submitted for authentication
171      * @param rememberMe if the user wishes their identity to be remembered across sessions
172      */
173     this(string username, string password, bool rememberMe) {
174         this(username, password !is null ? password.dup : null, rememberMe, cast(string)null);
175     }
176 
177     /**
178      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted, if the user
179      * wishes their identity to be remembered across sessions, and the inetAddress from where the attempt is occurring.
180      *
181      * @param username   the username submitted for authentication
182      * @param password   the password character array submitted for authentication
183      * @param rememberMe if the user wishes their identity to be remembered across sessions
184      * @param host       the host name or IP string from where the attempt is occurring
185      */
186     this(string username, char[] password, bool rememberMe, string host) {
187         this.username = username;
188         this.password = password;
189         this.rememberMe = rememberMe;
190         this.host = host;
191     }
192 
193 
194     /**
195      * Constructs a new UsernamePasswordToken encapsulating the username and password submitted, if the user
196      * wishes their identity to be remembered across sessions, and the inetAddress from where the attempt is occurring.
197      * <p/>
198      * <p>This is a convenience constructor and maintains the password internally via a character
199      * array, i.e. <tt>password.toCharArray();</tt>.  Note that storing a password as a string
200      * in your code could have possible security implications as noted in the class JavaDoc.</p>
201      *
202      * @param username   the username submitted for authentication
203      * @param password   the password string submitted for authentication
204      * @param rememberMe if the user wishes their identity to be remembered across sessions
205      * @param host       the host name or IP string from where the attempt is occurring
206      */
207     this(string username, string password, bool rememberMe, string host) {
208         this(username, password !is null ? password.dup : null, rememberMe, host);
209     }
210 
211     /*--------------------------------------------
212     |  A C C E S S O R S / M O D I F I E R S    |
213     ============================================*/
214 
215     string name() {
216         return _name;
217     }
218 
219     void name(string value) {
220         _name = value;
221     }
222 
223     /**
224      * Returns the username submitted during an authentication attempt.
225      *
226      * @return the username submitted during an authentication attempt.
227      */
228     string getUsername() {
229         return username;
230     }
231 
232     /**
233      * Sets the username for submission during an authentication attempt.
234      *
235      * @param username the username to be used for submission during an authentication attempt.
236      */
237     void setUsername(string username) {
238         this.username = username;
239     }
240 
241 
242     /**
243      * Returns the password submitted during an authentication attempt as a character array.
244      *
245      * @return the password submitted during an authentication attempt as a character array.
246      */
247     char[] getPassword() {
248         return password;
249     }
250 
251     /**
252      * Sets the password for submission during an authentication attempt.
253      *
254      * @param password the password to be used for submission during an authentication attempt.
255      */
256     void setPassword(char[] password) {
257         this.password = password;
258     }
259 
260     /**
261      * Simply returns {@link #getUsername() getUsername()}.
262      *
263      * @return the {@link #getUsername() username}.
264      * @see hunt.shiro.authc.AuthenticationToken#getPrincipal()
265      */
266     string getPrincipal() {
267         return getUsername();
268     }
269 
270     /**
271      * Returns the {@link #getPassword() password} char array.
272      *
273      * @return the {@link #getPassword() password} char array.
274      * @see hunt.shiro.authc.AuthenticationToken#getCredentials()
275      */
276     char[] getCredentials() {
277         return getPassword();
278     }
279 
280     /**
281      * Returns the host name or IP string from where the authentication attempt occurs.  May be <tt>null</tt> if the
282      * host name/IP is unknown or explicitly omitted.  It is up to the Authenticator implementation processing this
283      * token if an authentication attempt without a host is valid or not.
284      * <p/>
285      * <p>(Shiro's default Authenticator allows <tt>null</tt> hosts to support localhost and proxy server environments).</p>
286      *
287      * @return the host from where the authentication attempt occurs, or <tt>null</tt> if it is unknown or
288      *         explicitly omitted.
289      */
290     string getHost() {
291         return host;
292     }
293 
294     /**
295      * Sets the host name or IP string from where the authentication attempt occurs.  It is up to the Authenticator
296      * implementation processing this token if an authentication attempt without a host is valid or not.
297      * <p/>
298      * <p>(Shiro's default Authenticator
299      * allows <tt>null</tt> hosts to allow localhost and proxy server environments).</p>
300      *
301      * @param host the host name or IP string from where the attempt is occurring
302      */
303      void setHost(string host) {
304         this.host = host;
305     }
306 
307     /**
308      * Returns <tt>true</tt> if the submitting user wishes their identity (principal(s)) to be remembered
309      * across sessions, <tt>false</tt> otherwise.  Unless overridden, this value is <tt>false</tt> by default.
310      *
311      * @return <tt>true</tt> if the submitting user wishes their identity (principal(s)) to be remembered
312      *         across sessions, <tt>false</tt> otherwise (<tt>false</tt> by default).
313      */
314     bool isRememberMe() {
315         return rememberMe;
316     }
317 
318     /**
319      * Sets if the submitting user wishes their identity (principal(s)) to be remembered across sessions.  Unless
320      * overridden, the default value is <tt>false</tt>, indicating <em>not</em> to be remembered across sessions.
321      *
322      * @param rememberMe value indicating if the user wishes their identity (principal(s)) to be remembered across
323      *                   sessions.
324      */
325     void setRememberMe(bool rememberMe) {
326         this.rememberMe = rememberMe;
327     }
328 
329     /*--------------------------------------------
330     |               M E T H O D S               |
331     ============================================*/
332 
333     /**
334      * Clears out (nulls) the username, password, rememberMe, and inetAddress.  The password bytes are explicitly set to
335      * <tt>0x00</tt> before nulling to eliminate the possibility of memory access at a later time.
336      */
337      void clear() {
338         this.username = null;
339         this.host = null;
340         this.rememberMe = false;
341 
342         if (this.password !is null) {
343             for (int i = 0; i < password.length; i++) {
344                 this.password[i] = 0x00;
345             }
346             this.password = null;
347         }
348 
349     }
350 
351     /**
352      * Returns the string representation.  It does not include the password in the resulting
353      * string for security reasons to prevent accidentally printing out a password
354      * that might be widely viewable).
355      *
356      * @return the string representation of the <tt>UsernamePasswordToken</tt>, omitting
357      *         the password.
358      */
359     override string toString() {
360         StringBuilder sb = new StringBuilder();
361         sb.append(typeid(this).name);
362         sb.append(" - ");
363         sb.append(username);
364         sb.append(", rememberMe=").append(rememberMe);
365         if (host !is null) {
366             sb.append(" (").append(host).append(")");
367         }
368         return sb.toString();
369     }
370 
371 }