View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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 }