extensions/net.sf.basedb.otp/trunk/src/net/sf/basedb/otp/OtpAuthenticationManager.java

Code
Comments
Other
Rev Date Author Line
4847 13 Jun 18 nicklas 1 package net.sf.basedb.otp;
4847 13 Jun 18 nicklas 2
4853 15 Jun 18 nicklas 3 import java.security.GeneralSecurityException;
4853 15 Jun 18 nicklas 4
4847 13 Jun 18 nicklas 5 import com.warrenstrange.googleauth.GoogleAuthenticator;
4847 13 Jun 18 nicklas 6
4847 13 Jun 18 nicklas 7 import net.sf.basedb.core.AuthenticationContext;
4847 13 Jun 18 nicklas 8 import net.sf.basedb.core.authentication.AuthenticatedUser;
4847 13 Jun 18 nicklas 9 import net.sf.basedb.core.authentication.AuthenticationManager;
4919 08 Aug 18 nicklas 10 import net.sf.basedb.core.authentication.AuthenticationMethod;
4853 15 Jun 18 nicklas 11 import net.sf.basedb.core.authentication.LoginException;
4847 13 Jun 18 nicklas 12 import net.sf.basedb.core.authentication.LoginRequest;
4847 13 Jun 18 nicklas 13 import net.sf.basedb.core.data.UserData;
4847 13 Jun 18 nicklas 14 import net.sf.basedb.util.Values;
4847 13 Jun 18 nicklas 15
4847 13 Jun 18 nicklas 16 /**
4921 09 Aug 18 nicklas 17   Authentication manager for one-time-passwords.
4921 09 Aug 18 nicklas 18   The 'login' should be the regular login for user accounts.
4921 09 Aug 18 nicklas 19   
4921 09 Aug 18 nicklas 20   If the login-method is otp-only, the 'password' should be the OTP generated by the users device.
4921 09 Aug 18 nicklas 21   
4921 09 Aug 18 nicklas 22   If the login-method is otp-password, the 'password' should be the regular password
4921 09 Aug 18 nicklas 23   and the 'extraValue' attribute should be the OTP generated by the users device.
4921 09 Aug 18 nicklas 24   
4847 13 Jun 18 nicklas 25   @author nicklas
4847 13 Jun 18 nicklas 26   @since 1.0
4847 13 Jun 18 nicklas 27 */
4847 13 Jun 18 nicklas 28 public class OtpAuthenticationManager 
4847 13 Jun 18 nicklas 29   implements AuthenticationManager 
4847 13 Jun 18 nicklas 30 {
4847 13 Jun 18 nicklas 31
4847 13 Jun 18 nicklas 32   private final AuthenticationContext context;
4847 13 Jun 18 nicklas 33
4847 13 Jun 18 nicklas 34   public OtpAuthenticationManager(AuthenticationContext context)
4847 13 Jun 18 nicklas 35   {
4847 13 Jun 18 nicklas 36     this.context = context;
4847 13 Jun 18 nicklas 37   }
4847 13 Jun 18 nicklas 38   
4847 13 Jun 18 nicklas 39   @Override
4847 13 Jun 18 nicklas 40   public AuthenticatedUser authenticate() 
4847 13 Jun 18 nicklas 41   {
4847 13 Jun 18 nicklas 42     String clientId = context.getSessionControl().getExternalClientId();
4848 13 Jun 18 nicklas 43     boolean otpIsRequired = Otp.isOtpRequiredForClient(clientId);
4919 08 Aug 18 nicklas 44     AuthenticationMethod authMethod = Otp.getAuthenticationMethod();
4847 13 Jun 18 nicklas 45
4847 13 Jun 18 nicklas 46     LoginRequest request = context.getLoginRequest();
4847 13 Jun 18 nicklas 47     String login = request.getLogin();
5147 26 Nov 18 nicklas 48     String loginForm = request.getAttribute("login-form");
5151 28 Nov 18 nicklas 49     boolean isOtpForm = loginForm != null && loginForm.startsWith("net.sf.basedb.otp");
5151 28 Nov 18 nicklas 50     if (loginForm != null && !isOtpForm)
5147 26 Nov 18 nicklas 51     {
5151 28 Nov 18 nicklas 52       // The login is made with a different login manager...
5151 28 Nov 18 nicklas 53       if (otpIsRequired)
5151 28 Nov 18 nicklas 54       {
5151 28 Nov 18 nicklas 55         // If this client require YubiKey we do not allow the login
5151 28 Nov 18 nicklas 56         throw new LoginException("OTP login is required for using this client (" + clientId + ").");
5151 28 Nov 18 nicklas 57       }
5151 28 Nov 18 nicklas 58
5151 28 Nov 18 nicklas 59       // Return null to let internal authentication handle it
5147 26 Nov 18 nicklas 60       return null;
5147 26 Nov 18 nicklas 61     }
4847 13 Jun 18 nicklas 62
4919 08 Aug 18 nicklas 63     UserData user = null;
4919 08 Aug 18 nicklas 64     String otp = null;
4919 08 Aug 18 nicklas 65     AuthenticatedUser auth = null;
4919 08 Aug 18 nicklas 66     if (authMethod == Otp.OTP_PASSWORD_AUTHENTICATION_METHOD)
4919 08 Aug 18 nicklas 67     {
4919 08 Aug 18 nicklas 68       auth = context.verifyUserInternal(request);
4919 08 Aug 18 nicklas 69       user = context.getUserById(auth.getInternalId());
4919 08 Aug 18 nicklas 70       otp = request.getAttribute("extraValue");
4919 08 Aug 18 nicklas 71     }
4919 08 Aug 18 nicklas 72     else
4919 08 Aug 18 nicklas 73     {
4919 08 Aug 18 nicklas 74       user = context.getUserByLogin(login);
4919 08 Aug 18 nicklas 75       otp = request.getPassword();
4919 08 Aug 18 nicklas 76     }
4847 13 Jun 18 nicklas 77     if (user == null)
4847 13 Jun 18 nicklas 78     {
4851 14 Jun 18 nicklas 79       throw new LoginException("Unknown username '" + login + "'.");
4848 13 Jun 18 nicklas 80     }
4848 13 Jun 18 nicklas 81     
4853 15 Jun 18 nicklas 82     String otpKeyEncrypted = (String)user.getExtended("otpSecretKey");
4858 18 Jun 18 nicklas 83     otpIsRequired |= Boolean.TRUE.equals(user.getExtended("otpIsRequired"));
4947 27 Aug 18 nicklas 84     // Multi-user account can't use OTP so we dp not enforce it even if other settings say something else
4947 27 Aug 18 nicklas 85     if (user.isMultiuserAccount()) otpIsRequired = false; 
4853 15 Jun 18 nicklas 86     if (otpKeyEncrypted == null)
4848 13 Jun 18 nicklas 87     {
4858 18 Jun 18 nicklas 88       // This user has not configured OTP...
5151 28 Nov 18 nicklas 89       if (otpIsRequired || isOtpForm)
4848 13 Jun 18 nicklas 90       {
4858 18 Jun 18 nicklas 91         // ...but the client app (or admin) requires it... 
5157 30 Nov 18 nicklas 92         throw new LoginException("User '" + login + "' has not configured OTP.");
4848 13 Jun 18 nicklas 93       }
4847 13 Jun 18 nicklas 94     }
4919 08 Aug 18 nicklas 95     else
4847 13 Jun 18 nicklas 96     {
4919 08 Aug 18 nicklas 97       if (otp == null || !otp.matches("\\d{6}"))
4919 08 Aug 18 nicklas 98       {
4919 08 Aug 18 nicklas 99         // It seems like the user did not enter a 6-digit code
4921 09 Aug 18 nicklas 100         throw new LoginException("User '" + login + "' must login with OTP (6 digits).");
4919 08 Aug 18 nicklas 101       }
4919 08 Aug 18 nicklas 102       
4919 08 Aug 18 nicklas 103       String otpKey;
4919 08 Aug 18 nicklas 104       try
4919 08 Aug 18 nicklas 105       {
4919 08 Aug 18 nicklas 106         otpKey = CryptUtil.decrypt(otpKeyEncrypted, user.getId());
4919 08 Aug 18 nicklas 107       }
4919 08 Aug 18 nicklas 108       catch (GeneralSecurityException ex)
4919 08 Aug 18 nicklas 109       {
4919 08 Aug 18 nicklas 110         throw new LoginException(ex.getMessage(), ex);
4919 08 Aug 18 nicklas 111       }
4919 08 Aug 18 nicklas 112       GoogleAuthenticator ga = new GoogleAuthenticator();
4919 08 Aug 18 nicklas 113       if (!ga.authorize(otpKey, Values.getInt(otp)))
4919 08 Aug 18 nicklas 114       {
4919 08 Aug 18 nicklas 115         throw new LoginException("Invalid one-time passcode.");
4919 08 Aug 18 nicklas 116       }
4919 08 Aug 18 nicklas 117       
4919 08 Aug 18 nicklas 118       if (!UsedOtpCodes.INSTANCE.useCode(user.getId(), otp))
4919 08 Aug 18 nicklas 119       {
4919 08 Aug 18 nicklas 120         throw new LoginException("The one-time passcode has already been used.");
4919 08 Aug 18 nicklas 121       }
4919 08 Aug 18 nicklas 122       auth = new AuthenticatedUser(authMethod, user);
4847 13 Jun 18 nicklas 123     }
4847 13 Jun 18 nicklas 124     return auth;
4847 13 Jun 18 nicklas 125   }
4847 13 Jun 18 nicklas 126
5157 30 Nov 18 nicklas 127   /**
5157 30 Nov 18 nicklas 128     If the authenticated user has OTP configured we do not allow
5157 30 Nov 18 nicklas 129     other authentication methods.
5157 30 Nov 18 nicklas 130   */
5157 30 Nov 18 nicklas 131   @Override
5157 30 Nov 18 nicklas 132   public void vetoAuthenticatedUser(UserData user, AuthenticatedUser auth) 
5157 30 Nov 18 nicklas 133   {
5157 30 Nov 18 nicklas 134     String otpKeyEncrypted = (String)user.getExtended("otpSecretKey");
5157 30 Nov 18 nicklas 135     boolean otpIsRequried = Boolean.TRUE.equals(user.getExtended("otpIsRequired"));
5157 30 Nov 18 nicklas 136     if (otpKeyEncrypted != null || otpIsRequried)
5157 30 Nov 18 nicklas 137     {
5157 30 Nov 18 nicklas 138       if (!Otp.isAuthenticationMethodAllowed(auth.getAuthenticationMethod()))
5157 30 Nov 18 nicklas 139       {
5157 30 Nov 18 nicklas 140         throw new LoginException("User '" + user.getLogin() + "' must login with OTP!");
5157 30 Nov 18 nicklas 141       }
5157 30 Nov 18 nicklas 142     }
5157 30 Nov 18 nicklas 143   }
4847 13 Jun 18 nicklas 144
4847 13 Jun 18 nicklas 145 }