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