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.mgt.AbstractRememberMeManager;
20 
21 import hunt.shiro.mgt.RememberMeManager;
22 
23 import hunt.shiro.Exceptions;
24 import hunt.shiro.authc.AuthenticationInfo;
25 import hunt.shiro.authc.AuthenticationToken;
26 import hunt.shiro.authc.RememberMeAuthenticationToken;
27 import hunt.shiro.codec.Base64;
28 import hunt.shiro.crypto.AesCipherService;
29 import hunt.shiro.crypto.CipherService;
30 // import hunt.shiro.io.DefaultSerializer;
31 // import hunt.shiro.io.Serializer;
32 import hunt.shiro.subject.PrincipalCollection;
33 import hunt.shiro.subject.Subject;
34 import hunt.shiro.subject.SubjectContext;
35 import hunt.shiro.util.ByteSource;
36 
37 import hunt.Exceptions;
38 import hunt.logging.Logger;
39 
40 /**
41  * Abstract implementation of the {@code RememberMeManager} interface that handles
42  * {@link #setSerializer(hunt.shiro.io.Serializer) serialization} and
43  * {@link #setCipherService encryption} of the remembered user identity.
44  * <p/>
45  * The remembered identity storage location and details are left to subclasses.
46  * <h2>Default encryption key</h2>
47  * This implementation uses an {@link AesCipherService AesCipherService} for strong encryption by default.  It also
48  * uses a default generated symmetric key to both encrypt and decrypt data.  As AES is a symmetric cipher, the same
49  * {@code key} is used to both encrypt and decrypt data, BUT NOTE:
50  * <p/>
51  * Because Shiro is an open-source project, if anyone knew that you were using Shiro's default
52  * {@code key}, they could download/view the source, and with enough effort, reconstruct the {@code key}
53  * and decode encrypted data at will.
54  * <p/>
55  * Of course, this key is only really used to encrypt the remembered {@code PrincipalCollection} which is typically
56  * a user id or username.  So if you do not consider that sensitive information, and you think the default key still
57  * makes things 'sufficiently difficult', then you can ignore this issue.
58  * <p/>
59  * However, if you do feel this constitutes sensitive information, it is recommended that you provide your own
60  * {@code key} via the {@link #setCipherKey setCipherKey} method to a key known only to your application,
61  * guaranteeing that no third party can decrypt your data.  You can generate your own key by calling the
62  * {@code CipherService}'s {@link hunt.shiro.crypto.AesCipherService#generateNewKey() generateNewKey} method
63  * and using that result as the {@link #setCipherKey cipherKey} configuration attribute.
64  *
65  */
66 abstract class AbstractRememberMeManager : RememberMeManager {
67 
68     /**
69      * private inner log instance.
70      */
71 
72 
73     /**
74      * Serializer to use for converting PrincipalCollection instances to/from byte arrays
75      */
76     // private Serializer!(PrincipalCollection) serializer;
77 
78     /**
79      * Cipher to use for encrypting/decrypting serialized byte arrays for added security
80      */
81     private CipherService cipherService;
82 
83     /**
84      * Cipher encryption key to use with the Cipher when encrypting data
85      */
86     private byte[] encryptionCipherKey;
87 
88     /**
89      * Cipher decryption key to use with the Cipher when decrypting data
90      */
91     private byte[] decryptionCipherKey;
92 
93     /**
94      * Default constructor that initializes a {@link DefaultSerializer} as the {@link #getSerializer() serializer} and
95      * an {@link AesCipherService} as the {@link #getCipherService() cipherService}.
96      */
97      this() {
98         // this.serializer = new DefaultSerializer!(PrincipalCollection)();
99         AesCipherService cipherService = new AesCipherService();
100         this.cipherService = cipherService;
101         setCipherKey(cipherService.generateNewKey().getEncoded());
102     }
103 
104     // /**
105     //  * Returns the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
106     //  * persistent remember me storage.
107     //  * <p/>
108     //  * Unless overridden by the {@link #setSerializer} method, the default instance is a
109     //  * {@link hunt.shiro.io.DefaultSerializer}.
110     //  *
111     //  * @return the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
112     //  *         persistent remember me storage.
113     //  */
114     //  Serializer!(PrincipalCollection) getSerializer() {
115     //     return serializer;
116     // }
117 
118     // /**
119     //  * Sets the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
120     //  * persistent remember me storage.
121     //  * <p/>
122     //  * Unless overridden by this method, the default instance is a {@link DefaultSerializer}.
123     //  *
124     //  * @param serializer the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances
125     //  *                   for persistent remember me storage.
126     //  */
127     //  void setSerializer(Serializer!(PrincipalCollection) serializer) {
128     //     this.serializer = serializer;
129     // }
130 
131     /**
132      * Returns the {@code CipherService} to use for encrypting and decrypting serialized identity data to prevent easy
133      * inspection of Subject identity data.
134      * <p/>
135      * Unless overridden by the {@link #setCipherService} method, the default instance is an {@link AesCipherService}.
136      *
137      * @return the {@code Cipher} to use for encrypting and decrypting serialized identity data to prevent easy
138      *         inspection of Subject identity data
139      */
140      CipherService getCipherService() {
141         return cipherService;
142     }
143 
144     /**
145      * Sets the {@code CipherService} to use for encrypting and decrypting serialized identity data to prevent easy
146      * inspection of Subject identity data.
147      * <p/>
148      * If the CipherService is a symmetric CipherService (using the same key for both encryption and decryption), you
149      * should set your key via the {@link #setCipherKey(byte[])} method.
150      * <p/>
151      * If the CipherService is an asymmetric CipherService (different keys for encryption and decryption, such as
152      * public/private key pairs), you should set your encryption and decryption key via the respective
153      * {@link #setEncryptionCipherKey(byte[])} and {@link #setDecryptionCipherKey(byte[])} methods.
154      * <p/>
155      * <b>N.B.</b> Unless overridden by this method, the default CipherService instance is an
156      * {@link AesCipherService}.  This {@code RememberMeManager} implementation already has a configured symmetric key
157      * to use for encryption and decryption, but it is recommended to provide your own for added security.  See the
158      * class-level JavaDoc for more information and why it might be good to provide your own.
159      *
160      * @param cipherService the {@code CipherService} to use for encrypting and decrypting serialized identity data to
161      *                      prevent easy inspection of Subject identity data.
162      */
163      void setCipherService(CipherService cipherService) {
164         this.cipherService = cipherService;
165     }
166 
167     /**
168      * Returns the cipher key to use for encryption operations.
169      *
170      * @return the cipher key to use for encryption operations.
171      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
172      */
173      byte[] getEncryptionCipherKey() {
174         return encryptionCipherKey;
175     }
176 
177     /**
178      * Sets the encryption key to use for encryption operations.
179      *
180      * @param encryptionCipherKey the encryption key to use for encryption operations.
181      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
182      */
183      void setEncryptionCipherKey(byte[] encryptionCipherKey) {
184         this.encryptionCipherKey = encryptionCipherKey;
185     }
186 
187     /**
188      * Returns the decryption cipher key to use for decryption operations.
189      *
190      * @return the cipher key to use for decryption operations.
191      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
192      */
193      byte[] getDecryptionCipherKey() {
194         return decryptionCipherKey;
195     }
196 
197     /**
198      * Sets the decryption key to use for decryption operations.
199      *
200      * @param decryptionCipherKey the decryption key to use for decryption operations.
201      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
202      */
203      void setDecryptionCipherKey(byte[] decryptionCipherKey) {
204         this.decryptionCipherKey = decryptionCipherKey;
205     }
206 
207     /**
208      * Convenience method that returns the cipher key to use for <em>both</em> encryption and decryption.
209      * <p/>
210      * <b>N.B.</b> This method can only be called if the underlying {@link #getCipherService() cipherService} is a symmetric
211      * CipherService which by definition uses the same key for both encryption and decryption.  If using an asymmetric
212      * CipherService public/private key pair, you cannot use this method, and should instead use the
213      * {@link #getEncryptionCipherKey()} and {@link #getDecryptionCipherKey()} methods individually.
214      * <p/>
215      * The default {@link AesCipherService} instance is a symmetric cipher service, so this method can be used if you are
216      * using the default.
217      *
218      * @return the symmetric cipher key used for both encryption and decryption.
219      */
220      byte[] getCipherKey() {
221         //Since this method should only be used with symmetric ciphers
222         //(where the enc and dec keys are the same), either is fine, just return one of them:
223         return getEncryptionCipherKey();
224     }
225 
226     /**
227      * Convenience method that sets the cipher key to use for <em>both</em> encryption and decryption.
228      * <p/>
229      * <b>N.B.</b> This method can only be called if the underlying {@link #getCipherService() cipherService} is a
230      * symmetric CipherService?which by definition uses the same key for both encryption and decryption.  If using an
231      * asymmetric CipherService?(such as a public/private key pair), you cannot use this method, and should instead use
232      * the {@link #setEncryptionCipherKey(byte[])} and {@link #setDecryptionCipherKey(byte[])} methods individually.
233      * <p/>
234      * The default {@link AesCipherService} instance is a symmetric CipherService, so this method can be used if you
235      * are using the default.
236      *
237      * @param cipherKey the symmetric cipher key to use for both encryption and decryption.
238      */
239      void setCipherKey(byte[] cipherKey) {
240         //Since this method should only be used in symmetric ciphers
241         //(where the enc and dec keys are the same), set it on both:
242         setEncryptionCipherKey(cipherKey);
243         setDecryptionCipherKey(cipherKey);
244     }
245 
246     /**
247      * Forgets (removes) any remembered identity data for the specified {@link Subject} instance.
248      *
249      * @param subject the subject instance for which identity data should be forgotten from the underlying persistence
250      *                mechanism.
251      */
252     protected abstract void forgetIdentity(Subject subject);
253     
254     protected abstract void forgetIdentity(SubjectContext subject);
255 
256 
257     /**
258      * Determines whether or not remember me services should be performed for the specified token.  This method returns
259      * {@code true} iff:
260      * <ol>
261      * <li>The token is not {@code null} and</li>
262      * <li>The token is an {@code instanceof} {@link RememberMeAuthenticationToken} and</li>
263      * <li>{@code token}.{@link hunt.shiro.authc.RememberMeAuthenticationToken#isRememberMe() isRememberMe()} is
264      * {@code true}</li>
265      * </ol>
266      *
267      * @param token the authentication token submitted during the successful authentication attempt.
268      * @return true if remember me services should be performed as a result of the successful authentication attempt.
269      */
270     protected bool isRememberMe(AuthenticationToken token) {
271         auto tokenCast = cast(RememberMeAuthenticationToken)token;
272         return token !is null && (tokenCast !is null) &&
273                 tokenCast.isRememberMe();
274     }
275 
276     /**
277      * Reacts to the successful login attempt by first always {@link #forgetIdentity(Subject) forgetting} any previously
278      * stored identity.  Then if the {@code token}
279      * {@link #isRememberMe(hunt.shiro.authc.AuthenticationToken) is a RememberMe} token, the associated identity
280      * will be {@link #rememberIdentity(hunt.shiro.subject.Subject, hunt.shiro.authc.AuthenticationToken, hunt.shiro.authc.AuthenticationInfo) remembered}
281      * for later retrieval during a new user session.
282      *
283      * @param subject the subject for which the principals are being remembered.
284      * @param token   the token that resulted in a successful authentication attempt.
285      * @param info    the authentication info resulting from the successful authentication attempt.
286      */
287      void onSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info) {
288         //always clear any previous identity:
289         forgetIdentity(subject);
290 
291         //now save the new identity:
292         if (isRememberMe(token)) {
293             rememberIdentity(subject, token, info);
294         } else {
295             version(HUNT_DEBUG) {
296                 tracef("AuthenticationToken did not indicate RememberMe is requested.  " ~
297                         "RememberMe functionality will not be executed for corresponding account.");
298             }
299         }
300     }
301 
302     /**
303      * Remembers a subject-unique identity for retrieval later.  This implementation first
304      * {@link #getIdentityToRemember resolves} the exact
305      * {@link PrincipalCollection principals} to remember.  It then remembers the principals by calling
306      * {@link #rememberIdentity(hunt.shiro.subject.Subject, hunt.shiro.subject.PrincipalCollection)}.
307      * <p/>
308      * This implementation ignores the {@link AuthenticationToken} argument, but it is available to subclasses if
309      * necessary for custom logic.
310      *
311      * @param subject   the subject for which the principals are being remembered.
312      * @param token     the token that resulted in a successful authentication attempt.
313      * @param authcInfo the authentication info resulting from the successful authentication attempt.
314      */
315      void rememberIdentity(Subject subject, AuthenticationToken token, AuthenticationInfo authcInfo) {
316         PrincipalCollection principals = getIdentityToRemember(subject, authcInfo);
317         rememberIdentity(subject, principals);
318     }
319 
320     /**
321      * Returns {@code info}.{@link hunt.shiro.authc.AuthenticationInfo#getPrincipals() getPrincipals()} and
322      * ignores the {@link Subject} argument.
323      *
324      * @param subject the subject for which the principals are being remembered.
325      * @param info    the authentication info resulting from the successful authentication attempt.
326      * @return the {@code PrincipalCollection} to remember.
327      */
328     protected PrincipalCollection getIdentityToRemember(Subject subject, AuthenticationInfo info) {
329         return info.getPrincipals();
330     }
331 
332     /**
333      * Remembers the specified account principals by first
334      * {@link #convertPrincipalsToBytes(hunt.shiro.subject.PrincipalCollection) converting} them to a byte
335      * array and then {@link #rememberSerializedIdentity(hunt.shiro.subject.Subject, byte[]) remembers} that
336      * byte array.
337      *
338      * @param subject           the subject for which the principals are being remembered.
339      * @param accountPrincipals the principals to remember for retrieval later.
340      */
341     protected void rememberIdentity(Subject subject, PrincipalCollection accountPrincipals) {
342         byte[] bytes = convertPrincipalsToBytes(accountPrincipals);
343         rememberSerializedIdentity(subject, bytes);
344     }
345 
346     /**
347      * Converts the given principal collection the byte array that will be persisted to be 'remembered' later.
348      * <p/>
349      * This implementation first {@link #serialize(hunt.shiro.subject.PrincipalCollection) serializes} the
350      * principals to a byte array and then {@link #encrypt(byte[]) encrypts} that byte array.
351      *
352      * @param principals the {@code PrincipalCollection} to convert to a byte array
353      * @return the representative byte array to be persisted for remember me functionality.
354      */
355     protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
356         byte[] bytes = serialize(principals);
357         if (getCipherService() !is null) {
358             bytes = encrypt(bytes);
359         }
360         return bytes;
361     }
362 
363     /**
364      * Persists the identity bytes to a persistent store for retrieval later via the
365      * {@link #getRememberedSerializedIdentity(SubjectContext)} method.
366      *
367      * @param subject    the Subject for which the identity is being serialized.
368      * @param serialized the serialized bytes to be persisted.
369      */
370     protected abstract void rememberSerializedIdentity(Subject subject, byte[] serialized);
371 
372     /**
373      * Implements the interface method by first {@link #getRememberedSerializedIdentity(SubjectContext) acquiring}
374      * the remembered serialized byte array.  Then it {@link #convertBytesToPrincipals(byte[], SubjectContext) converts}
375      * them and returns the re-constituted {@link PrincipalCollection}.  If no remembered principals could be
376      * obtained, {@code null} is returned.
377      * <p/>
378      * If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, SubjectContext)} method
379      * is called to allow any necessary post-processing (such as immediately removing any previously remembered
380      * values for safety).
381      *
382      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
383      *                       is being used to construct a {@link Subject} instance.
384      * @return the remembered principals or {@code null} if none could be acquired.
385      */
386      PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
387         PrincipalCollection principals = null;
388         try {
389             byte[] bytes = getRememberedSerializedIdentity(subjectContext);
390             //SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
391             if (bytes !is null && bytes.length > 0) {
392                 principals = convertBytesToPrincipals(bytes, subjectContext);
393             }
394         } catch (RuntimeException re) {
395             principals = onRememberedPrincipalFailure(re, subjectContext);
396         }
397 
398         return principals;
399     }
400 
401     /**
402      * Based on the given subject context data, retrieves the previously persisted serialized identity, or
403      * {@code null} if there is no available data.  The context map is usually populated by a {@link Subject.Builder}
404      * implementation.  See the {@link SubjectFactory} class constants for Shiro's known map keys.
405      *
406      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
407      *                       is being used to construct a {@link Subject} instance.  To be used to assist with data
408      *                       lookup.
409      * @return the previously persisted serialized identity, or {@code null} if there is no available data for the
410      *         Subject.
411      */
412     protected abstract byte[] getRememberedSerializedIdentity(SubjectContext subjectContext);
413 
414     /**
415      * If a {@link #getCipherService() cipherService} is available, it will be used to first decrypt the byte array.
416      * Then the bytes are then {@link #deserialize(byte[]) deserialized} and then returned.
417      *
418      * @param bytes          the bytes to decrypt if necessary and then deserialize.
419      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
420      *                       is being used to construct a {@link Subject} instance.
421      * @return the de-serialized and possibly decrypted principals
422      */
423     protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
424         if (getCipherService() !is null) {
425             bytes = decrypt(bytes);
426         }
427         return deserialize(bytes);
428     }
429 
430     /**
431      * Called when an exception is thrown while trying to retrieve principals.  The default implementation logs a
432      * warning message and forgets ('unremembers') the problem identity by calling
433      * {@link #forgetIdentity(SubjectContext) forgetIdentity(context)} and then immediately re-throws the
434      * exception to allow the calling component to react accordingly.
435      * <p/>
436      * This method implementation never returns an
437      * object - it always rethrows, but can be overridden by subclasses for custom handling behavior.
438      * <p/>
439      * This most commonly would be called when an encryption key is updated and old principals are retrieved that have
440      * been encrypted with the previous key.
441      *
442      * @param e       the exception that was thrown.
443      * @param context the contextual data, usually provided by a {@link Subject.Builder} implementation, that
444      *                is being used to construct a {@link Subject} instance.
445      * @return nothing - the original {@code RuntimeException} is propagated in all cases.
446      */
447     protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context) {
448 
449         version(HUNT_DEBUG) {
450             string message = "There was a failure while trying to retrieve remembered principals.  This could be due to a " ~
451                     "configuration problem or corrupted principals.  This could also be due to a recently " ~
452                     "changed encryption key, if you are using a shiro.ini file, this property would be " ~
453                     "'securityManager.rememberMeManager.cipherKey' see: http://shiro.apache.org/web.html#Web-RememberMeServices. " ~
454                     "The remembered identity will be forgotten and not used for this request.";
455             warning(message);
456         }
457         forgetIdentity(context);
458         //propagate - security manager implementation will handle and warn appropriately
459         throw e;
460     }
461 
462     /**
463      * Encrypts the byte array by using the configured {@link #getCipherService() cipherService}.
464      *
465      * @param serialized the serialized object byte array to be encrypted
466      * @return an encrypted byte array returned by the configured {@link #getCipherService () cipher}.
467      */
468     protected byte[] encrypt(byte[] serialized) {
469         byte[] value = serialized;
470         CipherService cipherService = getCipherService();
471         if (cipherService !is null) {
472             ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());
473             value = byteSource.getBytes();
474         }
475         return value;
476     }
477 
478     /**
479      * Decrypts the byte array using the configured {@link #getCipherService() cipherService}.
480      *
481      * @param encrypted the encrypted byte array to decrypt
482      * @return the decrypted byte array returned by the configured {@link #getCipherService () cipher}.
483      */
484     protected byte[] decrypt(byte[] encrypted) {
485         byte[] serialized = encrypted;
486         CipherService cipherService = getCipherService();
487         if (cipherService !is null) {
488             ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());
489             serialized = byteSource.getBytes();
490         }
491         return serialized;
492     }
493 
494     /**
495      * Serializes the given {@code principals} by serializing them to a byte array by using the
496      * {@link #getSerializer() serializer}'s {@link Serializer#serialize(Object) serialize} method.
497      *
498      * @param principals the principal collection to serialize to a byte array
499      * @return the serialized principal collection in the form of a byte array
500      */
501     protected byte[] serialize(PrincipalCollection principals) {
502         // return getSerializer().serialize(principals);
503         implementationMissing(false);
504         return null;
505     }
506 
507     /**
508      * De-serializes the given byte array by using the {@link #getSerializer() serializer}'s
509      * {@link Serializer#deserialize deserialize} method.
510      *
511      * @param serializedIdentity the previously serialized {@code PrincipalCollection} as a byte array
512      * @return the de-serialized (reconstituted) {@code PrincipalCollection}
513      */
514     protected PrincipalCollection deserialize(byte[] serializedIdentity) {
515         // return getSerializer().deserialize(serializedIdentity);
516         
517         implementationMissing(false);
518         return null;
519     }
520 
521     /**
522      * Reacts to a failed login by immediately {@link #forgetIdentity(hunt.shiro.subject.Subject) forgetting} any
523      * previously remembered identity.  This is an additional security feature to prevent any remenant identity data
524      * from being retained in case the authentication attempt is not being executed by the expected user.
525      *
526      * @param subject the subject which executed the failed login attempt
527      * @param token   the authentication token resulting in a failed login attempt - ignored by this implementation
528      * @param ae      the exception thrown as a result of the failed login attempt - ignored by this implementation
529      */
530      void onFailedLogin(Subject subject, AuthenticationToken token, AuthenticationException ae) {
531         forgetIdentity(subject);
532     }
533 
534     /**
535      * Reacts to a subject logging out of the application and immediately
536      * {@link #forgetIdentity(hunt.shiro.subject.Subject) forgets} any previously stored identity and returns.
537      *
538      * @param subject the subject logging out.
539      */
540      void onLogout(Subject subject) {
541         forgetIdentity(subject);
542     }
543 }