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.Collection;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Optional;
25  import java.util.Set;
26  import java.util.concurrent.Callable;
27  import java.util.stream.Collectors;
28  import org.apache.commons.lang3.StringUtils;
29  import org.apache.syncope.common.lib.SyncopeConstants;
30  import org.apache.syncope.common.lib.types.EntitlementsHolder;
31  import org.apache.syncope.core.persistence.api.dao.UserDAO;
32  import org.apache.syncope.core.spring.ApplicationContextProvider;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
36  import org.springframework.security.core.Authentication;
37  import org.springframework.security.core.GrantedAuthority;
38  import org.springframework.security.core.context.SecurityContextHolder;
39  import org.springframework.security.core.userdetails.User;
40  
41  public final class AuthContextUtils {
42  
43      private static final Logger LOG = LoggerFactory.getLogger(AuthContextUtils.class);
44  
45      private static final String PLACEHOLDER_PWD = "PLACEHOLDER_PWD";
46  
47      private static final String UNAUTHENTICATED = "unauthenticated";
48  
49      public static String getUsername() {
50          Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
51          return authentication == null ? UNAUTHENTICATED : authentication.getName();
52      }
53  
54      public static void updateUsername(final String newUsername) {
55          Authentication auth = SecurityContextHolder.getContext().getAuthentication();
56  
57          UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(
58                  new User(newUsername, PLACEHOLDER_PWD, auth.getAuthorities()),
59                  auth.getCredentials(), auth.getAuthorities());
60          newAuth.setDetails(auth.getDetails());
61          SecurityContextHolder.getContext().setAuthentication(newAuth);
62      }
63  
64      public static Optional<String> getDelegatedBy() {
65          Authentication auth = SecurityContextHolder.getContext().getAuthentication();
66  
67          return auth != null && auth.getDetails() instanceof SyncopeAuthenticationDetails
68                  ? Optional.ofNullable(SyncopeAuthenticationDetails.class.cast(auth.getDetails()).getDelegatedBy())
69                  : Optional.empty();
70      }
71  
72      public static String getWho() {
73          return getUsername() + getDelegatedBy().map(d -> {
74              String delegatedBy = callAsAdmin(getDomain(),
75                      () -> ApplicationContextProvider.getApplicationContext().getBean(UserDAO.class).findUsername(d)).
76                      orElse(d);
77              return " [delegated by " + delegatedBy + "]";
78          }).orElse(StringUtils.EMPTY);
79      }
80  
81      public static Set<SyncopeGrantedAuthority> getAuthorities() {
82          return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).
83                  map(authentication -> authentication.getAuthorities().stream().
84                  filter(SyncopeGrantedAuthority.class::isInstance).
85                  map(SyncopeGrantedAuthority.class::cast).
86                  collect(Collectors.toSet())).
87                  orElse(Set.of());
88      }
89  
90      public static Map<String, Set<String>> getAuthorizations() {
91          return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).
92                  map(authentication -> authentication.getAuthorities().stream().
93                  filter(SyncopeGrantedAuthority.class::isInstance).
94                  map(SyncopeGrantedAuthority.class::cast).
95                  collect(Collectors.toMap(SyncopeGrantedAuthority::getAuthority, SyncopeGrantedAuthority::getRealms))).
96                  orElse(Map.of());
97      }
98  
99      public static String getDomain() {
100         Authentication auth = SecurityContextHolder.getContext().getAuthentication();
101 
102         String domainKey = auth != null && auth.getDetails() instanceof SyncopeAuthenticationDetails
103                 ? SyncopeAuthenticationDetails.class.cast(auth.getDetails()).getDomain()
104                 : null;
105         if (StringUtils.isBlank(domainKey)) {
106             domainKey = SyncopeConstants.MASTER_DOMAIN;
107         }
108 
109         return domainKey;
110     }
111 
112     private static <T> T call(final Authentication authentication, final Callable<T> callable) {
113         Authentication original = SecurityContextHolder.getContext().getAuthentication();
114         SecurityContextHolder.getContext().setAuthentication(authentication);
115         try {
116             return callable.call();
117         } catch (Exception e) {
118             LOG.debug("Error during execution under authentication {}", authentication, e);
119             if (e instanceof RuntimeException) {
120                 throw (RuntimeException) e;
121             } else {
122                 throw new RuntimeException(e);
123             }
124         } finally {
125             SecurityContextHolder.getContext().setAuthentication(original);
126         }
127     }
128 
129     public static <T> T callAs(
130             final String domain,
131             final String username,
132             final Collection<String> entitlements,
133             final Callable<T> callable) {
134 
135         List<GrantedAuthority> authorities = entitlements.stream().
136                 map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)).
137                 collect(Collectors.toList());
138         UsernamePasswordAuthenticationToken asAuth = new UsernamePasswordAuthenticationToken(
139                 new User(username, PLACEHOLDER_PWD, authorities), PLACEHOLDER_PWD, authorities);
140         asAuth.setDetails(new SyncopeAuthenticationDetails(domain, getDelegatedBy().orElse(null)));
141 
142         return call(asAuth, callable);
143     }
144 
145     public static <T> T callAsAdmin(final String domain, final Callable<T> callable) {
146         SecurityProperties properties = ApplicationContextProvider.getBeanFactory().getBean(SecurityProperties.class);
147         return callAs(
148                 domain,
149                 properties.getAdminUser(),
150                 EntitlementsHolder.getInstance().getValues(),
151                 callable);
152     }
153 
154     /**
155      * Private default constructor, for static-only classes.
156      */
157     private AuthContextUtils() {
158     }
159 }