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
.cms
.internal
.useradmin
;
18 import java
.util
.List
;
20 import javax
.jcr
.Node
;
21 import javax
.jcr
.RepositoryException
;
22 import javax
.jcr
.Session
;
23 import javax
.jcr
.Value
;
24 import javax
.jcr
.security
.Privilege
;
26 import org
.apache
.commons
.logging
.Log
;
27 import org
.apache
.commons
.logging
.LogFactory
;
28 import org
.apache
.jackrabbit
.core
.security
.user
.UserAccessControlProvider
;
29 import org
.argeo
.ArgeoException
;
30 import org
.argeo
.cms
.internal
.auth
.JcrSecurityModel
;
31 import org
.argeo
.jcr
.ArgeoJcrConstants
;
32 import org
.argeo
.jcr
.ArgeoNames
;
33 import org
.argeo
.jcr
.ArgeoTypes
;
34 import org
.argeo
.jcr
.JcrUtils
;
35 import org
.argeo
.jcr
.UserJcrUtils
;
38 * Manages data expected by the Argeo security model, such as user home and
41 public class SimpleJcrSecurityModel
implements JcrSecurityModel
{
42 private final static Log log
= LogFactory
43 .getLog(SimpleJcrSecurityModel
.class);
44 // ArgeoNames not implemented as interface in order to ease derivation by
47 /** The home base path. */
48 private String homeBasePath
= "/home";
49 private String peopleBasePath
= ArgeoJcrConstants
.PEOPLE_BASE_PATH
;
52 public void init(Session adminSession
) throws RepositoryException
{
53 JcrUtils
.mkdirs(adminSession
, homeBasePath
);
54 JcrUtils
.mkdirs(adminSession
, peopleBasePath
);
57 JcrUtils
.addPrivilege(adminSession
, homeBasePath
,
58 UserAccessControlProvider
.USER_ADMIN_GROUP_NAME
,
60 JcrUtils
.addPrivilege(adminSession
, peopleBasePath
,
61 UserAccessControlProvider
.USER_ADMIN_GROUP_NAME
,
65 public synchronized Node
sync(Session session
, String username
,
67 // TODO check user name validity (e.g. should not start by ROLE_)
70 Node userHome
= UserJcrUtils
.getUserHome(session
, username
);
71 if (userHome
== null) {
72 String homePath
= generateUserPath(homeBasePath
, username
);
73 userHome
= JcrUtils
.mkdirs(session
, homePath
);
74 // userHome = JcrUtils.mkfolders(session, homePath);
75 userHome
.addMixin(ArgeoTypes
.ARGEO_USER_HOME
);
76 userHome
.setProperty(ArgeoNames
.ARGEO_USER_ID
, username
);
79 JcrUtils
.clearAccessControList(session
, homePath
, username
);
80 JcrUtils
.addPrivilege(session
, homePath
, username
,
83 // for backward compatibility with pre 1.0 security model
84 if (userHome
.hasNode(ArgeoNames
.ARGEO_PROFILE
)) {
85 userHome
.getNode(ArgeoNames
.ARGEO_PROFILE
).remove();
86 userHome
.getSession().save();
92 // writeRemoteRoles(userHome, roles);
95 Node userProfile
= UserJcrUtils
.getUserProfile(session
, username
);
97 if (userProfile
== null) {
98 String personPath
= generateUserPath(peopleBasePath
, username
);
99 Node personBase
= JcrUtils
.mkdirs(session
, personPath
);
100 userProfile
= personBase
.addNode(ArgeoNames
.ARGEO_PROFILE
);
101 userProfile
.addMixin(ArgeoTypes
.ARGEO_USER_PROFILE
);
102 userProfile
.setProperty(ArgeoNames
.ARGEO_USER_ID
, username
);
103 userProfile
.setProperty(ArgeoNames
.ARGEO_ENABLED
, true);
104 userProfile
.setProperty(ArgeoNames
.ARGEO_ACCOUNT_NON_EXPIRED
,
106 userProfile
.setProperty(ArgeoNames
.ARGEO_ACCOUNT_NON_LOCKED
,
108 userProfile
.setProperty(
109 ArgeoNames
.ARGEO_CREDENTIALS_NON_EXPIRED
, true);
112 JcrUtils
.clearAccessControList(session
, userProfile
.getPath(),
114 JcrUtils
.addPrivilege(session
, userProfile
.getPath(), username
,
120 writeRemoteRoles(userProfile
, roles
);
123 } catch (RepositoryException e
) {
124 JcrUtils
.discardQuietly(session
);
125 throw new ArgeoException("Cannot sync node security model for "
130 /** Generate path for a new user home */
131 protected String
generateUserPath(String base
, String username
) {
132 int atIndex
= username
.indexOf('@');
134 String domain
= username
.substring(0, atIndex
);
135 String name
= username
.substring(atIndex
+ 1);
136 return base
+ '/' + JcrUtils
.firstCharsToPath(domain
, 2) + '/'
137 + domain
+ '/' + JcrUtils
.firstCharsToPath(name
, 2) + '/'
139 } else if (atIndex
== 0 || atIndex
== (username
.length() - 1)) {
140 throw new ArgeoException("Unsupported username " + username
);
142 return base
+ '/' + JcrUtils
.firstCharsToPath(username
, 2) + '/'
147 /** Write remote roles used by remote access in the home directory */
148 protected void writeRemoteRoles(Node userHome
, List
<String
> roles
)
149 throws RepositoryException
{
150 boolean writeRoles
= false;
151 if (userHome
.hasProperty(ArgeoNames
.ARGEO_REMOTE_ROLES
)) {
152 Value
[] remoteRoles
= userHome
.getProperty(
153 ArgeoNames
.ARGEO_REMOTE_ROLES
).getValues();
154 if (remoteRoles
.length
!= roles
.size())
157 for (int i
= 0; i
< remoteRoles
.length
; i
++)
158 if (!remoteRoles
[i
].getString().equals(roles
.get(i
)))
164 userHome
.getSession().getWorkspace().getVersionManager()
165 .checkout(userHome
.getPath());
166 String
[] roleIds
= roles
.toArray(new String
[roles
.size()]);
167 userHome
.setProperty(ArgeoNames
.ARGEO_REMOTE_ROLES
, roleIds
);
168 JcrUtils
.updateLastModified(userHome
);
169 userHome
.getSession().save();
170 userHome
.getSession().getWorkspace().getVersionManager()
171 .checkin(userHome
.getPath());
172 if (log
.isDebugEnabled())
173 log
.debug("Wrote remote roles " + roles
+ " for "
174 + userHome
.getProperty(ArgeoNames
.ARGEO_USER_ID
));
179 public void setHomeBasePath(String homeBasePath
) {
180 this.homeBasePath
= homeBasePath
;