UserDetailsLdapMapper.java
/*
* Copyright 2021 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.security.core.userdetails;
import static org.bremersee.data.ldaptive.LdaptiveEntryMapper.getAttributeValue;
import static org.bremersee.data.ldaptive.LdaptiveEntryMapper.getAttributeValuesAsSet;
import static org.bremersee.data.ldaptive.transcoder.UserAccountControlValueTranscoder.isUserAccountEnabled;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.ToString;
import org.bremersee.data.ldaptive.LdaptiveEntryMapper;
import org.bremersee.data.ldaptive.transcoder.UserAccountControlValueTranscoder;
import org.ldaptive.AttributeModification;
import org.ldaptive.LdapEntry;
import org.ldaptive.transcode.AbstractStringValueTranscoder;
import org.ldaptive.transcode.ValueTranscoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
/**
* The user details mapper.
*
* @author Christian Bremer
*/
@ToString
public class UserDetailsLdapMapper implements LdaptiveEntryMapper<UserDetails> {
@Getter(value = AccessLevel.PROTECTED)
private final String userName;
@Getter(value = AccessLevel.PROTECTED)
private final String userAccountControlAttributeName;
@Getter(value = AccessLevel.PROTECTED)
private final List<String> authorities;
@Getter(value = AccessLevel.PROTECTED)
private final String authorityAttributeName;
@Getter(value = AccessLevel.PROTECTED)
private final String authorityPrefix;
@Getter(value = AccessLevel.PROTECTED)
private final ValueTranscoder<GrantedAuthority> authorityTranscoder;
@Getter(value = AccessLevel.PROTECTED)
private final UserAccountControlValueTranscoder userAccountControlValueTranscoder;
/**
* Instantiates a new user details mapper.
*
* @param userName the user name
* @param userAccountControlAttributeName the user account control attribute name
* @param authorities the authorities
* @param authorityAttributeName the authority attribute name
* @param authorityDn the authority dn
* @param authorityMap the authority map
* @param authorityPrefix the authority prefix
*/
public UserDetailsLdapMapper(
String userName,
String userAccountControlAttributeName,
List<String> authorities,
String authorityAttributeName,
boolean authorityDn,
Map<String, String> authorityMap,
String authorityPrefix) {
this.userName = userName;
this.userAccountControlAttributeName = userAccountControlAttributeName;
this.authorities = authorities != null ? authorities : Collections.emptyList();
this.authorityAttributeName = authorityAttributeName;
this.authorityPrefix = authorityPrefix;
this.authorityTranscoder = new GrantedAuthorityValueTranscoder(authorityDn, authorityMap, authorityPrefix);
if (StringUtils.hasText(userAccountControlAttributeName)) {
userAccountControlValueTranscoder = new UserAccountControlValueTranscoder();
} else {
userAccountControlValueTranscoder = null;
}
}
@Override
public String[] getObjectClasses() {
return null;
}
@Override
public String mapDn(UserDetails domainObject) {
return null;
}
@Override
public UserDetails map(LdapEntry ldapEntry) {
return new User(
userName,
userName,
isAccountEnabled(ldapEntry),
isAccountNonExpired(ldapEntry),
isCredentialsNonExpired(ldapEntry),
isAccountNonLocked(ldapEntry),
getGrantedAuthorities(ldapEntry));
}
@Override
public void map(LdapEntry source, UserDetails destination) {
throw new UnsupportedOperationException("User details are unmodifiable.");
}
@Override
public AttributeModification[] mapAndComputeModifications(UserDetails source, LdapEntry destination) {
return new AttributeModification[0];
}
/**
* Determines whether the account is enabled or not.
*
* @param ldapEntry the ldap entry
* @return the boolean
*/
protected boolean isAccountEnabled(LdapEntry ldapEntry) {
return !StringUtils.hasText(getUserAccountControlAttributeName()) || isUserAccountEnabled(getAttributeValue(
ldapEntry, getUserAccountControlAttributeName(), getUserAccountControlValueTranscoder(), null));
}
/**
* Determines whether the account is not expired.
*
* @param ldapEntry the ldap entry
* @return the boolean
*/
protected boolean isAccountNonExpired(@SuppressWarnings("unused") LdapEntry ldapEntry) {
return true;
}
/**
* Determines whether the account credentials are not expired.
*
* @param ldapEntry the ldap entry
* @return the boolean
*/
protected boolean isCredentialsNonExpired(@SuppressWarnings("unused") LdapEntry ldapEntry) {
return true;
}
/**
* Determines whether the account is not locked.
*
* @param ldapEntry the ldap entry
* @return the boolean
*/
protected boolean isAccountNonLocked(@SuppressWarnings("unused") LdapEntry ldapEntry) {
return true;
}
/**
* Gets granted authorities.
*
* @param ldapEntry the ldap entry
* @return the granted authorities
*/
protected Collection<? extends GrantedAuthority> getGrantedAuthorities(LdapEntry ldapEntry) {
Set<GrantedAuthority> grantedAuthorities = getAuthorities().stream()
.map(value -> UserDetailsLdapMapper.prefixAuthority(getAuthorityPrefix(), value))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
if (StringUtils.hasText(getAuthorityAttributeName())) {
grantedAuthorities.addAll(
getAttributeValuesAsSet(ldapEntry, getAuthorityAttributeName(), getAuthorityTranscoder()));
}
return grantedAuthorities;
}
/**
* Prefix authority.
*
* @param prefix the prefix
* @param value the value
* @return the string
*/
protected static String prefixAuthority(String prefix, String value) {
return StringUtils.hasText(prefix) && !value.startsWith(prefix) ? prefix + value : value;
}
/**
* The granted authority value transcoder.
*
* @author Christian Bremer
*/
@ToString
protected static class GrantedAuthorityValueTranscoder extends AbstractStringValueTranscoder<GrantedAuthority> {
@Getter(value = AccessLevel.PROTECTED)
private final boolean authorityDn;
@Getter(value = AccessLevel.PROTECTED)
private final Map<String, String> authorityMap;
@Getter(value = AccessLevel.PROTECTED)
private final String authorityPrefix;
/**
* Instantiates a new granted authority value transcoder.
*
* @param authorityDn the authority dn
* @param authorityMap the authority map
* @param authorityPrefix the authority prefix
*/
public GrantedAuthorityValueTranscoder(
boolean authorityDn,
Map<String, String> authorityMap,
String authorityPrefix) {
this.authorityDn = authorityDn;
this.authorityMap = authorityMap != null ? authorityMap : Collections.emptyMap();
this.authorityPrefix = authorityPrefix;
}
@Override
public GrantedAuthority decodeStringValue(String value) {
String ldapValue = isAuthorityDn() ? LdaptiveEntryMapper.getRdn(value) : value;
String mappedValue = getAuthorityMap().getOrDefault(ldapValue, ldapValue);
String authorityValue = UserDetailsLdapMapper.prefixAuthority(getAuthorityPrefix(), mappedValue);
return new SimpleGrantedAuthority(authorityValue);
}
@Override
public String encodeStringValue(GrantedAuthority value) {
throw new UnsupportedOperationException("Getting ldap attribute value from granted authority is not supported.");
}
@Override
public Class<GrantedAuthority> getType() {
return GrantedAuthority.class;
}
}
}