You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2017/03/07 16:35:35 UTC
svn commit: r1785855 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/security/authentication/token/
main/java/org/apache/jackrabbit/oak/security/authentication/user/
main/java/org/apache/jackrabbit/oak/security/user/ main/j...
Author: angela
Date: Tue Mar 7 16:35:35 2017
New Revision: 1785855
URL: http://svn.apache.org/viewvc?rev=1785855&view=rev
Log:
OAK-5903 : Authentication: add extension to retrieve user principal
OAK-4462 : LoginModuleImpl: option to have AuthInfo populated with userId instead of loginName
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthentication.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/Authentication.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/package-info.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthenticationTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/WhiteboardUserAuthenticationFactoryTest.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthentication.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthentication.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthentication.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthentication.java Tue Mar 7 16:35:35 2017
@@ -16,7 +16,9 @@
*/
package org.apache.jackrabbit.oak.security.authentication.token;
+import java.security.Principal;
import java.util.Date;
+import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.jcr.Credentials;
@@ -63,6 +65,28 @@ class TokenAuthentication implements Aut
return false;
}
+ @CheckForNull
+ @Override
+ public String getUserId() {
+ if (tokenInfo == null) {
+ throw new IllegalStateException("UserId can only be retrieved after successful authentication.");
+ }
+ return tokenInfo.getUserId();
+ }
+
+ @CheckForNull
+ @Override
+ public Principal getUserPrincipal() {
+ if (tokenInfo == null) {
+ throw new IllegalStateException("Token info can only be retrieved after successful authentication.");
+ }
+ if (tokenInfo instanceof TokenProviderImpl.TokenInfoImpl) {
+ return ((TokenProviderImpl.TokenInfoImpl) tokenInfo).getPrincipal();
+ } else {
+ return null;
+ }
+ }
+
//-----------------------------------------------------------< internal >---
@Nonnull
TokenInfo getTokenInfo() {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenLoginModule.java Tue Mar 7 16:35:35 2017
@@ -120,6 +120,7 @@ public final class TokenLoginModule exte
private TokenCredentials tokenCredentials;
private TokenInfo tokenInfo;
private String userId;
+ private Principal principal;
//--------------------------------------------------------< LoginModule >---
@Override
@@ -136,7 +137,8 @@ public final class TokenLoginModule exte
if (authentication.authenticate(tc)) {
tokenCredentials = tc;
tokenInfo = authentication.getTokenInfo();
- userId = tokenInfo.getUserId();
+ userId = authentication.getUserId();
+ principal = authentication.getUserPrincipal();
log.debug("Login: adding login name to shared state.");
sharedState.put(SHARED_KEY_LOGIN_NAME, userId);
@@ -150,7 +152,7 @@ public final class TokenLoginModule exte
@Override
public boolean commit() throws LoginException {
if (tokenCredentials != null && userId != null) {
- Set<? extends Principal> principals = getPrincipals(userId);
+ Set<? extends Principal> principals = (principal != null) ? getPrincipals(principal) : getPrincipals(userId);
updateSubject(tokenCredentials, getAuthInfo(tokenInfo, principals), principals);
return true;
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenProviderImpl.java Tue Mar 7 16:35:35 2017
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.securi
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Calendar;
@@ -30,6 +31,7 @@ import java.util.Map;
import java.util.UUID;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
@@ -224,17 +226,12 @@ class TokenProviderImpl implements Token
root.commit(CommitMarker.asCommitAttributes());
}
return tokenInfo;
- } catch (NoSuchAlgorithmException e) {
+ } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
// error while generating login token
log.error(error, e.getMessage());
- } catch (UnsupportedEncodingException e) {
- // error while generating login token
- log.error(error, e.getMessage());
- } catch (CommitFailedException e) {
+ } catch (CommitFailedException | RepositoryException e) {
// conflict while committing changes
log.warn(error, e.getMessage());
- } catch (RepositoryException e) {
- log.warn(error, e.getMessage());
}
} else {
log.warn("Unable to get/create token store for user " + userId);
@@ -258,12 +255,16 @@ class TokenProviderImpl implements Token
String nodeId = (pos == -1) ? token : token.substring(0, pos);
Tree tokenTree = identifierManager.getTree(nodeId);
if (isValidTokenTree(tokenTree)) {
- String userId = getUserId(tokenTree);
- if (userId != null) {
- return new TokenInfoImpl(new NodeUtil(tokenTree), token, userId);
+ try {
+ User user = getUser(tokenTree);
+ if (user != null) {
+ return new TokenInfoImpl(new NodeUtil(tokenTree), token, user.getID(), user.getPrincipal());
+ }
+ } catch (RepositoryException e) {
+ log.debug("Cannot determine userID/principal from token: {}", e.getMessage());
}
}
- // not a valid token tree or failed to extract userID
+ // invalid token tree or failed to extract user or it's id/principal
return null;
}
@@ -331,17 +332,14 @@ class TokenProviderImpl implements Token
}
@CheckForNull
- private String getUserId(@Nonnull Tree tokenTree) {
- try {
- String userPath = Text.getRelativeParent(tokenTree.getPath(), 2);
- Authorizable authorizable = userManager.getAuthorizableByPath(userPath);
- if (authorizable != null && !authorizable.isGroup() && !((User) authorizable).isDisabled()) {
- return authorizable.getID();
- }
- } catch (RepositoryException e) {
- log.debug("Cannot determine userID from token: {}", e.getMessage());
+ private User getUser(@Nonnull Tree tokenTree) throws RepositoryException {
+ String userPath = Text.getRelativeParent(tokenTree.getPath(), 2);
+ Authorizable authorizable = userManager.getAuthorizableByPath(userPath);
+ if (authorizable != null && !authorizable.isGroup() && !((User) authorizable).isDisabled()) {
+ return (User) authorizable;
+ } else {
+ return null;
}
- return null;
}
@CheckForNull
@@ -425,7 +423,7 @@ class TokenProviderImpl implements Token
tokenNode.setString(name, attr);
}
}
- return new TokenInfoImpl(tokenNode, token, id);
+ return new TokenInfoImpl(tokenNode, token, id, null);
}
//--------------------------------------------------------------------------
@@ -433,11 +431,12 @@ class TokenProviderImpl implements Token
/**
* TokenInfo
*/
- private final class TokenInfoImpl implements TokenInfo {
+ final class TokenInfoImpl implements TokenInfo {
private final String token;
private final String tokenPath;
private final String userId;
+ private final Principal principal;
private final long expirationTime;
private final String key;
@@ -445,17 +444,17 @@ class TokenProviderImpl implements Token
private final Map<String, String> mandatoryAttributes;
private final Map<String, String> publicAttributes;
-
- private TokenInfoImpl(@Nonnull NodeUtil tokenNode, @Nonnull String token, @Nonnull String userId) {
+ private TokenInfoImpl(@Nonnull NodeUtil tokenNode, @Nonnull String token, @Nonnull String userId, @Nullable Principal principal) {
this.token = token;
this.tokenPath = tokenNode.getTree().getPath();
this.userId = userId;
+ this.principal = principal;
expirationTime = getExpirationTime(tokenNode, Long.MIN_VALUE);
key = tokenNode.getString(TOKEN_ATTRIBUTE_KEY, null);
- mandatoryAttributes = new HashMap<String, String>();
- publicAttributes = new HashMap<String, String>();
+ mandatoryAttributes = new HashMap();
+ publicAttributes = new HashMap();
for (PropertyState propertyState : tokenNode.getTree().getProperties()) {
String name = propertyState.getName();
String value = propertyState.getValue(STRING);
@@ -471,6 +470,11 @@ class TokenProviderImpl implements Token
}
}
+ @CheckForNull
+ Principal getPrincipal() {
+ return principal;
+ }
+
//------------------------------------------------------< TokenInfo >---
@Nonnull
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImpl.java Tue Mar 7 16:35:35 2017
@@ -111,34 +111,41 @@ public final class LoginModuleImpl exten
private Credentials credentials;
private String userId;
+ private Principal principal;
+ private boolean success;
//--------------------------------------------------------< LoginModule >---
@Override
public boolean login() throws LoginException {
- final boolean success;
credentials = getCredentials();
// check if we have a pre authenticated login from a previous login module
PreAuthenticatedLogin preAuthLogin = getSharedPreAuthLogin();
- if (preAuthLogin != null) {
- userId = preAuthLogin.getUserId();
- Authentication authentication = getUserAuthentication(userId);
- success = authentication != null && authentication.authenticate(PreAuthenticatedLogin.PRE_AUTHENTICATED);
- } else {
- userId = getUserId();
- Authentication authentication = getUserAuthentication(userId);
- success = authentication != null && authentication.authenticate(credentials);
- }
+ String loginName = getLoginId(preAuthLogin);
+ Authentication authentication = getUserAuthentication(loginName);
+ if (authentication != null) {
+ if (preAuthLogin != null) {
+ success = authentication.authenticate(PreAuthenticatedLogin.PRE_AUTHENTICATED);
+ } else {
+ success = authentication.authenticate(credentials);
+ }
- if (success) {
- log.debug("Adding Credentials to shared state.");
- //noinspection unchecked
- sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
-
- log.debug("Adding login name to shared state.");
- //noinspection unchecked
- sharedState.put(SHARED_KEY_LOGIN_NAME, userId);
+ if (success) {
+ log.debug("Adding Credentials to shared state.");
+ //noinspection unchecked
+ sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
+
+ log.debug("Adding login name to shared state.");
+ //noinspection unchecked
+ sharedState.put(SHARED_KEY_LOGIN_NAME, loginName);
+
+ userId = authentication.getUserId();
+ if (userId == null) {
+ userId = loginName;
+ }
+ principal = authentication.getUserPrincipal();
+ }
} else {
// ensure that we don't commit (OAK-2998, OAK-3032)
credentials = null;
@@ -149,14 +156,18 @@ public final class LoginModuleImpl exten
@Override
public boolean commit() {
- if (credentials == null) {
+ if (!success) {
// login attempt in this login module was not successful
clearState();
return false;
} else {
if (!subject.isReadOnly()) {
- Set<? extends Principal> principals = getPrincipals(userId);
- subject.getPrincipals().addAll(principals);
+ Set<Principal> principals = subject.getPrincipals();
+ if (principal != null) {
+ principals.addAll(getPrincipals(principal));
+ } else if (userId != null) {
+ principals.addAll(getPrincipals(userId));
+ }
subject.getPublicCredentials().add(credentials);
setAuthInfo(createAuthInfo(principals), subject);
} else {
@@ -179,11 +190,16 @@ public final class LoginModuleImpl exten
credentials = null;
userId = null;
+ principal = null;
}
//--------------------------------------------------------------------------
@CheckForNull
- private String getUserId() {
+ private String getLoginId(@CheckForNull PreAuthenticatedLogin preAuthenticatedLogin) {
+ if (preAuthenticatedLogin != null) {
+ return preAuthenticatedLogin.getUserId();
+ }
+
String uid = null;
if (credentials != null) {
if (credentials instanceof SimpleCredentials) {
@@ -225,14 +241,14 @@ public final class LoginModuleImpl exten
}
@CheckForNull
- private Authentication getUserAuthentication(@Nullable String userId) {
+ private Authentication getUserAuthentication(@Nullable String loginName) {
SecurityProvider securityProvider = getSecurityProvider();
Root root = getRoot();
if (securityProvider != null && root != null) {
UserConfiguration uc = securityProvider.getConfiguration(UserConfiguration.class);
UserAuthenticationFactory factory = uc.getParameters().getConfigValue(UserConstants.PARAM_USER_AUTHENTICATION_FACTORY, null, UserAuthenticationFactory.class);
if (factory != null) {
- return factory.getAuthentication(uc, root, userId);
+ return factory.getAuthentication(uc, root, loginName);
} else {
log.error("No user authentication factory configured in user configuration.");
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java Tue Mar 7 16:35:35 2017
@@ -16,6 +16,7 @@
*/
package org.apache.jackrabbit.oak.security.user;
+import java.security.Principal;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javax.annotation.CheckForNull;
@@ -79,42 +80,45 @@ class UserAuthentication implements Auth
private final UserConfiguration config;
private final Root root;
- private final String userId;
+ private final String loginId;
- UserAuthentication(@Nonnull UserConfiguration config, @Nonnull Root root, @Nullable String userId) {
+ private String userId;
+ private Principal principal;
+
+ UserAuthentication(@Nonnull UserConfiguration config, @Nonnull Root root, @Nullable String loginId) {
this.config = config;
this.root = root;
- this.userId = userId;
+ this.loginId = loginId;
}
//-----------------------------------------------------< Authentication >---
@Override
public boolean authenticate(@Nullable Credentials credentials) throws LoginException {
- if (credentials == null || userId == null) {
+ if (credentials == null || loginId == null) {
return false;
}
boolean success = false;
try {
UserManager userManager = config.getUserManager(root, NamePathMapper.DEFAULT);
- Authorizable authorizable = userManager.getAuthorizable(userId);
+ Authorizable authorizable = userManager.getAuthorizable(loginId);
if (authorizable == null) {
return false;
}
if (authorizable.isGroup()) {
- throw new AccountNotFoundException("Not a user " + userId);
+ throw new AccountNotFoundException("Not a user " + loginId);
}
User user = (User) authorizable;
if (user.isDisabled()) {
- throw new AccountLockedException("User with ID " + userId + " has been disabled: "+ user.getDisabledReason());
+ throw new AccountLockedException("User with ID " + loginId + " has been disabled: "+ user.getDisabledReason());
}
if (credentials instanceof SimpleCredentials) {
SimpleCredentials creds = (SimpleCredentials) credentials;
Credentials userCreds = user.getCredentials();
- if (userId.equals(creds.getUserID()) && userCreds instanceof CredentialsImpl) {
+ if (loginId.equals(creds.getUserID()) && userCreds instanceof CredentialsImpl) {
success = PasswordUtil.isSame(((CredentialsImpl) userCreds).getPasswordHash(), creds.getPassword());
}
checkSuccess(success, "UserId/Password mismatch.");
@@ -129,18 +133,39 @@ class UserAuthentication implements Auth
} else if (credentials instanceof ImpersonationCredentials) {
ImpersonationCredentials ipCreds = (ImpersonationCredentials) credentials;
AuthInfo info = ipCreds.getImpersonatorInfo();
- success = equalUserId(ipCreds, userId) && impersonate(info, user);
+ success = equalUserId(ipCreds, loginId) && impersonate(info, user);
checkSuccess(success, "Impersonation not allowed.");
} else {
// guest login is allowed if an anonymous user exists in the content (see get user above)
success = (credentials instanceof GuestCredentials) || credentials == PreAuthenticatedLogin.PRE_AUTHENTICATED;
}
+ userId = user.getID();
+ principal = user.getPrincipal();
} catch (RepositoryException e) {
throw new LoginException(e.getMessage());
}
return success;
}
+ @CheckForNull
+ @Override
+ public String getUserId() {
+ if (userId == null) {
+ throw new IllegalStateException("UserId can only be retrieved after successful authentication.");
+ }
+ return userId;
+ }
+
+ @CheckForNull
+ @Override
+ public Principal getUserPrincipal() {
+ if (principal == null) {
+ throw new IllegalStateException("Principal can only be retrieved after successful authentication.");
+ }
+ return principal;
+ }
+
+
//--------------------------------------------------------------------------
private static void checkSuccess(boolean success, String msg) throws LoginException {
if (!success) {
@@ -160,22 +185,22 @@ class UserAuthentication implements Auth
if (newPasswordObject instanceof String) {
user.changePassword((String) newPasswordObject);
root.commit();
- log.debug("User " + userId + ": changed user password");
+ log.debug("User " + loginId + ": changed user password");
return true;
} else {
- log.warn("Aborted password change for user " + userId
+ log.warn("Aborted password change for user " + loginId
+ ": provided new password is of incompatible type "
+ newPasswordObject.getClass().getName());
}
}
} catch (PasswordHistoryException e) {
credentials.setAttribute(e.getClass().getSimpleName(), e.getMessage());
- log.error("Failed to change password for user " + userId, e.getMessage());
+ log.error("Failed to change password for user " + loginId, e.getMessage());
} catch (RepositoryException e) {
- log.error("Failed to change password for user " + userId, e.getMessage());
+ log.error("Failed to change password for user " + loginId, e.getMessage());
} catch (CommitFailedException e) {
root.refresh();
- log.error("Failed to change password for user " + userId, e.getMessage());
+ log.error("Failed to change password for user " + loginId, e.getMessage());
}
return false;
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java Tue Mar 7 16:35:35 2017
@@ -21,6 +21,7 @@ import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
@@ -396,11 +397,7 @@ public abstract class AbstractLoginModul
} else {
log.debug("Unable to retrieve the Root via RepositoryCallback; ContentRepository not available.");
}
- } catch (UnsupportedCallbackException e) {
- log.debug(e.getMessage());
- } catch (IOException e) {
- log.debug(e.getMessage());
- } catch (PrivilegedActionException e){
+ } catch (UnsupportedCallbackException | PrivilegedActionException | IOException e) {
log.debug(e.getMessage());
}
}
@@ -429,9 +426,7 @@ public abstract class AbstractLoginModul
UserManagerCallback userCallBack = new UserManagerCallback();
callbackHandler.handle(new Callback[]{userCallBack});
userManager = userCallBack.getUserManager();
- } catch (IOException e) {
- log.debug(e.getMessage());
- } catch (UnsupportedCallbackException e) {
+ } catch (IOException | UnsupportedCallbackException e) {
log.debug(e.getMessage());
}
}
@@ -461,9 +456,7 @@ public abstract class AbstractLoginModul
PrincipalProviderCallback principalCallBack = new PrincipalProviderCallback();
callbackHandler.handle(new Callback[]{principalCallBack});
principalProvider = principalCallBack.getPrincipalProvider();
- } catch (IOException e) {
- log.debug(e.getMessage());
- } catch (UnsupportedCallbackException e) {
+ } catch (IOException | UnsupportedCallbackException e) {
log.debug(e.getMessage());
}
}
@@ -489,6 +482,20 @@ public abstract class AbstractLoginModul
}
}
+ @Nonnull
+ protected Set<? extends Principal> getPrincipals(@Nonnull Principal userPrincipal) {
+ PrincipalProvider principalProvider = getPrincipalProvider();
+ if (principalProvider == null) {
+ log.debug("Cannot retrieve principals. No principal provider configured.");
+ return Collections.emptySet();
+ } else {
+ Set<Principal> principals = new HashSet();
+ principals.add(userPrincipal);
+ principals.addAll(principalProvider.getGroupMembership(userPrincipal));
+ return principals;
+ }
+ }
+
protected static void setAuthInfo(@Nonnull AuthInfo authInfo, @Nonnull Subject subject) {
Set<AuthInfo> ais = subject.getPublicCredentials(AuthInfo.class);
if (!ais.isEmpty()) {
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/Authentication.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/Authentication.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/Authentication.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/Authentication.java Tue Mar 7 16:35:35 2017
@@ -16,6 +16,8 @@
*/
package org.apache.jackrabbit.oak.spi.security.authentication;
+import java.security.Principal;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.jcr.Credentials;
import javax.security.auth.login.LoginException;
@@ -49,4 +51,27 @@ public interface Authentication {
* @throws LoginException if the authentication failed.
*/
boolean authenticate(@Nullable Credentials credentials) throws LoginException;
+
+ /**
+ * Optional method that return the userID extracted upon {@link #authenticate(Credentials)}.
+ * It is expected to return {@code null} if the implementation doesn't support this.
+ *
+ * An {@link IllegalStateException} may be thrown if called prior to {@link #authenticate(Credentials)}.
+ *
+ * @return a user identifier or {@code null}
+ */
+ @CheckForNull
+ String getUserId();
+
+ /**
+ * Optional method that return the {@link Principal} of the authenticating user
+ * extracted upon {@link #authenticate(Credentials)}. It is expected to return
+ * {@code null} if the implementation doesn't support this.
+ *
+ * An {@link IllegalStateException} may be thrown if called prior to {@link #authenticate(Credentials)}.
+ *
+ * @return a valid {@code Principal} or {@code null}
+ */
+ @CheckForNull
+ Principal getUserPrincipal();
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/package-info.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/package-info.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/package-info.java Tue Mar 7 16:35:35 2017
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@Version("1.1.0")
+@Version("2.0.0")
package org.apache.jackrabbit.oak.spi.security.authentication;
import aQute.bnd.annotation.Version;
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthenticationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthenticationTest.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthenticationTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenAuthenticationTest.java Tue Mar 7 16:35:35 2017
@@ -31,7 +31,6 @@ import org.apache.jackrabbit.api.securit
import org.apache.jackrabbit.oak.AbstractSecurityTest;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
import org.apache.jackrabbit.oak.spi.security.authentication.Authentication;
-import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration;
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
import org.junit.Before;
@@ -44,9 +43,6 @@ import static org.junit.Assert.assertNul
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-/**
- * TokenAuthenticationTest...
- */
public class TokenAuthenticationTest extends AbstractSecurityTest {
TokenAuthentication authentication;
@@ -156,4 +152,28 @@ public class TokenAuthenticationTest ext
now = waitForSystemTimeIncrement(now);
}
}
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetUserIdBeforeLogin() {
+ authentication.getUserId();
+ }
+
+ @Test
+ public void testGetUserId() throws LoginException {
+ TokenInfo info = tokenProvider.createToken(userId, Collections.<String, Object>emptyMap());
+ assertTrue(authentication.authenticate(new TokenCredentials(info.getToken())));
+ assertEquals(userId, authentication.getUserId());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetUserPrincipalBeforeLogin() {
+ authentication.getUserPrincipal();
+ }
+
+ @Test
+ public void testGetUserPrincipal() throws Exception {
+ TokenInfo info = tokenProvider.createToken(userId, Collections.<String, Object>emptyMap());
+ assertTrue(authentication.authenticate(new TokenCredentials(info.getToken())));
+ assertEquals(getTestUser().getPrincipal(), authentication.getUserPrincipal());
+ }
}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/user/LoginModuleImplTest.java Tue Mar 7 16:35:35 2017
@@ -16,13 +16,26 @@
*/
package org.apache.jackrabbit.oak.security.authentication.user;
+import java.io.IOException;
+import java.security.Principal;
import java.util.Arrays;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.Credentials;
import javax.jcr.GuestCredentials;
import javax.jcr.RepositoryException;
import javax.jcr.SimpleCredentials;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginException;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
@@ -30,13 +43,20 @@ import org.apache.jackrabbit.oak.Abstrac
import org.apache.jackrabbit.oak.api.AuthInfo;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.Authentication;
import org.apache.jackrabbit.oak.spi.security.authentication.ConfigurationUtil;
import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
+import org.apache.jackrabbit.oak.spi.security.authentication.callback.RepositoryCallback;
+import org.apache.jackrabbit.oak.spi.security.user.UserAuthenticationFactory;
import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
import org.apache.jackrabbit.oak.spi.security.user.util.UserUtil;
import org.junit.Test;
+import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,9 +64,6 @@ import static org.junit.Assert.assertNot
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-/**
- * LoginTest...
- */
public class LoginModuleImplTest extends AbstractSecurityTest {
private static final String USER_ID = "test";
@@ -93,13 +110,10 @@ public class LoginModuleImplTest extends
@Test
public void testGuestLogin() throws Exception {
- ContentSession cs = login(new GuestCredentials());
- try {
+ try (ContentSession cs = login(new GuestCredentials())) {
AuthInfo authInfo = cs.getAuthInfo();
String anonymousID = UserUtil.getAnonymousId(getUserConfiguration().getParameters());
assertEquals(anonymousID, authInfo.getUserID());
- } finally {
- cs.close();
}
}
@@ -144,6 +158,22 @@ public class LoginModuleImplTest extends
}
@Test
+ public void testAuthInfoContainsUserId() throws Exception {
+ ContentSession cs = null;
+ try {
+ createTestUser();
+
+ cs = login(new SimpleCredentials(USER_ID_CASED, USER_PW.toCharArray()));
+ AuthInfo authInfo = cs.getAuthInfo();
+ assertEquals(user.getID(), authInfo.getUserID());
+ } finally {
+ if (cs != null) {
+ cs.close();
+ }
+ }
+ }
+
+ @Test
public void testUserLoginIsCaseInsensitive() throws Exception {
ContentSession cs = null;
try {
@@ -154,6 +184,7 @@ public class LoginModuleImplTest extends
UserManager userMgr = getUserManager(root);
Authorizable auth = userMgr.getAuthorizable(authInfo.getUserID());
assertNotNull(auth);
+ assertTrue(auth.getID().equalsIgnoreCase(USER_ID_CASED));
} finally {
if (cs != null) {
cs.close();
@@ -168,7 +199,8 @@ public class LoginModuleImplTest extends
createTestUser();
cs = login(new SimpleCredentials(USER_ID_CASED, USER_PW.toCharArray()));
AuthInfo authInfo = cs.getAuthInfo();
- assertEquals(USER_ID_CASED, authInfo.getUserID());
+ assertEquals(user.getID(), authInfo.getUserID());
+ assertTrue(USER_ID_CASED.equalsIgnoreCase(authInfo.getUserID()));
} finally {
if (cs != null) {
cs.close();
@@ -300,4 +332,82 @@ public class LoginModuleImplTest extends
}
}
}
+
+ @Test
+ public void testGetNullUserAuthentication() throws Exception {
+ LoginModuleImpl loginModule = new LoginModuleImpl();
+ CallbackHandler cbh = new TestCallbackHandler(Mockito.mock(UserAuthenticationFactory.class));
+ loginModule.initialize(new Subject(), cbh, Maps.<String, Object>newHashMap(), Maps.<String, Object>newHashMap());
+
+ assertFalse(loginModule.login());
+ assertFalse(loginModule.commit());
+ }
+
+ @Test
+ public void testCustomUserAuthentication() throws Exception {
+ LoginModuleImpl loginModule = new LoginModuleImpl();
+
+ UserAuthenticationFactory factory = new UserAuthenticationFactory() {
+ @CheckForNull
+ @Override
+ public Authentication getAuthentication(@Nonnull UserConfiguration configuration, @Nonnull Root root, @Nullable String userId) {
+ return new Authentication() {
+ @Override
+ public boolean authenticate(@Nullable Credentials credentials) throws LoginException {
+ return true;
+ }
+
+ @CheckForNull
+ @Override
+ public String getUserId() {
+ return null;
+ }
+
+ @CheckForNull
+ @Override
+ public Principal getUserPrincipal() {
+ return null;
+ }
+ };
+ }
+ };
+
+ CallbackHandler cbh = new TestCallbackHandler(factory);
+ SimpleCredentials creds = new SimpleCredentials("loginId", new char[0]);
+ Subject subject = new Subject(false, Sets.<Principal>newHashSet(), ImmutableSet.of(creds), Sets.newHashSet());
+
+ loginModule.initialize(subject, cbh, Maps.<String, Object>newHashMap(), Maps.<String, Object>newHashMap());
+ assertTrue(loginModule.login());
+ assertTrue(loginModule.commit());
+
+ AuthInfo authInfo = subject.getPublicCredentials(AuthInfo.class).iterator().next();
+ assertEquals("loginId", authInfo.getUserID());
+ }
+
+
+ private class TestCallbackHandler implements CallbackHandler {
+
+ private final SecurityProvider sp;
+
+ private TestCallbackHandler(@Nullable UserAuthenticationFactory authenticationFactory) {
+ ConfigurationParameters params = ConfigurationParameters.of(
+ UserConfiguration.NAME,
+ ConfigurationParameters.of(
+ UserConstants.PARAM_USER_AUTHENTICATION_FACTORY, authenticationFactory));
+ this.sp = new SecurityProviderImpl(params);
+ }
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback callback : callbacks) {
+ if (callback instanceof RepositoryCallback) {
+ ((RepositoryCallback) callback).setSecurityProvider(sp);
+ ((RepositoryCallback) callback).setContentRepository(getContentRepository());
+ } else {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+ }
+ }
+
}
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationTest.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserAuthenticationTest.java Tue Mar 7 16:35:35 2017
@@ -39,7 +39,9 @@ import org.apache.jackrabbit.oak.spi.sec
import org.junit.Before;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -96,10 +98,8 @@ public class UserAuthenticationTest exte
// success
assertTrue(e instanceof AccountNotFoundException);
} finally {
- if (g != null) {
- g.remove();
- root.commit();
- }
+ g.remove();
+ root.commit();
}
}
@@ -189,6 +189,39 @@ public class UserAuthenticationTest exte
assertTrue(authentication.authenticate(new ImpersonationCredentials(sc, new TestAuthInfo())));
}
+ @Test(expected = IllegalStateException.class)
+ public void testGetUserIdBeforeLogin() {
+ authentication.getUserId();
+ }
+
+ @Test
+ public void testGetUserId() throws LoginException {
+ authentication.authenticate(new SimpleCredentials(userId, userId.toCharArray()));
+ assertEquals(userId, authentication.getUserId());
+ }
+
+ @Test
+ public void testGetUserIdCasedLoginId() throws LoginException {
+ String loginId = userId.toLowerCase();
+
+ UserAuthentication auth = new UserAuthentication(getUserConfiguration(), root, loginId);
+ assertTrue(auth.authenticate(new SimpleCredentials(loginId, userId.toCharArray())));
+
+ assertNotEquals(loginId, auth.getUserId());
+ assertEquals(userId, auth.getUserId());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetUserPrincipalBeforeLogin() {
+ authentication.getUserPrincipal();
+ }
+
+ @Test
+ public void testGetUserPrincipal() throws Exception {
+ authentication.authenticate(new SimpleCredentials(userId, userId.toCharArray()));
+ assertEquals(getTestUser().getPrincipal(), authentication.getUserPrincipal());
+ }
+
//--------------------------------------------------------------------------
private final class TestAuthInfo implements AuthInfo {
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModuleTest.java Tue Mar 7 16:35:35 2017
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.oak.spi.se
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
@@ -65,9 +66,6 @@ import static org.junit.Assert.assertNul
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-/**
- * AbstractLoginModuleTest...
- */
public class AbstractLoginModuleTest {
private static AbstractLoginModule initLoginModule(Class supportedCredentials, Map sharedState) {
@@ -452,6 +450,31 @@ public class AbstractLoginModuleTest {
assertTrue(principals.isEmpty());
}
+ @Test
+ public void testGetPrincipalsFromPrincipal() {
+ PrincipalProvider principalProvider = new TestPrincipalProvider();
+
+ AbstractLoginModule loginModule = initLoginModule(TestCredentials.class, new TestCallbackHandler(principalProvider));
+
+ Principal principal = principalProvider.findPrincipals(PrincipalManager.SEARCH_TYPE_NOT_GROUP).next();
+ Set<Principal> expected = new HashSet<>();
+ expected.add(principal);
+ expected.addAll(principalProvider.getGroupMembership(principal));
+
+ Set<? extends Principal> principals = loginModule.getPrincipals(principal);
+
+ assertFalse(principals.isEmpty());
+ assertEquals(expected, principals);
+ }
+
+ @Test
+ public void testGetPrincipalsFromPrincipalMissingProvider() {
+ AbstractLoginModule loginModule = initLoginModule(TestCredentials.class, new TestCallbackHandler());
+
+ Set<? extends Principal> principals = loginModule.getPrincipals(new PrincipalImpl("principalName"));
+ assertTrue(principals.isEmpty());
+ }
+
@Test
public void testSetAuthInfo() {
Subject subject = new Subject();
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/WhiteboardUserAuthenticationFactoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/WhiteboardUserAuthenticationFactoryTest.java?rev=1785855&r1=1785854&r2=1785855&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/WhiteboardUserAuthenticationFactoryTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/whiteboard/WhiteboardUserAuthenticationFactoryTest.java Tue Mar 7 16:35:35 2017
@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.jcr.Credentials;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.spi.security.authentication.Authentication;
@@ -106,12 +105,7 @@ public class WhiteboardUserAuthenticatio
@Override
public Authentication getAuthentication(@Nonnull UserConfiguration configuration, @Nonnull Root root, @Nullable String userId) {
if (this.userId.equals(userId)) {
- return new Authentication() {
- @Override
- public boolean authenticate(@Nullable Credentials credentials) {
- return true;
- }
- };
+ return Mockito.mock(Authentication.class);
} else {
return null;
}