1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.onehippo.forge.security.support.shiro.realm;
17
18 import java.text.MessageFormat;
19 import java.util.HashSet;
20 import java.util.Set;
21
22 import javax.jcr.Credentials;
23 import javax.jcr.LoginException;
24 import javax.jcr.Node;
25 import javax.jcr.NodeIterator;
26 import javax.jcr.Repository;
27 import javax.jcr.RepositoryException;
28 import javax.jcr.Session;
29 import javax.jcr.SimpleCredentials;
30 import javax.jcr.query.Query;
31 import javax.jcr.query.QueryResult;
32
33 import org.apache.shiro.authc.AccountException;
34 import org.apache.shiro.authc.AuthenticationException;
35 import org.apache.shiro.authc.AuthenticationInfo;
36 import org.apache.shiro.authc.AuthenticationToken;
37 import org.apache.shiro.authc.SimpleAuthenticationInfo;
38 import org.apache.shiro.authc.UnknownAccountException;
39 import org.apache.shiro.authc.UsernamePasswordToken;
40 import org.apache.shiro.authz.AuthorizationException;
41 import org.apache.shiro.authz.AuthorizationInfo;
42 import org.apache.shiro.authz.SimpleAuthorizationInfo;
43 import org.apache.shiro.realm.AuthorizingRealm;
44 import org.apache.shiro.subject.PrincipalCollection;
45 import org.hippoecm.hst.site.HstServices;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49
50
51
52
53
54
55 public class HippoRepositoryRealm extends AuthorizingRealm {
56
57 private static final Logger log = LoggerFactory.getLogger(HippoRepositoryRealm.class);
58
59 private static final String DEFAULT_USER_QUERY = "//hippo:configuration/hippo:users/{0}";
60
61 private static final String DEFAULT_GROUPS_OF_USER_QUERY = "//element(*, hipposys:group)[(@hipposys:members = ''{0}'' or @hipposys:members = ''*'') and @hipposys:securityprovider = ''internal'']";
62
63 private static final String DEFAULT_ROLES_OF_USER_AND_GROUP_QUERY = "//hippo:configuration/hippo:domains//element(*, hipposys:authrole)[ @hipposys:users = ''{0}'' {1} ]";
64
65 private Repository systemRepository;
66
67 private Credentials systemCreds;
68
69 private String queryLanguage = Query.XPATH;
70
71 private String userQuery = DEFAULT_USER_QUERY;
72
73 private String groupsOfUserQuery = DEFAULT_GROUPS_OF_USER_QUERY;
74
75 private String rolesOfUserAndGroupQuery = DEFAULT_ROLES_OF_USER_AND_GROUP_QUERY;
76
77 private String defaultRoleName;
78
79 private String rolePrefix;
80
81 private boolean permissionsLookupEnabled;
82
83 private String defaultPermission;
84
85 public void setSystemRepository(Repository systemRepository) {
86 this.systemRepository = systemRepository;
87 }
88
89 public Repository getSystemRepository() {
90 if (systemRepository == null) {
91 systemRepository = HstServices.getComponentManager().getComponent(Repository.class.getName());
92 }
93
94 return systemRepository;
95 }
96
97 public void setSystemCredentials(Credentials systemCreds) {
98 this.systemCreds = systemCreds;
99 }
100
101 public Credentials getSystemCredentials() {
102 if (systemCreds == null) {
103 systemCreds = HstServices.getComponentManager().getComponent(
104 Credentials.class.getName() + ".hstconfigreader");
105 }
106
107 return systemCreds;
108 }
109
110 public void setQueryLanguage(String queryLanguage) {
111 this.queryLanguage = queryLanguage;
112 }
113
114 public String getQueryLanguage() {
115 return queryLanguage;
116 }
117
118 public String getUserQuery() {
119 return userQuery;
120 }
121
122 public void setUserQuery(String userQuery) {
123 this.userQuery = userQuery;
124 }
125
126 public void setGroupsOfUserQuery(String groupsOfUserQuery) {
127 this.groupsOfUserQuery = groupsOfUserQuery;
128 }
129
130 public String getGroupsOfUserQuery() {
131 return groupsOfUserQuery;
132 }
133
134 public void setRolesOfUserAndGroupQuery(String rolesOfUserAndGroupQuery) {
135 this.rolesOfUserAndGroupQuery = rolesOfUserAndGroupQuery;
136 }
137
138 public String getRolesOfUserAndGroupQuery() {
139 return rolesOfUserAndGroupQuery;
140 }
141
142 public void setDefaultRoleName(String defaultRoleName) {
143 this.defaultRoleName = defaultRoleName;
144 }
145
146 public String getDefaultRoleName() {
147 return defaultRoleName;
148 }
149
150 public String getRolePrefix() {
151 return rolePrefix;
152 }
153
154 public void setRolePrefix(String rolePrefix) {
155 this.rolePrefix = rolePrefix;
156 }
157
158 public boolean isPermissionsLookupEnabled() {
159 return permissionsLookupEnabled;
160 }
161
162 public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) {
163 this.permissionsLookupEnabled = permissionsLookupEnabled;
164 }
165
166 public String getDefaultPermission() {
167 return defaultPermission;
168 }
169
170 public void setDefaultPermission(String defaultPermission) {
171 this.defaultPermission = defaultPermission;
172 }
173
174 @Override
175 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
176 Repository sysRepo = getSystemRepository();
177
178 if (sysRepo == null) {
179 throw new UnknownAccountException("Hippo Repository is not available now.");
180 }
181
182 UsernamePasswordToken upToken = (UsernamePasswordToken) token;
183 String username = upToken.getUsername();
184
185
186 if (username == null) {
187 throw new AccountException("Null usernames are not allowed by this realm.");
188 }
189
190 char [] passwordChars = upToken.getPassword();
191
192 SimpleAuthenticationInfo info = null;
193 Session session = null;
194
195 try {
196 session = sysRepo.login(new SimpleCredentials(username, passwordChars));
197 info = new SimpleAuthenticationInfo(username, passwordChars, getName());
198 } catch (LoginException e) {
199 throw new UnknownAccountException("No account found for user [" + username + "]", e);
200 } catch (RepositoryException e) {
201 throw new UnknownAccountException("No account found for user [" + username + "]", e);
202 } finally {
203 try {
204 session.logout();
205 } catch (Exception e) {
206 log.error("Failed to logout jcr session. {}", e);
207 }
208 }
209
210 return info;
211 }
212
213 @Override
214 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
215
216 if (principals == null) {
217 throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
218 }
219
220 String username = (String) getAvailablePrincipal(principals);
221
222 Set<String> roleNames = getRoleNames(username);
223 Set<String> permissions = null;
224
225 if (isPermissionsLookupEnabled()) {
226 permissions = getPermissions(username, roleNames);
227 }
228
229 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
230
231 if (permissions != null) {
232 info.setStringPermissions(permissions);
233 }
234
235 return info;
236 }
237
238 protected Set<String> getRoleNames(String username) throws AuthorizationException {
239 Set<String> roleNames = new HashSet<String>();
240 Session session = null;
241
242 try {
243 if (getSystemCredentials() != null) {
244 session = getSystemRepository().login(getSystemCredentials());
245 } else {
246 session = getSystemRepository().login();
247 }
248
249 String statement = MessageFormat.format(getGroupsOfUserQuery(), username);
250
251 if (log.isDebugEnabled()) {
252 log.debug("Searching groups of user with query: " + statement);
253 }
254
255 Query q = session.getWorkspace().getQueryManager().createQuery(statement, getQueryLanguage());
256 QueryResult result = q.execute();
257 NodeIterator nodeIt = result.getNodes();
258
259 boolean defaultRoleAdded = false;
260 Node node;
261
262 while (nodeIt.hasNext()) {
263 node = nodeIt.nextNode();
264 String roleName = node.getName();
265 String prefixedRoleName = (rolePrefix != null ? rolePrefix + roleName : roleName);
266 roleNames.add(prefixedRoleName);
267
268 if (defaultRoleName != null && !defaultRoleAdded && roleName.equals(defaultRoleName)) {
269 defaultRoleAdded = true;
270 }
271 }
272
273 if (defaultRoleName != null && !defaultRoleAdded) {
274 String prefixedRoleName = (rolePrefix != null ? rolePrefix + defaultRoleName : defaultRoleName);
275 roleNames.add(prefixedRoleName);
276 }
277 } catch (RepositoryException e) {
278 final String message = "There was a repository exception while authorizing user [" + username + "]";
279
280 if (log.isErrorEnabled()) {
281 log.error(message, e);
282 }
283
284
285 throw new AuthorizationException(message, e);
286 } finally {
287 if (session != null) {
288 try {
289 session.logout();
290 } catch (Exception e) {
291 log.error("Failed to logout jcr session. {}", e);
292 }
293 }
294 }
295
296 return roleNames;
297 }
298
299 protected Set<String> getPermissions(String username, Set<String> roleNames) throws AuthorizationException {
300 Set<String> permissions = new HashSet<String>();
301
302 Session session = null;
303
304 try {
305 if (getSystemCredentials() != null) {
306 session = getSystemRepository().login(getSystemCredentials());
307 } else {
308 session = getSystemRepository().login();
309 }
310
311 StringBuilder groupsConstraintsBuilder = new StringBuilder(100);
312
313 for (String roleName : roleNames) {
314 String groupName = roleName;
315 groupsConstraintsBuilder.append("or @hipposys:groups = '").append(groupName).append("' ");
316 }
317
318 String statement = MessageFormat.format(getRolesOfUserAndGroupQuery(), username,
319 groupsConstraintsBuilder.toString());
320
321 Query q = session.getWorkspace().getQueryManager().createQuery(statement, getQueryLanguage());
322 QueryResult result = q.execute();
323 NodeIterator nodeIt = result.getNodes();
324
325 Node node;
326 Node parentNode;
327
328 String domain;
329 String authority;
330 String permission;
331
332 boolean defaultPermissionAdded = false;
333
334 while (nodeIt.hasNext()) {
335 node = nodeIt.nextNode();
336 parentNode = node.getParent();
337
338 domain = parentNode.getName();
339 authority = node.getProperty("hipposys:role").getString();
340
341 permission = new StringBuilder(20).append(domain).append(':').append(authority).toString();
342 permissions.add(permission);
343
344 if (defaultPermission != null && !defaultPermissionAdded && defaultPermission.equals(permission)) {
345 defaultPermissionAdded = true;
346 }
347 }
348
349 if (!defaultPermissionAdded && defaultPermission != null) {
350 permissions.add(defaultPermission);
351 }
352 } catch (RepositoryException e) {
353 final String message = "There was a repository exception while authorizing user [" + username + "]";
354
355 if (log.isErrorEnabled()) {
356 log.error(message, e);
357 }
358
359
360 throw new AuthorizationException(message, e);
361 } finally {
362 if (session != null) {
363 try {
364 session.logout();
365 } catch (Exception e) {
366 log.error("Failed to logout jcr session. {}", e);
367 }
368 }
369 }
370
371 return permissions;
372 }
373 }