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
;
45 /** Integrates Spring Security and Jackrabbit Security users and roles. */
46 public class ArgeoSecurityManager
extends DefaultSecurityManager
{
47 /** Legacy security sync */
48 final static String PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1
= "argeo.jackarabbit.securitySync.1.1";
50 private final static Log log
= LogFactory
51 .getLog(ArgeoSecurityManager
.class);
53 private static Boolean synchronize
= Boolean
.parseBoolean(System
54 .getProperty(PROPERTY_JACKRABBIT_SECURITY_SYNC_1_1
, "false"));
56 /** TODO? use a bounded buffer */
57 private Map
<String
, String
> userRolesCache
= Collections
58 .synchronizedMap(new HashMap
<String
, String
>());
61 public AccessManager
getAccessManager(Session session
, AMContext amContext
)
62 throws RepositoryException
{
63 synchronized (getSystemSession()) {
64 return super.getAccessManager(session
, amContext
);
69 public UserManager
getUserManager(Session session
)
70 throws RepositoryException
{
71 synchronized (getSystemSession()) {
72 return super.getUserManager(session
);
77 * Since this is called once when the session is created, we take the
78 * opportunity to make sure that Jackrabbit users and groups reflect Spring
79 * Security name and authorities.
82 public String
getUserID(Subject subject
, String workspaceName
)
83 throws RepositoryException
{
85 return super.getUserID(subject
, workspaceName
);
87 if (log
.isTraceEnabled())
89 // skip anonymous user (no rights)
90 if (!subject
.getPrincipals(AnonymousPrincipal
.class).isEmpty())
91 return super.getUserID(subject
, workspaceName
);
92 // skip Jackrabbit system user (all rights)
93 if (!subject
.getPrincipals(ArgeoSystemPrincipal
.class).isEmpty())
94 return super.getUserID(subject
, workspaceName
);
96 // retrieve Spring authentication from JAAS
97 // TODO? use Spring Security context holder
98 Authentication authen
;
99 Set
<Authentication
> authens
= subject
100 .getPrincipals(Authentication
.class);
101 String userId
= super.getUserID(subject
, workspaceName
);
102 if (authens
.size() == 0) {
103 // make sure that logged-in user has a Principal, useful for testing
104 // using an admin user
105 UserManager systemUm
= getSystemUserManager(null);
106 if (systemUm
.getAuthorizable(userId
) == null)
107 systemUm
.createUser(userId
, "");
108 } else {// Spring Security
109 authen
= authens
.iterator().next();
111 if (!userId
.equals(authen
.getName()))
112 log
.warn("User ID is '" + userId
+ "' but authen is "
114 StringBuffer roles
= new StringBuffer("");
115 GrantedAuthority
[] authorities
= authen
.getAuthorities();
116 for (GrantedAuthority ga
: authorities
) {
117 roles
.append(ga
.toString());
120 // do not sync if not changed
121 if (userRolesCache
.containsKey(userId
)
122 && userRolesCache
.get(userId
).equals(roles
.toString()))
125 // sync Spring and Jackrabbit
126 // workspace is irrelevant here
127 UserManager systemUm
= getSystemUserManager(null);
128 syncSpringAndJackrabbitSecurity(systemUm
, authen
);
129 userRolesCache
.put(userId
, roles
.toString());
135 * Make sure that the Jackrabbit security model contains this user and its
136 * granted authorities
138 static private void syncSpringAndJackrabbitSecurity(UserManager systemUm
,
139 Authentication authen
) throws RepositoryException
{
140 long begin
= System
.currentTimeMillis();
142 String userId
= authen
.getName();
143 User user
= (User
) systemUm
.getAuthorizable(userId
);
145 user
= systemUm
.createUser(userId
, authen
.getCredentials()
146 .toString(), authen
, null);
147 log
.info(userId
+ " added as " + user
);
151 List
<String
> userGroupIds
= new ArrayList
<String
>();
152 for (GrantedAuthority ga
: authen
.getAuthorities()) {
153 Group group
= (Group
) systemUm
.getAuthorizable(ga
.getAuthority());
155 group
= systemUm
.createGroup(ga
.getAuthority());
156 log
.info(ga
.getAuthority() + " added as " + group
);
158 if (!group
.isMember(user
))
159 group
.addMember(user
);
160 userGroupIds
.add(ga
.getAuthority());
163 // check if user has not been removed from some groups
164 for (Iterator
<Group
> it
= user
.declaredMemberOf(); it
.hasNext();) {
165 Group group
= it
.next();
166 if (!userGroupIds
.contains(group
.getID()))
167 group
.removeMember(user
);
170 if (log
.isTraceEnabled())
171 log
.trace("Spring and Jackrabbit Security synchronized for user "
172 + userId
+ " in " + (System
.currentTimeMillis() - begin
)
177 protected WorkspaceAccessManager
createDefaultWorkspaceAccessManager() {
178 WorkspaceAccessManager wam
= super
179 .createDefaultWorkspaceAccessManager();
180 return new ArgeoWorkspaceAccessManagerImpl(wam
);
183 private class ArgeoWorkspaceAccessManagerImpl
implements SecurityConstants
,
184 WorkspaceAccessManager
{
185 private final WorkspaceAccessManager wam
;
187 public ArgeoWorkspaceAccessManagerImpl(WorkspaceAccessManager wam
) {
192 public void init(Session systemSession
) throws RepositoryException
{
193 wam
.init(systemSession
);
196 public void close() throws RepositoryException
{
199 public boolean grants(Set
<Principal
> principals
, String workspaceName
)
200 throws RepositoryException
{
201 // TODO: implements finer access to workspaces