AuthenticationProperties.java
/*
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.spring.boot.autoconfigure.security.authentication;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* The authentication properties.
*
* @author Christian Bremer
*/
@ConfigurationProperties(prefix = "bremersee.authentication")
@Data
public class AuthenticationProperties {
/**
* The remember-me properties.
*/
private RememberMeProperties rememberMe = new RememberMeProperties();
/**
* The jwt converter properties.
*/
private JwtConverterProperties jwtConverter = new JwtConverterProperties();
/**
* The ldaptive properties.
*/
private LdaptiveProperties ldaptive = new LdaptiveProperties();
/**
* Instantiates new authentication properties.
*/
public AuthenticationProperties() {
super();
}
/**
* The remember-me properties.
*
* @author Christian Bremer
*/
@Data
public static class RememberMeProperties {
/**
* The key.
*/
private String key;
/**
* Specifies whether remember-me is always activated.
*/
private Boolean alwaysRemember;
/**
* The cookie name.
*/
private String cookieName;
/**
* The cookie domain.
*/
private String cookieDomain;
/**
* Specifies whether to use secure cookie.
*/
private Boolean useSecureCookie;
/**
* The parameter name (default remember-me).
*/
private String parameterName;
/**
* The token validity in seconds (default two weeks).
*/
private Integer tokenValiditySeconds;
/**
* Instantiates new remember-me properties.
*/
public RememberMeProperties() {
super();
}
}
/**
* The jwt converter properties.
*/
@Data
public static class JwtConverterProperties {
/**
* The json path to the username.
*/
private String nameJsonPath = "$.sub"; // keycloak: $.preferred_username
/**
* The json path to the first name.
*/
private String firstNameJsonPath = "$.given_name";
/**
* The json path to the last name.
*/
private String lastNameJsonPath = "$.family_name";
/**
* The json path to the email.
*/
private String emailJsonPath = "$.email";
/**
* The json path to the roles.
*/
private String rolesJsonPath = "$.scope"; // keycloak: $.realm_access.roles
/**
* Specifies whether the roles are represented as a json array or as a list separated by
* {@link #getRolesValueSeparator()}.
*/
private boolean rolesValueList = false; // keycloak: true
/**
* The roles separator to use if {@link #isRolesValueList()} is set to {@code false}.
*/
private String rolesValueSeparator = " ";
/**
* The default roles.
*/
private List<String> defaultRoles = new ArrayList<>();
/**
* The role mappings.
*/
private List<RoleMapping> roleMapping = new ArrayList<>();
/**
* The role prefix (like 'ROLE_' or 'SCOPE_').
*/
private String rolePrefix = "SCOPE_"; // keycloak: ROLE_
/**
* The role case transformation.
*/
private CaseTransformation roleCaseTransformation;
/**
* The string replacements for roles.
*/
private List<StringReplacement> roleStringReplacements;
/**
* Instantiates new jwt converter properties.
*/
public JwtConverterProperties() {
super();
}
/**
* To role mappings map.
*
* @return the map
*/
public Map<String, String> toRoleMappings() {
return Stream.ofNullable(getRoleMapping())
.flatMap(Collection::stream)
.collect(Collectors.toMap(
RoleMapping::getSource,
RoleMapping::getTarget,
(first, second) -> first,
LinkedHashMap::new));
}
/**
* To role string replacements map.
*
* @return the map
*/
public Map<String, String> toRoleStringReplacements() {
return Stream.ofNullable(getRoleStringReplacements())
.flatMap(Collection::stream)
.collect(Collectors.toMap(
StringReplacement::getRegex,
StringReplacement::getReplacement,
(first, second) -> first,
LinkedHashMap::new));
}
}
/**
* The ldaptive properties.
*/
@Data
public static class LdaptiveProperties {
/**
* The ldap template with default configuration properties.
*/
private Template template = Template.ACTIVE_DIRECTORY;
/**
* The username to bind dn converter property.
*/
private UsernameToBindDnConverterProperty usernameToBindDnConverter;
/**
* The user base dn (like 'ou=people,dc=example,dc=org'). This value is always required.
*/
private String userBaseDn;
/**
* A list with refused usernames.
*/
private List<String> refusedUsernames;
/**
* The object class of the user (like 'inetOrgPerson'). The selected template contains a
* default.
*/
private String userObjectClass;
/**
* The username attribute of the user (like 'uid' or 'sAMAccountName'). The selected template
* contains a default.
*/
private String usernameAttribute;
/**
* Applies only for simple bind. The rdn attribute of the user. This is normally the same as the
* username attribute.
*/
private String userRdnAttribute;
/**
* The password attribute of the user (like 'userPassword'). If it is empty, a simple user bind
* will be done with the credentials of the user for authentication. If it is present, the
* connection to the ldap server must be done by a 'global' user and a password encoder that
* fits your requirements must be present. The default password encoder only supports SHA, that
* is insecure.
*/
private String passwordAttribute;
/**
* The password last set attribute (like 'pwdLastSet') can be used to activate the remember-me
* functionality.
*/
private String passwordLastSetAttribute;
/**
* The filter to find the user. If it is empty, it will be generated from 'userObjectClass' and
* 'usernameAttribute' like this '(&(objectClass=inetOrgPerson)(uid={0}))'.
*/
private String userFindOneFilter;
/**
* The scope to find a user. Default is 'one level'.
*/
private SearchScope userFindOneSearchScope;
/**
* The first name attribute of the user. Default is 'givenName'.
*/
protected String firstNameAttribute;
/**
* The last name attribute of the user. Default is 'sn'.
*/
protected String lastNameAttribute;
/**
* The email attribute of the user. Default is 'mail';
*/
private String emailAttribute;
/**
* The account control evaluator.
*/
private AccountControlEvaluatorProperty accountControlEvaluator;
/**
* The group fetch strategy.
*/
private GroupFetchStrategy groupFetchStrategy;
/**
* The member attribute.
*/
private String memberAttribute;
/**
* The group base dn (like 'ou=groups,dc=example,dc=org'). It's only required, if
* {@code groupFetchStrategy} is set to {@code GROUP_CONTAINS_USERS}.
*/
private String groupBaseDn;
/**
* The group search scope. It's only required, if {@code groupFetchStrategy} is set to
* {@code GROUP_CONTAINS_USERS},
*/
private SearchScope groupSearchScope;
/**
* The group object class. It's only required, if {@code groupFetchStrategy} is set to
* {@code GROUP_CONTAINS_USERS}
*/
private String groupObjectClass;
/**
* The group id attribute. It's only required, if {@code groupFetchStrategy} is set to
* {@code GROUP_CONTAINS_USERS}
*/
private String groupIdAttribute;
/**
* The group member attribute. It's only required, if {@code groupFetchStrategy} is set to
* {@code GROUP_CONTAINS_USERS}
*/
private String groupMemberAttribute;
/**
* The group member format. It's only required, if {@code groupFetchStrategy} is set to
* {@code GROUP_CONTAINS_USERS}
*/
private String groupMemberFormat;
/**
* The role mappings.
*/
private List<RoleMapping> roleMapping;
/**
* The default roles.
*/
private List<String> defaultRoles;
/**
* The role prefix (like 'ROLE_').
*/
private String rolePrefix;
/**
* The role case transformation.
*/
private CaseTransformation roleCaseTransformation;
/**
* The string replacements for roles.
*/
private List<StringReplacement> roleStringReplacements;
/**
* Instantiates new ldaptive properties.
*/
public LdaptiveProperties() {
super();
}
/**
* The search scope.
*/
public enum SearchScope {
/**
* Base object search.
*/
OBJECT,
/**
* Single level search.
*/
ONELEVEL,
/**
* Whole subtree search.
*/
SUBTREE,
/**
* Subordinate subtree search. See draft-sermersheim-ldap-subordinate-scope.
*/
SUBORDINATE
}
/**
* The username to bind dn converter property.
*/
public enum UsernameToBindDnConverterProperty {
/**
* By user rdn attribute username to bind dn converter property.
*/
BY_USER_RDN_ATTRIBUTE,
/**
* By domain email username to bind dn converter property.
*/
BY_DOMAIN_EMAIL
}
/**
* The account control evaluator property.
*/
public enum AccountControlEvaluatorProperty {
/**
* The None.
*/
NONE,
/**
* The Active directory.
*/
ACTIVE_DIRECTORY
}
/**
* The group fetch strategy.
*/
public enum GroupFetchStrategy {
/**
* Groups will not be fetched.
*/
NONE,
/**
* User contains groups group-fetch strategy.
*/
USER_CONTAINS_GROUPS,
/**
* Group contains users group-fetch strategy.
*/
GROUP_CONTAINS_USERS
}
/**
* The templates for ldap authentication.
*
* @author Christian Bremer
*/
public enum Template {
/**
* Active directory template.
*/
ACTIVE_DIRECTORY,
/**
* Open ldap template.
*/
OPEN_LDAP,
/**
* User contains groups template.
*/
USER_CONTAINS_GROUPS,
/**
* Group contains users template.
*/
GROUP_CONTAINS_USERS
}
}
/**
* The role mapping.
*/
@Data
public static class RoleMapping {
private String source;
private String target;
/**
* Instantiates a new role mapping.
*/
public RoleMapping() {
super();
}
}
/**
* The case transformation.
*/
public enum CaseTransformation {
/**
* None case transformation.
*/
NONE,
/**
* To upper case transformation.
*/
TO_UPPER_CASE,
/**
* To lower case transformation.
*/
TO_LOWER_CASE
}
/**
* The string replacement.
*/
@Data
public static class StringReplacement {
/**
* The regular expression to which the string is to be matched. '{@code [- ]}' for example would
* replace every '-' and every space.
*/
private String regex;
/**
* The string to be substituted for each match.
*/
private String replacement;
/**
* Instantiates a new string replacement.
*/
public StringReplacement() {
super();
}
}
}