]> git.argeo.org Git - lgpl/argeo-commons.git/blob - security/jackrabbit/ArgeoSecurityManager.java
Prepare next development cycle
[lgpl/argeo-commons.git] / security / jackrabbit / ArgeoSecurityManager.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.argeo.security.jackrabbit;
17
18 import java.security.Principal;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import javax.jcr.RepositoryException;
28 import javax.jcr.Session;
29 import javax.security.auth.Subject;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.jackrabbit.api.security.user.Group;
34 import org.apache.jackrabbit.api.security.user.User;
35 import org.apache.jackrabbit.api.security.user.UserManager;
36 import org.apache.jackrabbit.core.DefaultSecurityManager;
37 import org.apache.jackrabbit.core.security.AMContext;
38 import org.apache.jackrabbit.core.security.AccessManager;
39 import org.apache.jackrabbit.core.security.AnonymousPrincipal;
40 import org.apache.jackrabbit.core.security.SecurityConstants;
41 import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager;
42 import org.springframework.security.Authentication;
43 import org.springframework.security.GrantedAuthority;
44 import org.springframework.security.context.SecurityContextHolder;
45
46 /** Integrates Spring Security and Jackrabbit Security users and roles. */
47 public class ArgeoSecurityManager extends DefaultSecurityManager {
48 /** Legacy security sync */
49 final static String PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1 = "argeo.jackarabbit.securitySync.1.1";
50
51 private final static Log log = LogFactory
52 .getLog(ArgeoSecurityManager.class);
53
54 private static Boolean synchronize = Boolean.parseBoolean(System
55 .getProperty(PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1, "false"));
56
57 /** TODO? use a bounded buffer */
58 private Map<String, String> userRolesCache = Collections
59 .synchronizedMap(new HashMap<String, String>());
60
61 @Override
62 public AccessManager getAccessManager(Session session, AMContext amContext)
63 throws RepositoryException {
64 synchronized (getSystemSession()) {
65 return super.getAccessManager(session, amContext);
66 }
67 }
68
69 @Override
70 public UserManager getUserManager(Session session)
71 throws RepositoryException {
72 synchronized (getSystemSession()) {
73 return super.getUserManager(session);
74 }
75 }
76
77 /**
78 * Since this is called once when the session is created, we take the
79 * opportunity to make sure that Jackrabbit users and groups reflect Spring
80 * Security name and authorities.
81 */
82 @Override
83 public String getUserID(Subject subject, String workspaceName)
84 throws RepositoryException {
85 if (!synchronize) {
86 Authentication authentication = SecurityContextHolder.getContext()
87 .getAuthentication();
88 if (authentication != null)
89 return authentication.getName();
90 else
91 return super.getUserID(subject, workspaceName);
92 }
93
94 if (log.isTraceEnabled())
95 log.trace(subject);
96 // skip anonymous user (no rights)
97 if (!subject.getPrincipals(AnonymousPrincipal.class).isEmpty())
98 return super.getUserID(subject, workspaceName);
99 // skip Jackrabbit system user (all rights)
100 if (!subject.getPrincipals(ArgeoSystemPrincipal.class).isEmpty())
101 return super.getUserID(subject, workspaceName);
102
103 // retrieve Spring authentication from JAAS
104 // TODO? use Spring Security context holder
105 Authentication authen;
106 Set<Authentication> authens = subject
107 .getPrincipals(Authentication.class);
108 String userId = super.getUserID(subject, workspaceName);
109 if (authens.size() == 0) {
110 // make sure that logged-in user has a Principal, useful for testing
111 // using an admin user
112 UserManager systemUm = getSystemUserManager(null);
113 if (systemUm.getAuthorizable(userId) == null)
114 systemUm.createUser(userId, "");
115 } else {// Spring Security
116 authen = authens.iterator().next();
117
118 if (!userId.equals(authen.getName()))
119 log.warn("User ID is '" + userId + "' but authen is "
120 + authen.getName());
121 StringBuffer roles = new StringBuffer("");
122 GrantedAuthority[] authorities = authen.getAuthorities();
123 for (GrantedAuthority ga : authorities) {
124 roles.append(ga.toString());
125 }
126
127 // do not sync if not changed
128 if (userRolesCache.containsKey(userId)
129 && userRolesCache.get(userId).equals(roles.toString()))
130 return userId;
131
132 // sync Spring and Jackrabbit
133 // workspace is irrelevant here
134 UserManager systemUm = getSystemUserManager(null);
135 syncSpringAndJackrabbitSecurity(systemUm, authen);
136 userRolesCache.put(userId, roles.toString());
137 }
138 return userId;
139 }
140
141 /**
142 * Make sure that the Jackrabbit security model contains this user and its
143 * granted authorities
144 */
145 static private void syncSpringAndJackrabbitSecurity(UserManager systemUm,
146 Authentication authen) throws RepositoryException {
147 long begin = System.currentTimeMillis();
148
149 String userId = authen.getName();
150 User user = (User) systemUm.getAuthorizable(userId);
151 if (user == null) {
152 user = systemUm.createUser(userId, authen.getCredentials()
153 .toString(), authen, null);
154 log.info(userId + " added as " + user);
155 }
156
157 // process groups
158 List<String> userGroupIds = new ArrayList<String>();
159 for (GrantedAuthority ga : authen.getAuthorities()) {
160 Group group = (Group) systemUm.getAuthorizable(ga.getAuthority());
161 if (group == null) {
162 group = systemUm.createGroup(ga.getAuthority());
163 log.info(ga.getAuthority() + " added as " + group);
164 }
165 if (!group.isMember(user))
166 group.addMember(user);
167 userGroupIds.add(ga.getAuthority());
168 }
169
170 // check if user has not been removed from some groups
171 for (Iterator<Group> it = user.declaredMemberOf(); it.hasNext();) {
172 Group group = it.next();
173 if (!userGroupIds.contains(group.getID()))
174 group.removeMember(user);
175 }
176
177 if (log.isTraceEnabled())
178 log.trace("Spring and Jackrabbit Security synchronized for user "
179 + userId + " in " + (System.currentTimeMillis() - begin)
180 + " ms");
181 }
182
183 @Override
184 protected WorkspaceAccessManager createDefaultWorkspaceAccessManager() {
185 WorkspaceAccessManager wam = super
186 .createDefaultWorkspaceAccessManager();
187 return new ArgeoWorkspaceAccessManagerImpl(wam);
188 }
189
190 private class ArgeoWorkspaceAccessManagerImpl implements SecurityConstants,
191 WorkspaceAccessManager {
192 private final WorkspaceAccessManager wam;
193
194 public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam) {
195 super();
196 this.wam = wam;
197 }
198
199 public void init(Session systemSession) throws RepositoryException {
200 wam.init(systemSession);
201 }
202
203 public void close() throws RepositoryException {
204 }
205
206 public boolean grants(Set<Principal> principals, String workspaceName)
207 throws RepositoryException {
208 // TODO: implements finer access to workspaces
209 return true;
210 }
211 }
212
213 }