blob: 405530bd311d72959131fd8da60b0a4137cabff5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.security.user;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.security.user.autosave.AutoSaveEnabledManager;
import org.apache.jackrabbit.oak.spi.commit.MoveTracker;
import org.apache.jackrabbit.oak.spi.commit.ThreeWayConflictHandler;
import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider;
import org.apache.jackrabbit.oak.spi.lifecycle.WorkspaceInitializer;
import org.apache.jackrabbit.oak.spi.security.ConfigurationBase;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
import org.apache.jackrabbit.oak.spi.security.Context;
import org.apache.jackrabbit.oak.spi.security.SecurityConfiguration;
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
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.PasswordUtil;
import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.Option;
/**
* Default implementation of the {@link UserConfiguration}.
*/
@Component(service = {UserConfiguration.class, SecurityConfiguration.class})
@Designate(ocd = UserConfigurationImpl.Configuration.class)
public class UserConfigurationImpl extends ConfigurationBase implements UserConfiguration, SecurityConfiguration {
@ObjectClassDefinition(name = "Apache Jackrabbit Oak UserConfiguration")
@interface Configuration {
@AttributeDefinition(
name = "User Path",
description = "Path underneath which user nodes are being created.")
String usersPath() default UserConstants.DEFAULT_USER_PATH;
@AttributeDefinition(
name = "Group Path",
description = "Path underneath which group nodes are being created.")
String groupsPath() default UserConstants.DEFAULT_GROUP_PATH;
@AttributeDefinition(
name = "System User Relative Path",
description = "Path relative to the user root path underneath which system user nodes are being " +
"created. The default value is 'system'.")
String systemRelativePath() default UserConstants.DEFAULT_SYSTEM_RELATIVE_PATH;
@AttributeDefinition(
name = "Default Depth",
description = "Number of levels that are used by default to store authorizable nodes")
int defaultDepth() default UserConstants.DEFAULT_DEPTH;
@AttributeDefinition(
name = "Import Behavior",
description = "Behavior for user/group related items upon XML import.",
options = {
@Option(label = ImportBehavior.NAME_ABORT, value = ImportBehavior.NAME_ABORT),
@Option(label = ImportBehavior.NAME_BESTEFFORT, value = ImportBehavior.NAME_BESTEFFORT),
@Option(label = ImportBehavior.NAME_IGNORE, value = ImportBehavior.NAME_IGNORE)
})
String importBehavior() default ImportBehavior.NAME_IGNORE;
@AttributeDefinition(
name = "Hash Algorithm",
description = "Name of the algorithm used to generate the password hash.")
String passwordHashAlgorithm() default PasswordUtil.DEFAULT_ALGORITHM;
@AttributeDefinition(
name = "Hash Iterations",
description = "Number of iterations to generate the password hash.")
int passwordHashIterations() default PasswordUtil.DEFAULT_ITERATIONS;
@AttributeDefinition(
name = "Hash Salt Size",
description = "Salt size to generate the password hash.")
int passwordSaltSize() default PasswordUtil.DEFAULT_SALT_SIZE;
@AttributeDefinition(
name = "Omit Admin Password",
description = "Boolean flag to prevent the administrator account to be created with a password " +
"upon repository initialization. Please note that changing this option after the initial " +
"repository setup will have no effect.")
boolean omitAdminPw() default false;
@AttributeDefinition(
name = "Autosave Support",
description = "Configuration option to enable autosave behavior. Note: this config option is " +
"present for backwards compatibility with Jackrabbit 2.x and should only be used for " +
"broken code that doesn't properly verify the autosave behavior (see Jackrabbit API). " +
"If this option is turned on autosave will be enabled by default; otherwise autosave is " +
"not supported.")
boolean supportAutoSave() default false;
@AttributeDefinition(
name = "Maximum Password Age",
description = "Maximum age in days a password may have. Values greater 0 will implicitly enable " +
"password expiry. A value of 0 indicates unlimited password age.")
int passwordMaxAge() default UserConstants.DEFAULT_PASSWORD_MAX_AGE;
@AttributeDefinition(
name = "Change Password On First Login",
description = "When enabled, forces users to change their password upon first login.")
boolean initialPasswordChange() default UserConstants.DEFAULT_PASSWORD_INITIAL_CHANGE;
@AttributeDefinition(
name = "Maximum Password History Size",
description = "Maximum number of passwords recorded for a user after changing her password (NOTE: " +
"upper limit is 1000). When changing the password the new password must not be present in the " +
"password history. A value of 0 indicates no password history is recorded.")
int passwordHistorySize() default UserConstants.PASSWORD_HISTORY_DISABLED_SIZE;
@AttributeDefinition(
name = "Principal Cache Expiration",
description = "Optional configuration defining the number of milliseconds " +
"until the principal cache expires (NOTE: currently only respected for principal resolution " +
"with the internal system session such as used for login). If not set or equal/lower than zero " +
"no caches are created/evaluated.")
long cacheExpiration() default UserPrincipalProvider.EXPIRATION_NO_CACHE;
@AttributeDefinition(
name = "RFC7613 Username Comparison Profile",
description = "Enable the UsercaseMappedProfile defined in RFC7613 for username comparison.")
boolean enableRFC7613UsercaseMappedProfile() default false;
}
private static final UserAuthenticationFactory DEFAULT_AUTH_FACTORY = new UserAuthenticationFactoryImpl();
public UserConfigurationImpl() {
super();
}
public UserConfigurationImpl(SecurityProvider securityProvider) {
super(securityProvider, securityProvider.getParameters(NAME));
}
public static UserAuthenticationFactory getDefaultAuthenticationFactory() {
return DEFAULT_AUTH_FACTORY;
}
@SuppressWarnings("UnusedDeclaration")
@Activate
// reference to @Configuration class needed for correct DS xml generation
private void activate(Configuration configuration, Map<String, Object> properties) {
setParameters(ConfigurationParameters.of(properties));
}
//----------------------------------------------< SecurityConfiguration >---
@NotNull
@Override
public String getName() {
return NAME;
}
@NotNull
@Override
public ConfigurationParameters getParameters() {
ConfigurationParameters params = super.getParameters();
if (!params.containsKey(UserConstants.PARAM_USER_AUTHENTICATION_FACTORY)) {
return ConfigurationParameters.of(
params,
ConfigurationParameters.of(UserConstants.PARAM_USER_AUTHENTICATION_FACTORY, DEFAULT_AUTH_FACTORY));
} else {
return params;
}
}
@NotNull
@Override
public WorkspaceInitializer getWorkspaceInitializer() {
return new UserInitializer(getSecurityProvider());
}
@NotNull
@Override
public List<? extends ValidatorProvider> getValidators(@NotNull String workspaceName, @NotNull Set<Principal> principals, @NotNull MoveTracker moveTracker) {
return ImmutableList.of(new UserValidatorProvider(getParameters(), getRootProvider(), getTreeProvider()), new CacheValidatorProvider(principals, getTreeProvider()));
}
@NotNull
@Override
public List<ThreeWayConflictHandler> getConflictHandlers() {
return ImmutableList.of(new RepMembersConflictHandler());
}
@NotNull
@Override
public List<ProtectedItemImporter> getProtectedItemImporters() {
return Collections.<ProtectedItemImporter>singletonList(new UserImporter(getParameters()));
}
@NotNull
@Override
public Context getContext() {
return UserContext.getInstance();
}
//--------------------------------------------------< UserConfiguration >---
@NotNull
@Override
public UserManager getUserManager(Root root, NamePathMapper namePathMapper) {
UserManager umgr = new UserManagerImpl(root, namePathMapper, getSecurityProvider());
if (getParameters().getConfigValue(UserConstants.PARAM_SUPPORT_AUTOSAVE, false)) {
return new AutoSaveEnabledManager(umgr, root);
} else {
return umgr;
}
}
@Nullable
@Override
public PrincipalProvider getUserPrincipalProvider(@NotNull Root root, @NotNull NamePathMapper namePathMapper) {
return new UserPrincipalProvider(root, this, namePathMapper);
}
}