2 * Copyright (C) 2007-2012 Mathieu Baudier
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org
.argeo
.security
.jackrabbit
;
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
;
27 import javax
.jcr
.RepositoryException
;
28 import javax
.jcr
.Session
;
29 import javax
.security
.auth
.Subject
;
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
.AnonymousPrincipal
;
38 import org
.apache
.jackrabbit
.core
.security
.SecurityConstants
;
39 import org
.apache
.jackrabbit
.core
.security
.authorization
.WorkspaceAccessManager
;
40 import org
.argeo
.ArgeoException
;
41 import org
.springframework
.security
.Authentication
;
42 import org
.springframework
.security
.GrantedAuthority
;
44 /** Integrates Spring Security and Jackrabbit Security user and roles. */
45 public class ArgeoSecurityManager
extends DefaultSecurityManager
{
46 private final static Log log
= LogFactory
47 .getLog(ArgeoSecurityManager
.class);
49 /** TODO? use a bounded buffer */
50 private Map
<String
, String
> userRolesCache
= Collections
51 .synchronizedMap(new HashMap
<String
, String
>());
54 * Since this is called once when the session is created, we take the
55 * opportunity to make sure that Jackrabbit users and groups reflect Spring
56 * Security name and authorities.
59 public String
getUserID(Subject subject
, String workspaceName
)
60 throws RepositoryException
{
61 if (log
.isTraceEnabled())
63 // skip anonymous user (no rights)
64 if (!subject
.getPrincipals(AnonymousPrincipal
.class).isEmpty())
65 return super.getUserID(subject
, workspaceName
);
66 // skip Jackrabbit system user (all rights)
67 if (!subject
.getPrincipals(ArgeoSystemPrincipal
.class).isEmpty())
68 return super.getUserID(subject
, workspaceName
);
70 // retrieve Spring authentication from JAAS
71 // TODO? use Spring Security context holder
72 Authentication authen
;
73 Set
<Authentication
> authens
= subject
74 .getPrincipals(Authentication
.class);
75 if (authens
.size() == 0)
76 throw new ArgeoException("No Spring authentication found in "
79 authen
= authens
.iterator().next();
81 String userId
= authen
.getName();
82 StringBuffer roles
= new StringBuffer("");
83 GrantedAuthority
[] authorities
= authen
.getAuthorities();
84 for (GrantedAuthority ga
: authorities
) {
85 roles
.append(ga
.toString());
88 // do not sync if not changed
89 if (userRolesCache
.containsKey(userId
)
90 && userRolesCache
.get(userId
).equals(roles
.toString()))
93 // sync Spring and Jackrabbit
94 // workspace is irrelevant here
95 UserManager systemUm
= getSystemUserManager(null);
96 syncSpringAndJackrabbitSecurity(systemUm
, authen
);
97 userRolesCache
.put(userId
, roles
.toString());
103 * Make sure that the Jackrabbit security model contains this user and its
104 * granted authorities
106 static void syncSpringAndJackrabbitSecurity(UserManager systemUm
,
107 Authentication authen
) throws RepositoryException
{
108 long begin
= System
.currentTimeMillis();
110 String userId
= authen
.getName();
111 User user
= (User
) systemUm
.getAuthorizable(userId
);
113 user
= systemUm
.createUser(userId
, authen
.getCredentials()
114 .toString(), authen
, null);
115 // SecurityJcrUtils.createUserHomeIfNeeded(getSystemSession(),
117 // getSystemSession().save();
118 // setSecurityHomeAuthorizations(user);
119 log
.info(userId
+ " added as " + user
);
123 List
<String
> userGroupIds
= new ArrayList
<String
>();
124 for (GrantedAuthority ga
: authen
.getAuthorities()) {
125 Group group
= (Group
) systemUm
.getAuthorizable(ga
.getAuthority());
127 group
= systemUm
.createGroup(ga
.getAuthority());
128 log
.info(ga
.getAuthority() + " added as " + group
);
130 if (!group
.isMember(user
))
131 group
.addMember(user
);
132 userGroupIds
.add(ga
.getAuthority());
135 // check if user has not been removed from some groups
136 for (Iterator
<Group
> it
= user
.declaredMemberOf(); it
.hasNext();) {
137 Group group
= it
.next();
138 if (!userGroupIds
.contains(group
.getID()))
139 group
.removeMember(user
);
142 if (log
.isTraceEnabled())
143 log
.trace("Spring and Jackrabbit Security synchronized for user "
144 + userId
+ " in " + (System
.currentTimeMillis() - begin
)
148 // protected synchronized void setSecurityHomeAuthorizations(User user) {
149 // // give read privileges on user security home
150 // String userId = "<not yet set>";
152 // userId = user.getID();
153 // Node userHome = SecurityJcrUtils.getUserHome(getSystemSession(), userId);
154 // if (userHome == null)
155 // throw new ArgeoException("No security home available for user "
158 // String path = userHome.getPath();
159 // Principal principal = user.getPrincipal();
161 // JackrabbitAccessControlManager acm = (JackrabbitAccessControlManager)
162 // getSystemSession()
163 // .getAccessControlManager();
164 // JackrabbitAccessControlPolicy[] ps = acm
165 // .getApplicablePolicies(principal);
166 // if (ps.length == 0) {
167 // // log.warn("No ACL found for " + user);
171 // JackrabbitAccessControlList list = (JackrabbitAccessControlList) ps[0];
174 // Privilege[] privileges = new Privilege[] { acm
175 // .privilegeFromName(Privilege.JCR_READ) };
176 // Map<String, Value> restrictions = new HashMap<String, Value>();
177 // ValueFactory vf = getSystemSession().getValueFactory();
178 // restrictions.put("rep:nodePath",
179 // vf.createValue(path, PropertyType.PATH));
180 // restrictions.put("rep:glob", vf.createValue("*"));
181 // list.addEntry(principal, privileges, true /* allow or deny */,
183 // } catch (Exception e) {
184 // e.printStackTrace();
185 // throw new ArgeoException(
186 // "Cannot set authorization on security home for " + userId
187 // + ": " + e.getMessage());
193 protected WorkspaceAccessManager
createDefaultWorkspaceAccessManager() {
194 WorkspaceAccessManager wam
= super
195 .createDefaultWorkspaceAccessManager();
196 return new ArgeoWorkspaceAccessManagerImpl(wam
);
199 private class ArgeoWorkspaceAccessManagerImpl
implements SecurityConstants
,
200 WorkspaceAccessManager
{
201 private final WorkspaceAccessManager wam
;
203 // private String defaultWorkspace;
205 public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam
) {
210 public void init(Session systemSession
) throws RepositoryException
{
211 wam
.init(systemSession
);
212 // defaultWorkspace = ((RepositoryImpl) getRepository()).getConfig()
213 // .getDefaultWorkspaceName();
216 public void close() throws RepositoryException
{
219 public boolean grants(Set
<Principal
> principals
, String workspaceName
)
220 throws RepositoryException
{
221 // everybody has access to all workspaces
222 // TODO: implements finer access to workspaces
225 // anonymous has access to the default workspace (required for
226 // remoting which does a default login when initializing the
228 // Boolean anonymous = false;
229 // for (Principal principal : principals)
230 // if (principal instanceof AnonymousPrincipal)
233 // if (anonymous && workspaceName.equals(defaultWorkspace))
236 // return wam.grants(principals, workspaceName);