diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/AbstractPhoneFormAuthenticator.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/AbstractPhoneFormAuthenticator.java new file mode 100755 index 00000000..8f8c2051 --- /dev/null +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/AbstractPhoneFormAuthenticator.java @@ -0,0 +1,117 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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.sunbird.keycloak.login.phone; + +import org.jboss.logging.Logger; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.AuthenticationFlowError; +import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator; +import org.keycloak.events.Details; +import org.keycloak.events.Errors; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.services.ServicesLogger; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.messages.Messages; +import org.sunbird.keycloak.resetcredential.sms.KeycloakSmsAuthenticatorConstants; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import java.util.List; + +public abstract class AbstractPhoneFormAuthenticator extends AbstractUsernameFormAuthenticator { + + private static final Logger logger = Logger.getLogger(AbstractPhoneFormAuthenticator.class); + + public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap inputData) { + String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME); + logger.debug("AbstractPhoneFormAuthenticator@validateUserAndPassword - Username -" + username); + + if (username == null) { + context.getEvent().error(Errors.USER_NOT_FOUND); + Response challengeResponse = invalidUser(context); + context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse); + return false; + } + + // remove leading and trailing whitespace + username = username.trim(); + + context.getEvent().detail(Details.USERNAME, username); + context.getAuthenticationSession().setAuthNote(AbstractPhoneFormAuthenticator.ATTEMPTED_USERNAME, username); + + UserModel user = null; + try { + user = getUser(context, username); + } catch (ModelDuplicateException mde) { + ServicesLogger.LOGGER.modelDuplicateException(mde); + + // Could happen during federation import + if (mde.getDuplicateFieldName() != null && mde.getDuplicateFieldName().equals(UserModel.EMAIL)) { + setDuplicateUserChallenge(context, Errors.EMAIL_IN_USE, Messages.EMAIL_EXISTS, AuthenticationFlowError.INVALID_USER); + } else { + setDuplicateUserChallenge(context, Errors.USERNAME_IN_USE, Messages.USERNAME_EXISTS, AuthenticationFlowError.INVALID_USER); + } + + return false; + } + + if (invalidUser(context, user)) { + return false; + } + + if (!validatePassword(context, user, inputData)) { + return false; + } + + if (!enabledUser(context, user)) { + return false; + } + + String rememberMe = inputData.getFirst("rememberMe"); + boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on"); + if (remember) { + context.getAuthenticationSession().setAuthNote(Details.REMEMBER_ME, "true"); + context.getEvent().detail(Details.REMEMBER_ME, "true"); + } else { + context.getAuthenticationSession().removeAuthNote(Details.REMEMBER_ME); + } + context.setUser(user); + return true; + } + + private UserModel getUser(AuthenticationFlowContext context, String username) { + String numberRegex = "\\d+"; + KeycloakSession session = context.getSession(); + + if (username.matches(numberRegex)) { + List userModels = session.users().searchForUserByUserAttribute(KeycloakSmsAuthenticatorConstants.ATTR_MOBILE, username, context.getRealm()); + + if (userModels != null && userModels.size() > 0) { + return userModels.get(0); + } else { + return KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username); + } + } else { + return KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username); + } + } + +} diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/PhonePasswordForm.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/PhonePasswordForm.java new file mode 100755 index 00000000..2179e80d --- /dev/null +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/PhonePasswordForm.java @@ -0,0 +1,112 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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.sunbird.keycloak.login.phone; + +import org.jboss.logging.Logger; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.AuthenticationProcessor; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator; +import org.keycloak.forms.login.LoginFormsProvider; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.services.ServicesLogger; +import org.keycloak.services.managers.AuthenticationManager; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PhonePasswordForm extends AbstractPhoneFormAuthenticator implements Authenticator { + protected static ServicesLogger log = ServicesLogger.LOGGER; + private static final Logger logger = Logger.getLogger(PhonePasswordForm.class); + + @Override + public void action(AuthenticationFlowContext context) { + logger.debug("PhonePasswordForm@action - called"); + MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters(); + if (formData.containsKey("cancel")) { + context.cancelLogin(); + return; + } + if (!validateForm(context, formData)) { + return; + } + context.success(); + } + + protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap formData) { + logger.debug("PhonePasswordForm@validateForm - called"); + return validateUserAndPassword(context, formData); + } + + @Override + public void authenticate(AuthenticationFlowContext context) { + MultivaluedMap formData = new MultivaluedMapImpl<>(); + String loginHint = context.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM); + + String rememberMeUsername = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders()); + + if (loginHint != null || rememberMeUsername != null) { + if (loginHint != null) { + formData.add(AuthenticationManager.FORM_USERNAME, loginHint); + } else { + formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername); + formData.add("rememberMe", "on"); + } + } + Response challengeResponse = challenge(context, formData); + context.challenge(challengeResponse); + } + + @Override + public boolean requiresUser() { + return false; + } + + protected Response challenge(AuthenticationFlowContext context, MultivaluedMap formData) { + LoginFormsProvider forms = context.form(); + + if (formData.size() > 0) forms.setFormData(formData); + + return forms.createLogin(); + } + + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + // never called + return true; + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + // never called + } + + @Override + public void close() { + + } +} diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/PhonePasswordFormFactory.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/PhonePasswordFormFactory.java new file mode 100755 index 00000000..7eaed729 --- /dev/null +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/login/phone/PhonePasswordFormFactory.java @@ -0,0 +1,108 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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.sunbird.keycloak.login.phone; + +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PhonePasswordFormFactory implements AuthenticatorFactory { + + private static final Logger logger = Logger.getLogger(PhonePasswordFormFactory.class); + + public static final String PROVIDER_ID = "auth-phone-password-form"; + public static final PhonePasswordForm SINGLETON = new PhonePasswordForm(); + + @Override + public Authenticator create(KeycloakSession session) { + logger.debug("PhonePasswordFormFactory@create - PhonePasswordFormFactory is created"); + return SINGLETON; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public String getReferenceCategory() { + return UserCredentialModel.PASSWORD; + } + + @Override + public boolean isConfigurable() { + return false; + } + public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { + AuthenticationExecutionModel.Requirement.REQUIRED + }; + + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public String getDisplayType() { + return "Phone Password Form"; + } + + @Override + public String getHelpText() { + return "Validates a phone and password from login form."; + } + + @Override + public List getConfigProperties() { + return null; + } + + @Override + public boolean isUserSetupAllowed() { + return false; + } + +} diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticator.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticator.java similarity index 99% rename from keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticator.java rename to keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticator.java index 947bf6de..cabfea70 100644 --- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticator.java +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticator.java @@ -1,4 +1,4 @@ -package org.sunbird.keycloak; +package org.sunbird.keycloak.resetcredential.sms; import org.apache.http.util.TextUtils; import org.jboss.logging.Logger; diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorConstants.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorConstants.java similarity index 95% rename from keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorConstants.java rename to keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorConstants.java index 916b0039..ff6e7cf8 100644 --- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorConstants.java +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorConstants.java @@ -1,4 +1,4 @@ -package org.sunbird.keycloak; +package org.sunbird.keycloak.resetcredential.sms; /** * Created by joris on 18/11/2016. diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorCredentialProvider.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorCredentialProvider.java similarity index 99% rename from keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorCredentialProvider.java rename to keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorCredentialProvider.java index a0a15ec7..a414ddba 100644 --- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorCredentialProvider.java +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorCredentialProvider.java @@ -1,4 +1,4 @@ -package org.sunbird.keycloak; +package org.sunbird.keycloak.resetcredential.sms; import org.jboss.logging.Logger; import org.keycloak.common.util.Time; diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorCredentialProviderFactory.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorCredentialProviderFactory.java similarity index 94% rename from keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorCredentialProviderFactory.java rename to keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorCredentialProviderFactory.java index 6288b0e7..67cc5d59 100644 --- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorCredentialProviderFactory.java +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorCredentialProviderFactory.java @@ -1,4 +1,4 @@ -package org.sunbird.keycloak; +package org.sunbird.keycloak.resetcredential.sms; import org.jboss.logging.Logger; import org.keycloak.credential.CredentialProvider; diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorFactory.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorFactory.java similarity index 98% rename from keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorFactory.java rename to keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorFactory.java index 34d935fa..6868ae90 100644 --- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorFactory.java +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorFactory.java @@ -1,4 +1,4 @@ -package org.sunbird.keycloak; +package org.sunbird.keycloak.resetcredential.sms; import org.jboss.logging.Logger; import org.keycloak.Config; diff --git a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorUtil.java b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorUtil.java similarity index 99% rename from keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorUtil.java rename to keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorUtil.java index 2480cc37..0db6c74f 100644 --- a/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/KeycloakSmsAuthenticatorUtil.java +++ b/keycloak/sms-provider/src/main/java/org/sunbird/keycloak/resetcredential/sms/KeycloakSmsAuthenticatorUtil.java @@ -1,4 +1,4 @@ -package org.sunbird.keycloak; +package org.sunbird.keycloak.resetcredential.sms; import com.amazonaws.util.StringUtils; import org.jboss.logging.Logger; diff --git a/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 500f32fa..f820d6e0 100755 --- a/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -1 +1,2 @@ -org.sunbird.keycloak.KeycloakSmsAuthenticatorFactory +org.sunbird.keycloak.resetcredential.sms.KeycloakSmsAuthenticatorFactory +org.sunbird.keycloak.login.phone.PhonePasswordFormFactory diff --git a/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory b/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory index edd6c136..fd4daa97 100644 --- a/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory +++ b/keycloak/sms-provider/src/main/resources/META-INF/services/org.keycloak.credential.CredentialProviderFactory @@ -1 +1 @@ -org.sunbird.keycloak.KeycloakSmsAuthenticatorCredentialProviderFactory +org.sunbird.keycloak.resetcredential.sms.KeycloakSmsAuthenticatorCredentialProviderFactory