View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.kernel.security;
23  
24  import org.opencastproject.security.api.JaxbOrganization;
25  import org.opencastproject.security.api.JaxbRole;
26  import org.opencastproject.security.api.JaxbUser;
27  import org.opencastproject.security.api.Organization;
28  import org.opencastproject.security.api.SecurityService;
29  import org.opencastproject.security.api.User;
30  import org.opencastproject.security.api.UserDirectoryService;
31  import org.opencastproject.security.util.SecurityUtil;
32  
33  import org.osgi.service.component.annotations.Component;
34  import org.osgi.service.component.annotations.Reference;
35  import org.osgi.service.component.annotations.ReferenceCardinality;
36  import org.osgi.service.component.annotations.ReferencePolicy;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  import org.springframework.security.authentication.AnonymousAuthenticationToken;
40  import org.springframework.security.core.Authentication;
41  import org.springframework.security.core.GrantedAuthority;
42  import org.springframework.security.core.context.SecurityContextHolder;
43  import org.springframework.security.core.userdetails.UserDetails;
44  
45  import java.util.Collection;
46  import java.util.HashSet;
47  import java.util.Set;
48  
49  /**
50   * A Spring Security implementation of {@link SecurityService}.
51   */
52  @Component(
53    property = {
54      "service.description=Provides username and role information for the current user"
55    },
56    service = { SecurityService.class }
57  )
58  public class SecurityServiceSpringImpl implements SecurityService {
59  
60    /** The logger */
61    private static final Logger logger = LoggerFactory.getLogger(SecurityServiceSpringImpl.class);
62  
63    /** Holds delegates users for new threads that have been spawned from authenticated threads */
64    private static final ThreadLocal<User> delegatedUserHolder = new ThreadLocal<User>();
65  
66    /** Holds the IP address for the delegated user for the current thread */
67    private static final ThreadLocal<String> delegatedUserIPHolder = new ThreadLocal<String>();
68  
69    /** Holds organization responsible for the current thread */
70    private static final ThreadLocal<Organization> organization = new ThreadLocal<Organization>();
71  
72    /** The user directory */
73    private UserDirectoryService userDirectory;
74  
75    /**
76     * {@inheritDoc}
77     *
78     * @see org.opencastproject.security.api.SecurityService#getOrganization()
79     */
80    @Override
81    public Organization getOrganization() {
82      return SecurityServiceSpringImpl.organization.get();
83    }
84  
85    /**
86     * {@inheritDoc}
87     *
88     * @see org.opencastproject.security.api.SecurityService#setOrganization(Organization)
89     */
90    @Override
91    public void setOrganization(Organization organization) {
92      SecurityServiceSpringImpl.organization.set(organization);
93    }
94  
95    /**
96     * {@inheritDoc}
97     *
98     * @see org.opencastproject.security.api.SecurityService#getUser()
99     */
100   @Override
101   public User getUser() throws IllegalStateException {
102     Organization org = getOrganization();
103     if (org == null)
104       throw new IllegalStateException("No organization is set in security context");
105 
106     User delegatedUser = delegatedUserHolder.get();
107 
108     Authentication auth = SecurityContextHolder.getContext().getAuthentication();
109     if (auth instanceof AnonymousAuthenticationToken) {
110       return SecurityUtil.createAnonymousUser(org);
111     }
112 
113     if (delegatedUser != null) {
114       return delegatedUser;
115     }
116 
117     JaxbOrganization jaxbOrganization = JaxbOrganization.fromOrganization(org);
118     if (auth != null) {
119       Object principal = auth.getPrincipal();
120       if ((principal instanceof UserDetails)) {
121         UserDetails userDetails = (UserDetails) principal;
122 
123         User user = null;
124 
125         // If user exists, fetch it from the userDirectory
126         if (userDirectory != null) {
127           user = userDirectory.loadUser(userDetails.getUsername());
128           if (user == null) {
129             logger.debug("Authenticated user '{}' could not be found in any of the current UserProviders. "
130                 + "Continuing anyway...", userDetails.getUsername());
131           }
132         } else {
133           logger.debug("No UserDirectory was found when trying to search for user '{}'", userDetails.getUsername());
134         }
135 
136         // Add the roles (authorities) in the security context
137         Set<JaxbRole> roles = new HashSet<>();
138         Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
139         if (authorities != null) {
140           for (GrantedAuthority ga : authorities) {
141             roles.add(new JaxbRole(ga.getAuthority(), jaxbOrganization));
142           }
143         }
144 
145         if (user == null) {
146           // No user was found. Create one to hold the auth information from the security context
147           user = new JaxbUser(userDetails.getUsername(), null, jaxbOrganization, roles);
148         } else {
149           // Combine the existing user with the roles in the security context
150           user = JaxbUser.fromUser(user, roles);
151         }
152 
153         // Save the user to retrieve it quicker the next time(s) this method is called (by this thread)
154         delegatedUserHolder.set(user);
155 
156         return user;
157       }
158     }
159 
160     // Return the anonymous user by default
161     return SecurityUtil.createAnonymousUser(jaxbOrganization);
162   }
163 
164   /**
165    * {@inheritDoc}
166    *
167    * @see org.opencastproject.security.api.SecurityService#setUser(User)
168    */
169   @Override
170   public void setUser(User user) {
171     delegatedUserHolder.set(user);
172   }
173 
174   @Override
175   public String getUserIP() {
176     return delegatedUserIPHolder.get();
177   }
178 
179   @Override
180   public void setUserIP(String userIP) {
181     delegatedUserIPHolder.set(userIP);
182   }
183 
184   /**
185    * OSGi callback for setting the user directory.
186    *
187    * @param userDirectory
188    *          the user directory
189    */
190   @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, unbind = "removeUserDirectory")
191   void setUserDirectory(UserDirectoryService userDirectory) {
192     this.userDirectory = userDirectory;
193   }
194 
195   /**
196    * OSGi callback for removing the user directory.
197    */
198   void removeUserDirectory(UserDirectoryService unused) {
199     if (this.userDirectory == unused) {
200       this.userDirectory = null;
201     }
202   }
203 
204 }