View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
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
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
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.
15   */
16  package org.argeo.api;
17  
18  import java.security.PrivilegedAction;
19  import java.util.HashMap;
20  import java.util.Map;
21  
22  import javax.jcr.NoSuchWorkspaceException;
23  import javax.jcr.Node;
24  import javax.jcr.Repository;
25  import javax.jcr.RepositoryException;
26  import javax.jcr.RepositoryFactory;
27  import javax.jcr.Session;
28  import javax.naming.InvalidNameException;
29  import javax.naming.ldap.LdapName;
30  import javax.security.auth.AuthPermission;
31  import javax.security.auth.Subject;
32  import javax.security.auth.login.LoginContext;
33  import javax.security.auth.login.LoginException;
34  
35  /** Utilities related to Argeo model in JCR */
36  public class NodeUtils {
37  	/**
38  	 * Wraps the call to the repository factory based on parameter
39  	 * {@link NodeConstants#CN} in order to simplify it and protect against future
40  	 * API changes.
41  	 */
42  	public static Repository getRepositoryByAlias(RepositoryFactory repositoryFactory, String alias) {
43  		try {
44  			Map<String, String> parameters = new HashMap<String, String>();
45  			parameters.put(NodeConstants.CN, alias);
46  			return repositoryFactory.getRepository(parameters);
47  		} catch (RepositoryException e) {
48  			throw new RuntimeException("Unexpected exception when trying to retrieve repository with alias " + alias,
49  					e);
50  		}
51  	}
52  
53  	/**
54  	 * Wraps the call to the repository factory based on parameter
55  	 * {@link NodeConstants#LABELED_URI} in order to simplify it and protect against
56  	 * future API changes.
57  	 */
58  	public static Repository getRepositoryByUri(RepositoryFactory repositoryFactory, String uri) {
59  		return getRepositoryByUri(repositoryFactory, uri, null);
60  	}
61  
62  	/**
63  	 * Wraps the call to the repository factory based on parameter
64  	 * {@link NodeConstants#LABELED_URI} in order to simplify it and protect against
65  	 * future API changes.
66  	 */
67  	public static Repository getRepositoryByUri(RepositoryFactory repositoryFactory, String uri, String alias) {
68  		try {
69  			Map<String, String> parameters = new HashMap<String, String>();
70  			parameters.put(NodeConstants.LABELED_URI, uri);
71  			if (alias != null)
72  				parameters.put(NodeConstants.CN, alias);
73  			return repositoryFactory.getRepository(parameters);
74  		} catch (RepositoryException e) {
75  			throw new RuntimeException("Unexpected exception when trying to retrieve repository with uri " + uri, e);
76  		}
77  	}
78  
79  	/**
80  	 * Returns the home node of the user or null if none was found.
81  	 * 
82  	 * @param session  the session to use in order to perform the search, this can
83  	 *                 be a session with a different user ID than the one searched,
84  	 *                 typically when a system or admin session is used.
85  	 * @param username the username of the user
86  	 */
87  	public static Node getUserHome(Session session, String username) {
88  //		try {
89  //			QueryObjectModelFactory qomf = session.getWorkspace().getQueryManager().getQOMFactory();
90  //			Selector sel = qomf.selector(NodeTypes.NODE_USER_HOME, "sel");
91  //			DynamicOperand dop = qomf.propertyValue(sel.getSelectorName(), NodeNames.LDAP_UID);
92  //			StaticOperand sop = qomf.literal(session.getValueFactory().createValue(username));
93  //			Constraint constraint = qomf.comparison(dop, QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, sop);
94  //			Query query = qomf.createQuery(sel, constraint, null, null);
95  //			return querySingleNode(query);
96  //		} catch (RepositoryException e) {
97  //			throw new RuntimeException("Cannot find home for user " + username, e);
98  //		}
99  
100 		try {
101 			checkUserWorkspace(session, username);
102 			String homePath = getHomePath(username);
103 			if (session.itemExists(homePath))
104 				return session.getNode(homePath);
105 			// legacy
106 			homePath = "/home/" + username;
107 			if (session.itemExists(homePath))
108 				return session.getNode(homePath);
109 			return null;
110 		} catch (RepositoryException e) {
111 			throw new RuntimeException("Cannot find home for user " + username, e);
112 		}
113 	}
114 
115 	private static String getHomePath(String username) {
116 		LdapName dn;
117 		try {
118 			dn = new LdapName(username);
119 		} catch (InvalidNameException e) {
120 			throw new IllegalArgumentException("Invalid name " + username, e);
121 		}
122 		String userId = dn.getRdn(dn.size() - 1).getValue().toString();
123 		return '/' + userId;
124 	}
125 
126 	private static void checkUserWorkspace(Session session, String username) {
127 		String workspaceName = session.getWorkspace().getName();
128 		if (!NodeConstants.HOME_WORKSPACE.equals(workspaceName))
129 			throw new IllegalArgumentException(workspaceName + " is not the home workspace for user " + username);
130 	}
131 
132 	/**
133 	 * Returns the home node of the user or null if none was found.
134 	 * 
135 	 * @param session   the session to use in order to perform the search, this can
136 	 *                  be a session with a different user ID than the one searched,
137 	 *                  typically when a system or admin session is used.
138 	 * @param groupname the name of the group
139 	 */
140 	public static Node getGroupHome(Session session, String groupname) {
141 //		try {
142 //			QueryObjectModelFactory qomf = session.getWorkspace().getQueryManager().getQOMFactory();
143 //			Selector sel = qomf.selector(NodeTypes.NODE_GROUP_HOME, "sel");
144 //			DynamicOperand dop = qomf.propertyValue(sel.getSelectorName(), NodeNames.LDAP_CN);
145 //			StaticOperand sop = qomf.literal(session.getValueFactory().createValue(cn));
146 //			Constraint constraint = qomf.comparison(dop, QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, sop);
147 //			Query query = qomf.createQuery(sel, constraint, null, null);
148 //			return querySingleNode(query);
149 //		} catch (RepositoryException e) {
150 //			throw new RuntimeException("Cannot find home for group " + cn, e);
151 //		}
152 
153 		try {
154 			checkGroupWorkspace(session, groupname);
155 			String homePath = getGroupPath(groupname);
156 			if (session.itemExists(homePath))
157 				return session.getNode(homePath);
158 			// legacy
159 			homePath = "/groups/" + groupname;
160 			if (session.itemExists(homePath))
161 				return session.getNode(homePath);
162 			return null;
163 		} catch (RepositoryException e) {
164 			throw new RuntimeException("Cannot find home for group " + groupname, e);
165 		}
166 
167 	}
168 
169 	private static String getGroupPath(String groupname) {
170 		String cn;
171 		try {
172 			LdapName dn = new LdapName(groupname);
173 			cn = dn.getRdn(dn.size() - 1).getValue().toString();
174 		} catch (InvalidNameException e) {
175 			cn = groupname;
176 		}
177 		return '/' + cn;
178 	}
179 
180 	private static void checkGroupWorkspace(Session session, String groupname) {
181 		String workspaceName = session.getWorkspace().getName();
182 		if (!NodeConstants.SRV_WORKSPACE.equals(workspaceName))
183 			throw new IllegalArgumentException(workspaceName + " is not the group workspace for group " + groupname);
184 	}
185 
186 	/**
187 	 * Queries one single node.
188 	 * 
189 	 * @return one single node or null if none was found
190 	 * @throws ArgeoJcrException if more than one node was found
191 	 */
192 //	private static Node querySingleNode(Query query) {
193 //		NodeIterator nodeIterator;
194 //		try {
195 //			QueryResult queryResult = query.execute();
196 //			nodeIterator = queryResult.getNodes();
197 //		} catch (RepositoryException e) {
198 //			throw new RuntimeException("Cannot execute query " + query, e);
199 //		}
200 //		Node node;
201 //		if (nodeIterator.hasNext())
202 //			node = nodeIterator.nextNode();
203 //		else
204 //			return null;
205 //
206 //		if (nodeIterator.hasNext())
207 //			throw new RuntimeException("Query returned more than one node.");
208 //		return node;
209 //	}
210 
211 	/** Returns the home node of the session user or null if none was found. */
212 	public static Node getUserHome(Session session) {
213 		String userID = session.getUserID();
214 		return getUserHome(session, userID);
215 	}
216 
217 	/**
218 	 * Translate the path to this node into a path containing the name of the
219 	 * repository and the name of the workspace.
220 	 */
221 	public static String getDataPath(String cn, Node node) throws RepositoryException {
222 		assert node != null;
223 		StringBuilder buf = new StringBuilder(NodeConstants.PATH_DATA);
224 		return buf.append('/').append(cn).append('/').append(node.getSession().getWorkspace().getName())
225 				.append(node.getPath()).toString();
226 	}
227 
228 	/**
229 	 * Open a JCR session with full read/write rights on the data, as
230 	 * {@link NodeConstants#ROLE_USER_ADMIN}, using the
231 	 * {@link NodeConstants#LOGIN_CONTEXT_DATA_ADMIN} login context. For security
232 	 * hardened deployement, use {@link AuthPermission} on this login context.
233 	 */
234 	public static Session openDataAdminSession(Repository repository, String workspaceName) {
235 		ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
236 		LoginContext loginContext;
237 		try {
238 			loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_DATA_ADMIN);
239 			loginContext.login();
240 		} catch (LoginException e1) {
241 			throw new RuntimeException("Could not login as data admin", e1);
242 		} finally {
243 			Thread.currentThread().setContextClassLoader(currentCl);
244 		}
245 		return Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Session>() {
246 
247 			@Override
248 			public Session run() {
249 				try {
250 					return repository.login(workspaceName);
251 				} catch (NoSuchWorkspaceException e) {
252 					throw new IllegalArgumentException("No workspace " + workspaceName + " available", e);
253 				} catch (RepositoryException e) {
254 					throw new RuntimeException("Cannot open data admin session", e);
255 				}
256 			}
257 
258 		});
259 	}
260 
261 	/** Singleton. */
262 	private NodeUtils() {
263 	}
264 
265 }