diff --git a/commons/src/main/java/org/eclipse/kapua/commons/logging/LoggingMdcKeys.java b/commons/src/main/java/org/eclipse/kapua/commons/logging/LoggingMdcKeys.java new file mode 100644 index 00000000000..bce95fd90fe --- /dev/null +++ b/commons/src/main/java/org/eclipse/kapua/commons/logging/LoggingMdcKeys.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2021 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech - initial API and implementation + *******************************************************************************/ +package org.eclipse.kapua.commons.logging; + +import org.eclipse.kapua.service.account.Account; +import org.eclipse.kapua.service.user.User; +import org.slf4j.MDC; + +/** + * Logging {@link MDC} keys. + * + * @see MDC + * @since 1.6.0 + */ +public class LoggingMdcKeys { + + /** + * Constructor. + * + * @since 1.6.0 + */ + private LoggingMdcKeys() { + } + + /** + * The {@link Account#getId()}. + * + * @since 1.0.0 + */ + public static final String SCOPE_ID = "scopeId"; + + /** + * The {@link Account#getName()}. + * + * @since 1.0.0 + */ + public static final String ACCOUNT_NAME = "scopeName"; + + /** + * The {@link User#getId()} + * + * @since 1.0.0 + */ + public static final String USER_ID = "userId"; + + /** + * The {@link User#getName()} ()} + * + * @since 1.6.0 + */ + public static final String USER_NAME = "userName"; +} diff --git a/commons/src/main/java/org/eclipse/kapua/commons/security/KapuaSecurityUtils.java b/commons/src/main/java/org/eclipse/kapua/commons/security/KapuaSecurityUtils.java index 6ff1e9d3d29..a9f98dc7819 100644 --- a/commons/src/main/java/org/eclipse/kapua/commons/security/KapuaSecurityUtils.java +++ b/commons/src/main/java/org/eclipse/kapua/commons/security/KapuaSecurityUtils.java @@ -13,24 +13,22 @@ *******************************************************************************/ package org.eclipse.kapua.commons.security; -import java.util.concurrent.Callable; - import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.commons.util.ThrowingRunnable; import org.eclipse.kapua.model.id.KapuaId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.Callable; + /** - * Kapua security utility to handle the bind/unbind operation of the Kapua session into the thread context. + * Security utilities to handle the {@link KapuaSession}. * - * @since 1.0 + * @since 1.0.0 */ public class KapuaSecurityUtils { - private static final Logger logger = LoggerFactory.getLogger(KapuaSecurityUtils.class); - - public static final String MDC_USER_ID = "userId"; + private static final Logger LOG = LoggerFactory.getLogger(KapuaSecurityUtils.class); private static final ThreadLocal THREAD_SESSION = new ThreadLocal<>(); @@ -38,47 +36,47 @@ private KapuaSecurityUtils() { } /** - * Return the {@link KapuaSession} associated to the current thread session. + * Returns the {@link KapuaSession} associated to the current {@link ThreadLocal}. * - * @return + * @return The {@link KapuaSession} associated to the current {@link ThreadLocal}. + * @since 1.0.0 */ public static KapuaSession getSession() { return THREAD_SESSION.get(); } /** - * Bound the {@link KapuaSession} to the current thread session. + * Bounds the {@link KapuaSession} to the current {@link ThreadLocal}. * - * @param session + * @param session The {@link KapuaSession} to the current {@link ThreadLocal}. + * @since 1.0.0 */ public static void setSession(KapuaSession session) { THREAD_SESSION.set(session); } /** - * Clear the {@link KapuaSession} from the current thread session. + * Clears the {@link KapuaSession} from the current {@link ThreadLocal}. + * + * @since 1.0.0 */ public static void clearSession() { THREAD_SESSION.remove(); } /** - * Execute the {@link Runnable} in a privileged context.
- * Trusted mode means that checks for permissions and role will pass. + * Executes the {@link Runnable} in a privileged context. + *

+ * Trusted mode means that checks for permissions and role will be skipped. * - * @param runnable - * The {@link ThrowingRunnable} action to be executed. + * @param runnable The {@link ThrowingRunnable} action to be executed. * @throws KapuaException + * @since 1.0.0 */ public static void doPrivileged(final ThrowingRunnable runnable) throws KapuaException { - doPrivileged(new Callable() { - - @Override - public Void call() throws Exception { - runnable.run(); - return null; - } - + doPrivileged((Callable) () -> { + runnable.run(); + return null; }); } @@ -86,8 +84,7 @@ public Void call() throws Exception { * Execute the {@link Callable} in a privileged context.
* Trusted mode means that checks for permissions and role will pass. * - * @param privilegedAction - * The {@link Callable} action to be executed. + * @param privilegedAction The {@link Callable} action to be executed. * @return The result of the {@link Callable} action. * @throws KapuaException * @since 1.0.0 @@ -95,15 +92,23 @@ public Void call() throws Exception { public static T doPrivileged(Callable privilegedAction) throws KapuaException { // get (and keep) the current session KapuaSession previousSession = getSession(); - KapuaSession currentSession = null; + KapuaSession currentSession; if (previousSession == null) { - logger.debug("==> create new session"); currentSession = new KapuaSession(null, KapuaId.ONE, KapuaId.ONE); currentSession.setTrustedMode(true); + LOG.debug("Created a new KapuaSession as ScopeId: {} - UserId: {} - Trusted: {} - Token: {}", + currentSession.getScopeId(), + currentSession.getUserId(), + currentSession.isTrustedMode(), + currentSession.getAccessToken() != null ? currentSession.getAccessToken().getTokenId() : null); } else { - logger.debug("==> clone from previous session"); currentSession = KapuaSession.createFrom(); + LOG.debug("Cloning KapuaSession as ScopeId: {} - UserId: {} - Trusted: {} - Token: {}", + currentSession.getScopeId(), + currentSession.getUserId(), + currentSession.isTrustedMode(), + currentSession.getAccessToken() != null ? currentSession.getAccessToken().getTokenId() : null); } setSession(currentSession); @@ -114,7 +119,7 @@ public static T doPrivileged(Callable privilegedAction) throws KapuaExcep } catch (Exception e) { throw KapuaException.internalError(e); } finally { - // restore the original session + // Restore the original session. setSession(previousSession); } } diff --git a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/AuthenticationServiceShiroImpl.java b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/AuthenticationServiceShiroImpl.java index e6cd9c49b25..17b188bc25d 100644 --- a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/AuthenticationServiceShiroImpl.java +++ b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/AuthenticationServiceShiroImpl.java @@ -28,6 +28,7 @@ import org.eclipse.kapua.KapuaEntityNotFoundException; import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.KapuaRuntimeException; +import org.eclipse.kapua.commons.logging.LoggingMdcKeys; import org.eclipse.kapua.commons.model.id.KapuaEid; import org.eclipse.kapua.commons.security.KapuaSecurityUtils; import org.eclipse.kapua.commons.security.KapuaSession; @@ -52,6 +53,7 @@ import org.eclipse.kapua.service.authentication.credential.mfa.MfaOption; import org.eclipse.kapua.service.authentication.credential.mfa.MfaOptionService; import org.eclipse.kapua.service.authentication.shiro.exceptions.MfaRequiredException; +import org.eclipse.kapua.service.authentication.shiro.session.ShiroSessionKeys; import org.eclipse.kapua.service.authentication.shiro.setting.KapuaAuthenticationSetting; import org.eclipse.kapua.service.authentication.shiro.setting.KapuaAuthenticationSettingKeys; import org.eclipse.kapua.service.authentication.token.AccessToken; @@ -203,8 +205,6 @@ public AccessToken login(LoginCredentials loginCredentials, boolean enableTrust) accessToken.setTrustKey(trustKey); } - // Set some logging - MDC.put(KapuaSecurityUtils.MDC_USER_ID, accessToken.getUserId().toCompactId()); LOG.info("Login for thread '{}' - '{}' - '{}'", Thread.currentThread().getId(), Thread.currentThread().getName(), shiroSubject); } catch (ShiroException se) { @@ -256,7 +256,7 @@ public void authenticate(SessionCredentials sessionCredentials) throws KapuaExce // Set some logging Subject shiroSubject = SecurityUtils.getSubject(); - MDC.put(KapuaSecurityUtils.MDC_USER_ID, accessToken.getUserId().toCompactId()); + MDC.put(LoggingMdcKeys.USER_ID, accessToken.getUserId().toCompactId()); LOG.info("Login for thread '{}' - '{}' - '{}'", Thread.currentThread().getId(), Thread.currentThread().getName(), shiroSubject); } catch (ShiroException se) { @@ -523,11 +523,11 @@ private AccessToken createAccessToken(Session session) throws KapuaException { * @since 1.0 */ private AccessToken createAccessToken(KapuaEid scopeId, KapuaEid userId) throws KapuaException { - // Retrieve TTL access token KapuaAuthenticationSetting settings = KapuaAuthenticationSetting.getInstance(); long tokenTtl = settings.getLong(KapuaAuthenticationSettingKeys.AUTHENTICATION_TOKEN_EXPIRE_AFTER); long refreshTokenTtl = settings.getLong(KapuaAuthenticationSettingKeys.AUTHENTICATION_REFRESH_TOKEN_EXPIRE_AFTER); + // Generate token Date now = new Date(); String jwt = generateJwt(scopeId, userId, now, tokenTtl); @@ -539,6 +539,7 @@ private AccessToken createAccessToken(KapuaEid scopeId, KapuaEid userId) throws new Date(now.getTime() + tokenTtl), UUID.randomUUID().toString(), new Date(now.getTime() + refreshTokenTtl)); + AccessToken accessToken; try { accessToken = KapuaSecurityUtils.doPrivileged(() -> accessTokenService.create(accessTokenCreator)); @@ -552,7 +553,15 @@ private AccessToken createAccessToken(KapuaEid scopeId, KapuaEid userId) throws private void establishSession(Subject subject, AccessToken accessToken, String openIDidToken) { KapuaSession kapuaSession = new KapuaSession(accessToken, accessToken.getScopeId(), accessToken.getUserId(), openIDidToken); KapuaSecurityUtils.setSession(kapuaSession); - subject.getSession().setAttribute(KapuaSession.KAPUA_SESSION_KEY, kapuaSession); + + Session subjectSession = subject.getSession(); + subjectSession.setAttribute(KapuaSession.KAPUA_SESSION_KEY, kapuaSession); + + // Set some logging stuff + MDC.put(LoggingMdcKeys.SCOPE_ID, accessToken.getScopeId().toCompactId()); + MDC.put(LoggingMdcKeys.ACCOUNT_NAME, (String) subjectSession.getAttribute(ShiroSessionKeys.ACCOUNT_NAME)); + MDC.put(LoggingMdcKeys.USER_ID, accessToken.getUserId().toCompactId()); + MDC.put(LoggingMdcKeys.USER_NAME, (String) subjectSession.getAttribute(ShiroSessionKeys.USER_NAME)); } private String generateJwt(KapuaEid scopeId, KapuaEid userId, Date now, long ttl) { diff --git a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/ApiKeyAuthenticatingRealm.java b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/ApiKeyAuthenticatingRealm.java index 71eab0d109b..0e4f0c1f9e4 100644 --- a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/ApiKeyAuthenticatingRealm.java +++ b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/ApiKeyAuthenticatingRealm.java @@ -17,30 +17,20 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.DisabledAccountException; -import org.apache.shiro.authc.ExpiredCredentialsException; -import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.realm.AuthenticatingRealm; -import org.apache.shiro.session.Session; -import org.apache.shiro.subject.Subject; import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.KapuaIllegalArgumentException; import org.eclipse.kapua.KapuaRuntimeException; import org.eclipse.kapua.commons.security.KapuaSecurityUtils; import org.eclipse.kapua.locator.KapuaLocator; import org.eclipse.kapua.service.account.Account; -import org.eclipse.kapua.service.account.AccountService; import org.eclipse.kapua.service.authentication.ApiKeyCredentials; import org.eclipse.kapua.service.authentication.credential.Credential; import org.eclipse.kapua.service.authentication.credential.CredentialService; -import org.eclipse.kapua.service.authentication.credential.CredentialStatus; import org.eclipse.kapua.service.authentication.shiro.ApiKeyCredentialsImpl; -import org.eclipse.kapua.service.authentication.shiro.exceptions.ExpiredAccountException; -import org.eclipse.kapua.service.authentication.shiro.exceptions.TemporaryLockedAccountException; import org.eclipse.kapua.service.user.User; import org.eclipse.kapua.service.user.UserService; -import org.eclipse.kapua.service.user.UserStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,26 +40,25 @@ /** * {@link ApiKeyCredentials} based {@link AuthenticatingRealm} implementation. * - * since 1.0 - * + * @since 1.0.0 */ -public class ApiKeyAuthenticatingRealm extends AuthenticatingRealm { +public class ApiKeyAuthenticatingRealm extends KapuaAuthenticatingRealm { - private static final Logger logger = LoggerFactory.getLogger(ApiKeyAuthenticatingRealm.class); + private static final Logger LOG = LoggerFactory.getLogger(ApiKeyAuthenticatingRealm.class); private static final KapuaLocator LOCATOR = KapuaLocator.getInstance(); /** - * Realm name + * Realm name. */ public static final String REALM_NAME = "apiKeyAuthenticatingRealm"; /** - * Constructor + * Constructor. * - * @throws KapuaException + * @since 1.0.0 */ - public ApiKeyAuthenticatingRealm() throws KapuaException { + public ApiKeyAuthenticatingRealm() { setName(REALM_NAME); CredentialsMatcher credentialsMather = new ApiKeyCredentialsMatcher(); @@ -87,49 +76,44 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent // // Get Services UserService userService; - AccountService accountService; CredentialService credentialService; try { userService = LOCATOR.getService(UserService.class); - accountService = LOCATOR.getService(AccountService.class); credentialService = LOCATOR.getService(CredentialService.class); } catch (KapuaRuntimeException kre) { throw new ShiroException("Error while getting services!", kre); } // - // Find credentials - // FIXME: manage multiple credentials and multiple credentials type + // Find credential Credential credential = null; try { credential = KapuaSecurityUtils.doPrivileged(() -> credentialService.findByApiKey(tokenApiKey)); } catch (AuthenticationException ae) { throw ae; - } catch (KapuaIllegalArgumentException kiae) { - logger.warn("Api Key value is not valid"); + } catch (KapuaIllegalArgumentException ae) { + LOG.warn("The given Api Key is not valid. Subsequent UnknownAccountException expected! Given ApiKey: {}", tokenApiKey); } catch (Exception e) { + throw new ShiroException("Error while find credentials!", e); } - // Check existence - if (credential == null) { - throw new UnknownAccountException(); - } + // + // Check credential + checkCredential(credential); - // Check credential disabled - if (CredentialStatus.DISABLED.equals(credential.getStatus())) { - throw new DisabledAccountException(); - } + // + // Get CredentialService config + Map credentialServiceConfig = getCredentialServiceConfig(credential.getScopeId()); - // Check if credential expired - if (credential.getExpirationDate() != null && !credential.getExpirationDate().after(new Date())) { - throw new ExpiredCredentialsException(); - } + // + // Check credential lockout + checkCredentialLockout(credential, credentialServiceConfig); // // Get the associated user by name - final User user; + User user; try { Credential finalCredential = credential; user = KapuaSecurityUtils.doPrivileged(() -> userService.find(finalCredential.getScopeId(), finalCredential.getUserId())); @@ -139,66 +123,13 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent throw new ShiroException("Error while find user!", e); } - // Check existence - if (user == null) { - throw new UnknownAccountException(); - } - - // Check disabled - if (UserStatus.DISABLED.equals(user.getStatus())) { - throw new DisabledAccountException(); - } - - // Check if expired - if (user.getExpirationDate() != null && !user.getExpirationDate().after(new Date())) { - throw new ExpiredCredentialsException(); - } - // - // Find account - final Account account; - try { - account = KapuaSecurityUtils.doPrivileged(() -> accountService.find(user.getScopeId())); - } catch (AuthenticationException ae) { - throw ae; - } catch (Exception e) { - throw new ShiroException("Error while find account!", e); - } + // Check user + checkUser(user); - // Check existence - if (account == null) { - throw new UnknownAccountException(); - } - - // Check account expired - if (account.getExpirationDate() != null && !account.getExpirationDate().after(new Date())) { - throw new ExpiredAccountException(account.getExpirationDate()); - } - - // Check credential disabled - if (CredentialStatus.DISABLED.equals(credential.getStatus())) { - throw new DisabledAccountException(); - } - - // Check if credential expired - if (credential.getExpirationDate() != null && !credential.getExpirationDate().after(new Date())) { - throw new ExpiredCredentialsException(); - } - - // Check if lockout policy is blocking credential - Map credentialServiceConfig; - try { - credentialServiceConfig = KapuaSecurityUtils.doPrivileged(() -> credentialService.getConfigValues(account.getId())); - boolean lockoutPolicyEnabled = (boolean) credentialServiceConfig.get("lockoutPolicy.enabled"); - if (lockoutPolicyEnabled) { - Date now = new Date(); - if (credential.getLockoutReset() != null && now.before(credential.getLockoutReset())) { - throw new TemporaryLockedAccountException(credential.getLockoutReset()); - } - } - } catch (KapuaException kex) { - throw new ShiroException("Error while checking lockout policy", kex); - } + // + // Check account + Account account = checkAccount(user.getScopeId()); // // BuildAuthenticationInfo @@ -224,7 +155,8 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica boolean lockoutPolicyEnabled = (boolean) credentialServiceConfig.get("lockoutPolicy.enabled"); if (lockoutPolicyEnabled) { Date now = new Date(); - int resetAfterSeconds = (int)credentialServiceConfig.get("lockoutPolicy.resetAfter"); + int resetAfterSeconds = (int) credentialServiceConfig.get("lockoutPolicy.resetAfter"); + Date firstLoginFailure; boolean resetAttempts = failedCredential.getFirstLoginFailure() == null || now.after(failedCredential.getLoginFailuresReset()); if (resetAttempts) { @@ -234,12 +166,13 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica firstLoginFailure = failedCredential.getFirstLoginFailure(); failedCredential.setLoginFailures(failedCredential.getLoginFailures() + 1); } - Date loginFailureWindowExpiration = new Date(firstLoginFailure.getTime() + (resetAfterSeconds * 1000)); + + Date loginFailureWindowExpiration = new Date(firstLoginFailure.getTime() + (resetAfterSeconds * 1000L)); failedCredential.setFirstLoginFailure(firstLoginFailure); failedCredential.setLoginFailuresReset(loginFailureWindowExpiration); - int maxLoginFailures = (int)credentialServiceConfig.get("lockoutPolicy.maxFailures"); + int maxLoginFailures = (int) credentialServiceConfig.get("lockoutPolicy.maxFailures"); if (failedCredential.getLoginFailures() >= maxLoginFailures) { - long lockoutDuration = (int)credentialServiceConfig.get("lockoutPolicy.lockDuration"); + long lockoutDuration = (int) credentialServiceConfig.get("lockoutPolicy.lockDuration"); Date resetDate = new Date(now.getTime() + (lockoutDuration * 1000)); failedCredential.setLockoutReset(resetDate); } @@ -252,20 +185,14 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica } throw authenticationEx; } - Credential credential = (Credential) kapuaInfo.getCredentials(); - credential.setFirstLoginFailure(null); - credential.setLoginFailuresReset(null); - credential.setLockoutReset(null); - credential.setLoginFailures(0); - try { - KapuaSecurityUtils.doPrivileged(() -> credentialService.update(credential)); - } catch (KapuaException kex) { - throw new ShiroException("Error while updating lockout policy", kex); - } - Subject currentSubject = SecurityUtils.getSubject(); - Session session = currentSubject.getSession(); - session.setAttribute("scopeId", kapuaInfo.getUser().getScopeId()); - session.setAttribute("userId", kapuaInfo.getUser().getId()); + + // + // Reset Credential lockout policy after successful login + resetCredentialLockout((Credential) kapuaInfo.getCredentials()); + + // + // Populate Session with info + populateSession(SecurityUtils.getSubject(), kapuaInfo); } @Override diff --git a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/JwtAuthenticatingRealm.java b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/JwtAuthenticatingRealm.java index ca6fc2e7710..235784b67d6 100644 --- a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/JwtAuthenticatingRealm.java +++ b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/JwtAuthenticatingRealm.java @@ -18,67 +18,59 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.DisabledAccountException; -import org.apache.shiro.authc.ExpiredCredentialsException; -import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.realm.AuthenticatingRealm; -import org.apache.shiro.session.Session; -import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Destroyable; -import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.KapuaRuntimeException; import org.eclipse.kapua.commons.security.KapuaSecurityUtils; import org.eclipse.kapua.locator.KapuaLocator; +import org.eclipse.kapua.plugin.sso.openid.JwtProcessor; +import org.eclipse.kapua.plugin.sso.openid.exception.OpenIDException; import org.eclipse.kapua.service.account.Account; -import org.eclipse.kapua.service.account.AccountService; import org.eclipse.kapua.service.authentication.ApiKeyCredentials; import org.eclipse.kapua.service.authentication.credential.Credential; import org.eclipse.kapua.service.authentication.credential.CredentialStatus; import org.eclipse.kapua.service.authentication.credential.CredentialType; import org.eclipse.kapua.service.authentication.credential.shiro.CredentialImpl; import org.eclipse.kapua.service.authentication.shiro.JwtCredentialsImpl; -import org.eclipse.kapua.service.authentication.shiro.exceptions.ExpiredAccountException; import org.eclipse.kapua.service.authentication.shiro.utils.JwtProcessors; import org.eclipse.kapua.service.user.User; import org.eclipse.kapua.service.user.UserService; -import org.eclipse.kapua.service.user.UserStatus; -import org.eclipse.kapua.plugin.sso.openid.JwtProcessor; -import org.eclipse.kapua.plugin.sso.openid.exception.OpenIDException; import org.jose4j.jwt.consumer.JwtContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - /** * {@link ApiKeyCredentials} based {@link AuthenticatingRealm} implementation. + * + * @since 1.0.0 */ -public class JwtAuthenticatingRealm extends AuthenticatingRealm implements Destroyable { +public class JwtAuthenticatingRealm extends KapuaAuthenticatingRealm implements Destroyable { - private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticatingRealm.class); + private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticatingRealm.class); /** - * Realm name + * Realm name. */ public static final String REALM_NAME = "jwtAuthenticatingRealm"; /** - * JWT Processor + * JWT Processor. */ private JwtProcessor jwtProcessor; /** - * Constructor + * Constructor. * - * @throws KapuaException + * @since 1.0.0 */ - public JwtAuthenticatingRealm() throws KapuaException { + public JwtAuthenticatingRealm() { setName(REALM_NAME); } @Override protected void onInit() { super.onInit(); + try { jwtProcessor = JwtProcessors.createDefault(); setCredentialsMatcher(new JwtCredentialsMatcher(jwtProcessor)); @@ -106,20 +98,18 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent // Get Services final KapuaLocator locator; final UserService userService; - final AccountService accountService; try { locator = KapuaLocator.getInstance(); userService = locator.getService(UserService.class); - accountService = locator.getService(AccountService.class); } catch (KapuaRuntimeException kre) { throw new ShiroException("Error while getting services!", kre); } - final String id = extractExternalId(idToken); - logger.debug("JWT contains external id: {}", id); + String id = extractExternalId(idToken); + LOG.debug("JWT contains external id: {}", id); + // // Get the associated user by external id - final User user; try { user = KapuaSecurityUtils.doPrivileged(() -> userService.findByExternalId(id)); @@ -129,51 +119,19 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent throw new ShiroException("Error looking up the user", e); } - // Check user existence - - if (user == null) { - throw new UnknownAccountException(); - } - - // Check disabled - - if (UserStatus.DISABLED.equals(user.getStatus())) { - throw new DisabledAccountException(); - } - - // Check if expired - if (user.getExpirationDate() != null && !user.getExpirationDate().after(new Date())) { - throw new ExpiredCredentialsException(); - } - - // Find account - - final Account account; - try { - account = KapuaSecurityUtils.doPrivileged(() -> accountService.find(user.getScopeId())); - } catch (AuthenticationException e) { - throw e; - } catch (Exception e) { - throw new ShiroException("Error while find account!", e); - } - - // Check account expired - if (account.getExpirationDate() != null && !account.getExpirationDate().after(new Date())) { - throw new ExpiredAccountException(account.getExpirationDate()); - } - - // Check account existence + // + // Check user + checkUser(user); - if (account == null) { - throw new UnknownAccountException(); - } + // + // Check account + Account account = checkAccount(user.getScopeId()); + // // Create credential - - final Credential credential = new CredentialImpl(user.getScopeId(), user.getId(), CredentialType.JWT, idToken, CredentialStatus.ENABLED, null); + Credential credential = new CredentialImpl(user.getScopeId(), user.getId(), CredentialType.JWT, idToken, CredentialStatus.ENABLED, null); // Build AuthenticationInfo - return new LoginAuthenticationInfo(getName(), account, user, @@ -207,14 +165,13 @@ private String extractExternalId(String jwt) { @Override protected void assertCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) throws AuthenticationException { - final LoginAuthenticationInfo kapuaInfo = (LoginAuthenticationInfo) info; + LoginAuthenticationInfo kapuaInfo = (LoginAuthenticationInfo) info; super.assertCredentialsMatch(authcToken, info); - final Subject currentSubject = SecurityUtils.getSubject(); - Session session = currentSubject.getSession(); - session.setAttribute("scopeId", kapuaInfo.getUser().getScopeId()); - session.setAttribute("userId", kapuaInfo.getUser().getId()); + // + // Populate Session with info + populateSession(SecurityUtils.getSubject(), kapuaInfo); } @Override diff --git a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/KapuaAuthenticatingRealm.java b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/KapuaAuthenticatingRealm.java new file mode 100644 index 00000000000..cfa8bfbc17d --- /dev/null +++ b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/KapuaAuthenticatingRealm.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2021 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech - initial API and implementation + *******************************************************************************/ +package org.eclipse.kapua.service.authentication.shiro.realm; + + +import org.apache.shiro.ShiroException; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.DisabledAccountException; +import org.apache.shiro.authc.ExpiredCredentialsException; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.realm.AuthenticatingRealm; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; +import org.eclipse.kapua.KapuaException; +import org.eclipse.kapua.commons.security.KapuaSecurityUtils; +import org.eclipse.kapua.locator.KapuaLocator; +import org.eclipse.kapua.model.id.KapuaId; +import org.eclipse.kapua.service.account.Account; +import org.eclipse.kapua.service.account.AccountService; +import org.eclipse.kapua.service.authentication.credential.Credential; +import org.eclipse.kapua.service.authentication.credential.CredentialService; +import org.eclipse.kapua.service.authentication.credential.CredentialStatus; +import org.eclipse.kapua.service.authentication.shiro.exceptions.ExpiredAccountException; +import org.eclipse.kapua.service.authentication.shiro.exceptions.TemporaryLockedAccountException; +import org.eclipse.kapua.service.authentication.shiro.session.ShiroSessionKeys; +import org.eclipse.kapua.service.user.User; +import org.eclipse.kapua.service.user.UserStatus; + +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.Map; + +/** + * Base {@code abstract} {@link AuthenticatingRealm} extension. + * + * @since 1.6.0 + */ +public abstract class KapuaAuthenticatingRealm extends AuthenticatingRealm { + + private static final KapuaLocator LOCATOR = KapuaLocator.getInstance(); + + // + // Session + + /** + * Populates the {@link Session} of the given {@link Subject} with information from the given {@link LoginAuthenticationInfo}. + * + * @param subject The {@link Subject} of the login. + * @param loginAuthenticationInfo The {@link LoginAuthenticationInfo} source of the data + * @since 1.6.0 + */ + protected void populateSession(@NotNull Subject subject, @NotNull LoginAuthenticationInfo loginAuthenticationInfo) { + Session session = subject.getSession(); + + session.setAttribute(ShiroSessionKeys.SCOPE_ID, loginAuthenticationInfo.getAccount().getId()); + session.setAttribute(ShiroSessionKeys.ACCOUNT_NAME, loginAuthenticationInfo.getAccount().getName()); + session.setAttribute(ShiroSessionKeys.USER_ID, loginAuthenticationInfo.getUser().getId()); + session.setAttribute(ShiroSessionKeys.USER_NAME, loginAuthenticationInfo.getUser().getName()); + } + + // + // Account + + /** + * Check the given {@link Account#getId()}. + *

+ * Checks performed: + *

+ * + * @param accountId The {@link Account#getId()} to check. + * @return The found {@link Account}. + * @since 1.6.0 + */ + protected Account checkAccount(KapuaId accountId) { + AccountService accountService = LOCATOR.getService(AccountService.class); + + Account account; + try { + account = KapuaSecurityUtils.doPrivileged(() -> accountService.find(accountId)); + } catch (AuthenticationException ae) { + throw ae; + } catch (Exception e) { + throw new ShiroException("Internal error while looking for the account!", e); + } + + // Check existence + if (account == null) { + throw new UnknownAccountException(); + } + + // Check account expired + if (account.getExpirationDate() != null && !account.getExpirationDate().after(new Date())) { + throw new ExpiredAccountException(account.getExpirationDate()); + } + + return account; + } + + + // + // Credential + + /** + * Check the given {@link Credential}. + *

+ * Checks performed: + *

+ * + * @param credential The {@link Credential} to check. + * @throws UnknownAccountException if {@link Credential} is {@code null} + * @throws DisabledAccountException if {@link Credential#getStatus()} is {@link CredentialStatus#DISABLED}. + * @throws ExpiredCredentialsException if {@link Credential#getExpirationDate()} is passed. + * @since 1.6.0 + */ + protected void checkCredential(Credential credential) throws UnknownAccountException, DisabledAccountException, ExpiredCredentialsException { + if (credential == null) { + throw new UnknownAccountException(); + } + + // Check credential disabled + if (CredentialStatus.DISABLED.equals(credential.getStatus())) { + throw new DisabledAccountException(); + } + + // Check if credential expired + if (credential.getExpirationDate() != null && !credential.getExpirationDate().after(new Date())) { + throw new ExpiredCredentialsException(); + } + } + + /** + * Checks the {@link Credential} against the Lockout policy of the {@link CredentialService#getConfigValues(KapuaId)}. + * + * @param credential The {@link Credential} to check. + * @param credentialServiceConfig The {@link CredentialService#getConfigValues(KapuaId)} + * @throws TemporaryLockedAccountException if the {@link Credential} is temporary locked. + * @since 1.6.0 + */ + protected void checkCredentialLockout(Credential credential, Map credentialServiceConfig) throws TemporaryLockedAccountException { + + boolean lockoutPolicyEnabled = (boolean) credentialServiceConfig.get("lockoutPolicy.enabled"); + + if (lockoutPolicyEnabled) { + Date now = new Date(); + if (credential.getLockoutReset() != null && now.before(credential.getLockoutReset())) { + throw new TemporaryLockedAccountException(credential.getLockoutReset()); + } + } + } + + /** + * Gets the {@link CredentialService#getConfigValues(KapuaId)} for the given scope {@link KapuaId}. + * + * @param scopeId The scope {@link KapuaId}. + * @return The {@link Map} of configuration values of the {@link CredentialService}. + * @since 1.6.0 + */ + protected Map getCredentialServiceConfig(KapuaId scopeId) { + try { + CredentialService credentialService = LOCATOR.getService(CredentialService.class); + return KapuaSecurityUtils.doPrivileged(() -> credentialService.getConfigValues(scopeId)); + } catch (KapuaException e) { + throw new ShiroException("Error while find credentials!", e); + } + } + + /** + * Reset the lockout policy of the {@link Credential}. + * To be used after a succeessful login. + * + * @param credential The {@link Credential} to reset. + * @since 1.6.0 + */ + protected void resetCredentialLockout(Credential credential) { + CredentialService credentialService = LOCATOR.getService(CredentialService.class); + + credential.setFirstLoginFailure(null); + credential.setLoginFailuresReset(null); + credential.setLockoutReset(null); + credential.setLoginFailures(0); + try { + KapuaSecurityUtils.doPrivileged(() -> credentialService.update(credential)); + } catch (KapuaException kex) { + throw new ShiroException("Error while updating lockout policy", kex); + } + } + + // + // User + + /** + * Check the given {@link User}. + *

+ * Checks performed: + *

    + *
  • Existence of the {@link User}
  • + *
  • {@link User#getStatus()}
  • + *
  • {@link User#getExpirationDate()} ()}
  • + *
+ * + * @param user The {@link User} to check. + * @throws UnknownAccountException if {@link User} is {@code null} + * @throws DisabledAccountException if {@link User#getStatus()} is {@link UserStatus#DISABLED}. + * @throws ExpiredCredentialsException if {@link User#getExpirationDate()} is passed. + * @since 1.6.0 + */ + protected void checkUser(User user) { + if (user == null) { + throw new UnknownAccountException(); + } + + // Check disabled + if (UserStatus.DISABLED.equals(user.getStatus())) { + throw new DisabledAccountException(); + } + + // Check if expired + if (user.getExpirationDate() != null && !user.getExpirationDate().after(new Date())) { + throw new ExpiredCredentialsException(); + } + } +} diff --git a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/UserPassAuthenticatingRealm.java b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/UserPassAuthenticatingRealm.java index 6a9926849f4..6fb425a3696 100644 --- a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/UserPassAuthenticatingRealm.java +++ b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/realm/UserPassAuthenticatingRealm.java @@ -17,61 +17,53 @@ import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.DisabledAccountException; -import org.apache.shiro.authc.ExpiredCredentialsException; import org.apache.shiro.authc.IncorrectCredentialsException; -import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.realm.AuthenticatingRealm; -import org.apache.shiro.session.Session; -import org.apache.shiro.subject.Subject; import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.KapuaRuntimeException; import org.eclipse.kapua.commons.security.KapuaSecurityUtils; import org.eclipse.kapua.locator.KapuaLocator; import org.eclipse.kapua.model.id.KapuaId; import org.eclipse.kapua.service.account.Account; -import org.eclipse.kapua.service.account.AccountService; import org.eclipse.kapua.service.authentication.UsernamePasswordCredentials; import org.eclipse.kapua.service.authentication.credential.Credential; import org.eclipse.kapua.service.authentication.credential.CredentialListResult; import org.eclipse.kapua.service.authentication.credential.CredentialService; -import org.eclipse.kapua.service.authentication.credential.CredentialStatus; import org.eclipse.kapua.service.authentication.credential.CredentialType; import org.eclipse.kapua.service.authentication.credential.mfa.MfaOptionService; import org.eclipse.kapua.service.authentication.shiro.UsernamePasswordCredentialsImpl; -import org.eclipse.kapua.service.authentication.shiro.exceptions.ExpiredAccountException; -import org.eclipse.kapua.service.authentication.shiro.exceptions.TemporaryLockedAccountException; import org.eclipse.kapua.service.user.User; import org.eclipse.kapua.service.user.UserService; -import org.eclipse.kapua.service.user.UserStatus; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; +import java.util.List; import java.util.Map; /** * {@link UsernamePasswordCredentials} based {@link AuthenticatingRealm} implementation. - *

- * since 1.0 + * + * @since 1.0.0 */ -public class UserPassAuthenticatingRealm extends AuthenticatingRealm { +public class UserPassAuthenticatingRealm extends KapuaAuthenticatingRealm { + + private static final Logger LOG = LoggerFactory.getLogger(UserPassAuthenticatingRealm.class); - private static final Logger logger = LoggerFactory.getLogger(UserPassAuthenticatingRealm.class); private static final KapuaLocator LOCATOR = KapuaLocator.getInstance(); + /** - * Realm name + * Realm name. */ public static final String REALM_NAME = "userPassAuthenticatingRealm"; /** - * Constructor + * Constructor. * - * @throws KapuaException + * @since 1.0.0 */ - public UserPassAuthenticatingRealm() throws KapuaException { + public UserPassAuthenticatingRealm() { setName(REALM_NAME); CredentialsMatcher credentialsMather = new UserPassCredentialsMatcher(); @@ -89,12 +81,10 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent // // Get Services UserService userService; - AccountService accountService; CredentialService credentialService; try { userService = LOCATOR.getService(UserService.class); - accountService = LOCATOR.getService(AccountService.class); credentialService = LOCATOR.getService(CredentialService.class); } catch (KapuaRuntimeException kre) { throw new ShiroException("Error while getting services!", kre); @@ -111,41 +101,13 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent throw new ShiroException("Error while find user!", e); } - // Check existence - if (user == null) { - throw new UnknownAccountException(); - } - - // Check disabled - if (UserStatus.DISABLED.equals(user.getStatus())) { - throw new DisabledAccountException(); - } - - // Check if expired - if (user.getExpirationDate() != null && !user.getExpirationDate().after(new Date())) { - throw new ExpiredCredentialsException(); - } - // - // Find account - final Account account; - try { - account = KapuaSecurityUtils.doPrivileged(() -> accountService.find(user.getScopeId())); - } catch (AuthenticationException ae) { - throw ae; - } catch (Exception e) { - throw new ShiroException("Error while find account!", e); - } - - // Check existence - if (account == null) { - throw new UnknownAccountException(); - } + // Check user + checkUser(user); - // Check account expired - if (account.getExpirationDate() != null && !account.getExpirationDate().after(new Date())) { - throw new ExpiredAccountException(account.getExpirationDate()); - } + // + // Check account + Account account = checkAccount(user.getScopeId()); // // Find credentials @@ -153,20 +115,11 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent Credential credential; try { credential = KapuaSecurityUtils.doPrivileged(() -> { - CredentialListResult credentialList = credentialService.findByUserId(user.getScopeId(), user.getId()); + CredentialListResult userCredentialList = credentialService.findByUserId(user.getScopeId(), user.getId()); - if (credentialList != null && !credentialList.isEmpty()) { - Credential credentialMatched = null; - for (Credential c : credentialList.getItems()) { - if (CredentialType.PASSWORD.equals(c.getCredentialType())) { - credentialMatched = c; - break; - } - } - return credentialMatched; - } else { - return null; - } + List passwordCredentialList = userCredentialList.getItems(c -> CredentialType.PASSWORD.equals(c.getCredentialType())); + + return passwordCredentialList.isEmpty() ? null : passwordCredentialList.get(0); }); } catch (AuthenticationException ae) { throw ae; @@ -174,35 +127,17 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authent throw new ShiroException("Error while find credentials!", e); } - // Check existence - if (credential == null) { - throw new UnknownAccountException(); - } - - // Check credential disabled - if (CredentialStatus.DISABLED.equals(credential.getStatus())) { - throw new DisabledAccountException(); - } + // + // Check credential + checkCredential(credential); - // Check if credential expired - if (credential.getExpirationDate() != null && !credential.getExpirationDate().after(new Date())) { - throw new ExpiredCredentialsException(); - } + // + // Get CredentialService config + Map credentialServiceConfig = getCredentialServiceConfig(credential.getScopeId()); - // Check if lockout policy is blocking credential - Map credentialServiceConfig; - try { - credentialServiceConfig = KapuaSecurityUtils.doPrivileged(() -> credentialService.getConfigValues(account.getId())); - boolean lockoutPolicyEnabled = (boolean) credentialServiceConfig.get("lockoutPolicy.enabled"); - if (lockoutPolicyEnabled) { - Date now = new Date(); - if (credential.getLockoutReset() != null && now.before(credential.getLockoutReset())) { - throw new TemporaryLockedAccountException(credential.getLockoutReset()); - } - } - } catch (KapuaException kex) { - throw new ShiroException("Error while checking lockout policy", kex); - } + // + // Check credential lockout + checkCredentialLockout(credential, credentialServiceConfig); // // BuildAuthenticationInfo @@ -229,8 +164,8 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica hasMfa = true; } } catch (KapuaException e) { - logger.warn(e.toString()); - throw new ShiroException("Error while find user!", e); + LOG.warn("Error while finding User. Error: {}", e.getMessage()); + throw new ShiroException("Error while finding user!", e); } try { @@ -255,7 +190,8 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica boolean lockoutPolicyEnabled = (boolean) credentialServiceConfig.get("lockoutPolicy.enabled"); if (lockoutPolicyEnabled) { Date now = new Date(); - int resetAfterSeconds = (int)credentialServiceConfig.get("lockoutPolicy.resetAfter"); + int resetAfterSeconds = (int) credentialServiceConfig.get("lockoutPolicy.resetAfter"); + Date firstLoginFailure; boolean resetAttempts = failedCredential.getFirstLoginFailure() == null || now.after(failedCredential.getLoginFailuresReset()) || @@ -267,12 +203,13 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica firstLoginFailure = failedCredential.getFirstLoginFailure(); failedCredential.setLoginFailures(failedCredential.getLoginFailures() + 1); } - Date loginFailureWindowExpiration = new Date(firstLoginFailure.getTime() + (resetAfterSeconds * 1000)); + + Date loginFailureWindowExpiration = new Date(firstLoginFailure.getTime() + (resetAfterSeconds * 1000L)); failedCredential.setFirstLoginFailure(firstLoginFailure); failedCredential.setLoginFailuresReset(loginFailureWindowExpiration); - int maxLoginFailures = (int)credentialServiceConfig.get("lockoutPolicy.maxFailures"); + int maxLoginFailures = (int) credentialServiceConfig.get("lockoutPolicy.maxFailures"); if (failedCredential.getLoginFailures() >= maxLoginFailures) { - long lockoutDuration = (int)credentialServiceConfig.get("lockoutPolicy.lockDuration"); + long lockoutDuration = (int) credentialServiceConfig.get("lockoutPolicy.lockDuration"); Date resetDate = new Date(now.getTime() + (lockoutDuration * 1000)); failedCredential.setLockoutReset(resetDate); } @@ -285,20 +222,14 @@ protected void assertCredentialsMatch(AuthenticationToken authcToken, Authentica } throw authenticationEx; } - Credential credential = (Credential) kapuaInfo.getCredentials(); - credential.setFirstLoginFailure(null); - credential.setLoginFailuresReset(null); - credential.setLockoutReset(null); - credential.setLoginFailures(0); - try { - KapuaSecurityUtils.doPrivileged(() -> credentialService.update(credential)); - } catch (KapuaException kex) { - throw new ShiroException("Error while updating lockout policy", kex); - } - Subject currentSubject = SecurityUtils.getSubject(); - Session session = currentSubject.getSession(); - session.setAttribute("scopeId", scopeId); - session.setAttribute("userId", userId); + + // + // Reset Credential lockout policy after successful login + resetCredentialLockout((Credential) kapuaInfo.getCredentials()); + + // + // Populate Session with info + populateSession(SecurityUtils.getSubject(), kapuaInfo); } @Override diff --git a/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/session/ShiroSessionKeys.java b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/session/ShiroSessionKeys.java new file mode 100644 index 00000000000..cb508249911 --- /dev/null +++ b/service/security/shiro/src/main/java/org/eclipse/kapua/service/authentication/shiro/session/ShiroSessionKeys.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2021 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech - initial API and implementation + *******************************************************************************/ +package org.eclipse.kapua.service.authentication.shiro.session; + +import org.apache.shiro.session.Session; +import org.eclipse.kapua.service.account.Account; +import org.eclipse.kapua.service.user.User; + +/** + * {@link Session#getAttribute(Object)} keys. + * + * @since 1.6.0 + */ +public class ShiroSessionKeys { + + /** + * Constructor. + * + * @since 1.6.0 + */ + private ShiroSessionKeys() { + } + + /** + * The {@link Account#getId()}. + * + * @since 1.0.0 + */ + public static final String SCOPE_ID = "scopeId"; + + /** + * The {@link Account#getName()}. + * + * @since 1.6.0 + */ + public static final String ACCOUNT_NAME = "scopeName"; + + /** + * The {@link User#getId()}. + * + * @since 1.0.0 + */ + public static final String USER_ID = "userId"; + + /** + * The {@link User#getName()}. + * + * @since 1.6.0 + */ + public static final String USER_NAME = "userName"; +}