2 * Copyright (C) 2007-2012 Argeo GmbH
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
.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
;
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";
51 private final static Log log
= LogFactory
52 .getLog(ArgeoSecurityManager
.class);
54 private static Boolean synchronize
= Boolean
.parseBoolean(System
55 .getProperty(PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1
, "false"));
57 /** TODO? use a bounded buffer */
58 private Map
<String
, String
> userRolesCache
= Collections
59 .synchronizedMap(new HashMap
<String
, String
>());
62 public AccessManager
getAccessManager(Session session
, AMContext amContext
)
63 throws RepositoryException
{
64 synchronized (getSystemSession()) {
65 return super.getAccessManager(session
, amContext
);
70 public UserManager
getUserManager(Session session
)
71 throws RepositoryException
{
72 synchronized (getSystemSession()) {
73 return super.getUserManager(session
);
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.
83 public String
getUserID(Subject subject
, String workspaceName
)
84 throws RepositoryException
{
86 Authentication authentication
= SecurityContextHolder
.getContext()
88 if (authentication
!= null)
89 return authentication
.getName();
91 return super.getUserID(subject
, workspaceName
);
94 if (log
.isTraceEnabled())
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
);
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();
118 if (!userId
.equals(authen
.getName()))
119 log
.warn("User ID is '" + userId
+ "' but authen is "
121 StringBuffer roles
= new StringBuffer("");
122 GrantedAuthority
[] authorities
= authen
.getAuthorities();
123 for (GrantedAuthority ga
: authorities
) {
124 roles
.append(ga
.toString());
127 // do not sync if not changed
128 if (userRolesCache
.containsKey(userId
)
129 && userRolesCache
.get(userId
).equals(roles
.toString()))
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());
142 * Make sure that the Jackrabbit security model contains this user and its
143 * granted authorities
145 static private void syncSpringAndJackrabbitSecurity(UserManager systemUm
,
146 Authentication authen
) throws RepositoryException
{
147 long begin
= System
.currentTimeMillis();
149 String userId
= authen
.getName();
150 User user
= (User
) systemUm
.getAuthorizable(userId
);
152 user
= systemUm
.createUser(userId
, authen
.getCredentials()
153 .toString(), authen
, null);
154 log
.info(userId
+ " added as " + user
);
158 List
<String
> userGroupIds
= new ArrayList
<String
>();
159 for (GrantedAuthority ga
: authen
.getAuthorities()) {
160 Group group
= (Group
) systemUm
.getAuthorizable(ga
.getAuthority());
162 group
= systemUm
.createGroup(ga
.getAuthority());
163 log
.info(ga
.getAuthority() + " added as " + group
);
165 if (!group
.isMember(user
))
166 group
.addMember(user
);
167 userGroupIds
.add(ga
.getAuthority());
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
);
177 if (log
.isTraceEnabled())
178 log
.trace("Spring and Jackrabbit Security synchronized for user "
179 + userId
+ " in " + (System
.currentTimeMillis() - begin
)
184 protected WorkspaceAccessManager
createDefaultWorkspaceAccessManager() {
185 WorkspaceAccessManager wam
= super
186 .createDefaultWorkspaceAccessManager();
187 return new ArgeoWorkspaceAccessManagerImpl(wam
);
190 private class ArgeoWorkspaceAccessManagerImpl
implements SecurityConstants
,
191 WorkspaceAccessManager
{
192 private final WorkspaceAccessManager wam
;
194 public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam
) {
199 public void init(Session systemSession
) throws RepositoryException
{
200 wam
.init(systemSession
);
203 public void close() throws RepositoryException
{
206 public boolean grants(Set
<Principal
> principals
, String workspaceName
)
207 throws RepositoryException
{
208 // TODO: implements finer access to workspaces