1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.spring.security;
20
21 import java.util.concurrent.atomic.AtomicReference;
22 import org.apache.commons.lang3.BooleanUtils;
23 import org.apache.commons.lang3.tuple.Triple;
24 import org.apache.syncope.common.keymaster.client.api.DomainOps;
25 import org.apache.syncope.common.keymaster.client.api.KeymasterException;
26 import org.apache.syncope.common.keymaster.client.api.model.Domain;
27 import org.apache.syncope.common.lib.SyncopeConstants;
28 import org.apache.syncope.common.lib.types.AuditElements.Result;
29 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
30 import org.apache.syncope.core.persistence.api.entity.user.User;
31 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.springframework.beans.factory.annotation.Configurable;
35 import org.springframework.security.authentication.AuthenticationProvider;
36 import org.springframework.security.authentication.BadCredentialsException;
37 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
38 import org.springframework.security.core.Authentication;
39
40 @Configurable
41 public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {
42
43 protected static final Logger LOG = LoggerFactory.getLogger(UsernamePasswordAuthenticationProvider.class);
44
45 protected static final Encryptor ENCRYPTOR = Encryptor.getInstance();
46
47 protected final DomainOps domainOps;
48
49 protected final AuthDataAccessor dataAccessor;
50
51 protected final UserProvisioningManager provisioningManager;
52
53 protected final DefaultCredentialChecker credentialChecker;
54
55 protected final SecurityProperties securityProperties;
56
57 public UsernamePasswordAuthenticationProvider(
58 final DomainOps domainOps,
59 final AuthDataAccessor dataAccessor,
60 final UserProvisioningManager provisioningManager,
61 final DefaultCredentialChecker credentialChecker,
62 final SecurityProperties securityProperties) {
63
64 this.domainOps = domainOps;
65 this.dataAccessor = dataAccessor;
66 this.provisioningManager = provisioningManager;
67 this.credentialChecker = credentialChecker;
68 this.securityProperties = securityProperties;
69 }
70
71 @Override
72 public Authentication authenticate(final Authentication authentication) {
73 Domain domain;
74 if (SyncopeConstants.MASTER_DOMAIN.equals(
75 SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain())) {
76
77 domain = new Domain.Builder(SyncopeConstants.MASTER_DOMAIN).build();
78 } else {
79 try {
80 domain = domainOps.read(
81 SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain());
82 } catch (NotFoundException | KeymasterException e) {
83 throw new BadCredentialsException("Could not find domain "
84 + SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain(), e);
85 }
86 }
87
88 AtomicReference<String> username = new AtomicReference<>();
89 Boolean authenticated;
90 AtomicReference<String> delegationKey = new AtomicReference<>();
91
92 if (securityProperties.getAnonymousUser().equals(authentication.getName())) {
93 username.set(securityProperties.getAnonymousUser());
94 credentialChecker.checkIsDefaultAnonymousKeyInUse();
95 authenticated = authentication.getCredentials().toString().equals(securityProperties.getAnonymousKey());
96 } else if (securityProperties.getAdminUser().equals(authentication.getName())) {
97 username.set(securityProperties.getAdminUser());
98 if (SyncopeConstants.MASTER_DOMAIN.equals(domain.getKey())) {
99 credentialChecker.checkIsDefaultAdminPasswordInUse();
100 authenticated = ENCRYPTOR.verify(
101 authentication.getCredentials().toString(),
102 securityProperties.getAdminPasswordAlgorithm(),
103 securityProperties.getAdminPassword());
104 } else {
105 authenticated = ENCRYPTOR.verify(
106 authentication.getCredentials().toString(),
107 domain.getAdminCipherAlgorithm(),
108 domain.getAdminPassword());
109 }
110 } else {
111 Triple<User, Boolean, String> authResult = AuthContextUtils.callAsAdmin(domain.getKey(),
112 () -> dataAccessor.authenticate(domain.getKey(), authentication));
113 authenticated = authResult.getMiddle();
114 if (authResult.getLeft() != null && authResult.getMiddle() != null) {
115 username.set(authResult.getLeft().getUsername());
116
117 if (!authenticated) {
118 AuthContextUtils.callAsAdmin(domain.getKey(), () -> {
119 provisioningManager.internalSuspend(
120 authResult.getLeft().getKey(),
121 securityProperties.getAdminUser(),
122 "Failed authentication");
123 return null;
124 });
125 }
126 }
127 delegationKey.set(authResult.getRight());
128 }
129 if (username.get() == null) {
130 username.set(authentication.getPrincipal().toString());
131 }
132
133 return finalizeAuthentication(
134 authenticated, domain.getKey(), username.get(), delegationKey.get(), authentication);
135 }
136
137 protected Authentication finalizeAuthentication(
138 final Boolean authenticated,
139 final String domain,
140 final String username,
141 final String delegationKey,
142 final Authentication authentication) {
143
144 UsernamePasswordAuthenticationToken token;
145 if (BooleanUtils.isTrue(authenticated)) {
146 token = AuthContextUtils.callAsAdmin(domain, () -> {
147 UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(
148 username,
149 null,
150 dataAccessor.getAuthorities(username, delegationKey));
151 upat.setDetails(authentication.getDetails());
152 dataAccessor.audit(
153 username,
154 delegationKey,
155 Result.SUCCESS,
156 true,
157 authentication,
158 "Successfully authenticated, with entitlements: " + upat.getAuthorities());
159 return upat;
160 });
161
162 LOG.debug("User {} successfully authenticated, with entitlements {}", username, token.getAuthorities());
163 } else {
164 AuthContextUtils.callAsAdmin(domain, () -> {
165 dataAccessor.audit(
166 username,
167 delegationKey,
168 Result.FAILURE,
169 false,
170 authentication,
171 "Not authenticated");
172 return null;
173 });
174
175 LOG.debug("User {} not authenticated", username);
176
177 throw new BadCredentialsException("User " + username + " not authenticated");
178 }
179
180 return token;
181 }
182
183 @Override
184 public boolean supports(final Class<?> authentication) {
185 return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
186 }
187 }