/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.shiro.authc.credential;
import org.apache.shiro.util.ByteSource;
/**
* A {@code PasswordService} supports common use cases when using passwords as a credentials mechanism.
* <p/>
* Most importantly, implementations of this interface are expected to employ best-practices to ensure that
* passwords remain as safe as possible in application environments.
* <h2>Usage</h2>
* A {@code PasswordService} is used at two different times during an application's lifecycle:
* <ul>
* <li>When creating a user account or resetting their password</li>
* <li>When a user logs in, when passwords must be compared</li>
* </ul>
* <h3>Account Creation or Password Reset</h3>
* Whenever you create a new user account or reset that account's password, we must translate the end-user submitted
* raw/plaintext password value to a string format that is much safer to store. You do that by calling the
* {@link #encryptPassword(Object)} method to create the safer value. For
* example:
* <pre>
* String submittedPlaintextPassword = ...
* String encryptedValue = passwordService.encryptPassword(submittedPlaintextPassword);
* ...
* userAccount.setPassword(encryptedValue);
* userAccount.save(); //create or update to your data store
* </pre>
* Be sure to save this encrypted password in your data store and never the original/raw submitted password.
* <h3>Login Password Comparison</h3>
* Shiro performs the comparison during login automatically. Along with your {@code PasswordService}, you just
* have to configure a {@link PasswordMatcher} on a realm that has password-based accounts. During a login attempt,
* shiro will use the {@code PasswordMatcher} and the {@code PasswordService} to automatically compare submitted
* passwords.
* <p/>
* For example, if using Shiro's INI, here is how you might configure the PasswordMatcher and PasswordService:
* <pre>
* [main]
* ...
* passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
* # configure the passwordService to use the settings you desire
* ...
* passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
* passwordMatcher.passwordService = $passwordService
* ...
* # Finally, set the matcher on a realm that requires password matching for account authentication:
* myRealm = ...
* myRealm.credentialsMatcher = $passwordMatcher
* </pre>
*
* @see DefaultPasswordService
* @see PasswordMatcher
* @since 1.2
*/
public interface PasswordService {
/**
* Converts the specified plaintext password (usually acquired from your application's 'new user' or 'password reset'
* workflow) into a formatted string safe for storage. The returned string can be safely saved with the
* corresponding user account record (e.g. as a 'password' attribute).
* <p/>
* It is expected that the String returned from this method will be presented to the
* {@link #passwordsMatch(Object, String) passwordsMatch(plaintext,encrypted)} method when performing a
* password comparison check.
* <h3>Usage</h3>
* The input argument type can be any 'byte backed' {@code Object} - almost always either a
* String or character array representing passwords (character arrays are often a safer way to represent passwords
* as they can be cleared/nulled-out after use. Any argument type supported by
* {@link ByteSource.Util#isCompatible(Object)} is valid.
* <p/>
* For example:
* <pre>
* String rawPassword = ...
* String encryptedValue = passwordService.encryptPassword(rawPassword);
* </pre>
* or, identically:
* <pre>
* char[] rawPasswordChars = ...
* String encryptedValue = passwordService.encryptPassword(rawPasswordChars);
* </pre>
* <p/>
* The resulting {@code encryptedValue} should be stored with the account to be retrieved later during a
* login attempt. For example:
* <pre>
* String encryptedValue = passwordService.encryptPassword(rawPassword);
* ...
* userAccount.setPassword(encryptedValue);
* userAccount.save(); //create or update to your data store
* </pre>
*
* @param plaintextPassword the raw password as 'byte-backed' object (String, character array, {@link ByteSource},
* etc) usually acquired from your application's 'new user' or 'password reset' workflow.
* @return the encrypted password, formatted for storage.
* @throws IllegalArgumentException if the argument cannot be easily converted to bytes as defined by
* {@link ByteSource.Util#isCompatible(Object)}.
* @see ByteSource.Util#isCompatible(Object)
*/
String encryptPassword(Object plaintextPassword) throws IllegalArgumentException;
/**
* Returns {@code true} if the {@code submittedPlaintext} password matches the existing {@code saved} password,
* {@code false} otherwise.
* <h3>Usage</h3>
* The {@code submittedPlaintext} argument type can be any 'byte backed' {@code Object} - almost always either a
* String or character array representing passwords (character arrays are often a safer way to represent passwords
* as they can be cleared/nulled-out after use. Any argument type supported by
* {@link ByteSource.Util#isCompatible(Object)} is valid.
* <p/>
* For example:
* <pre>
* String submittedPassword = ...
* passwordService.passwordsMatch(submittedPassword, encryptedPassword);
* </pre>
* or similarly:
* <pre>
* char[] submittedPasswordCharacters = ...
* passwordService.passwordsMatch(submittedPasswordCharacters, encryptedPassword);
* </pre>
*
* @param submittedPlaintext a raw/plaintext password submitted by an end user/Subject.
* @param encrypted the previously encrypted password known to be associated with an account.
* This value is expected to have been previously generated from the
* {@link #encryptPassword(Object) encryptPassword} method (typically
* when the account is created or the account's password is reset).
* @return {@code true} if the {@code submittedPlaintext} password matches the existing {@code saved} password,
* {@code false} otherwise.
* @see ByteSource.Util#isCompatible(Object)
*/
boolean passwordsMatch(Object submittedPlaintext, String encrypted);
}