1 package org.collectionspace.services.common.authorization_mgt;
\r
3 import java.sql.Connection;
\r
4 import java.sql.PreparedStatement;
\r
5 import java.sql.ResultSet;
\r
6 import java.sql.SQLException;
\r
7 import java.sql.Statement;
\r
8 import java.util.ArrayList;
\r
9 import java.util.Hashtable;
\r
10 import java.util.List;
\r
11 import java.util.UUID;
\r
13 import javax.naming.NamingException;
\r
15 import org.collectionspace.services.authorization.AuthZ;
\r
16 import org.collectionspace.services.authorization.CSpaceAction;
\r
17 import org.collectionspace.services.authorization.PermissionException;
\r
18 import org.collectionspace.services.authorization.PermissionRole;
\r
19 import org.collectionspace.services.authorization.RoleValue;
\r
20 import org.collectionspace.services.authorization.URIResourceImpl;
\r
21 import org.collectionspace.services.authorization.perms.EffectType;
\r
22 import org.collectionspace.services.authorization.perms.Permission;
\r
23 import org.collectionspace.services.authorization.perms.PermissionAction;
\r
24 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
\r
25 import org.collectionspace.services.common.security.SecurityUtils;
\r
26 import org.collectionspace.services.common.storage.DatabaseProductType;
\r
27 import org.collectionspace.services.common.storage.JDBCTools;
\r
28 import org.collectionspace.services.common.tenant.TenantBindingType;
\r
29 import org.slf4j.Logger;
\r
30 import org.slf4j.LoggerFactory;
\r
31 import org.springframework.security.acls.model.AlreadyExistsException;
\r
34 public class AuthorizationCommon {
\r
35 final static Logger logger = LoggerFactory.getLogger(AuthorizationCommon.class);
\r
37 public static final String TENANT_ADMIN_ACCT_PREFIX = "admin@";
\r
38 public static final String TENANT_READER_ACCT_PREFIX = "reader@";
\r
39 public static final String ROLE_PREFIX = "ROLE_";
\r
40 public static final String SPRING_ADMIN_ROLE = "ROLE_SPRING_ADMIN";
\r
41 public static final String TENANT_ADMIN_ROLE_SUFFIX = "_TENANT_ADMINISTRATOR";
\r
42 public static final String TENANT_READER_ROLE_SUFFIX = "_TENANT_READER";
\r
43 public static final String DEFAULT_ADMIN_PASSWORD = "Administrator";
\r
44 public static final String DEFAULT_READER_PASSWORD = "reader";
\r
46 public static String ROLE_SPRING_ADMIN_ID = "-1";
\r
47 public static String ROLE_SPRING_ADMIN_NAME = "ROLE_SPRING_ADMIN";
\r
50 * addPermissionsForUri add permissions from given permission configuration
\r
51 * with assumption that resource is of type URI
\r
52 * @param permission configuration
\r
54 public static void addPermissionsForUri(Permission perm,
\r
55 PermissionRole permRole) throws PermissionException {
\r
56 List<String> principals = new ArrayList<String>();
\r
57 if (!perm.getCsid().equals(permRole.getPermission().get(0).getPermissionId())) {
\r
58 throw new IllegalArgumentException("permission ids do not"
\r
59 + " match for role=" + permRole.getRole().get(0).getRoleName()
\r
60 + " with permissionId=" + permRole.getPermission().get(0).getPermissionId()
\r
61 + " for permission with csid=" + perm.getCsid());
\r
63 for (RoleValue roleValue : permRole.getRole()) {
\r
64 principals.add(roleValue.getRoleName());
\r
66 List<PermissionAction> permActions = perm.getAction();
\r
67 for (PermissionAction permAction : permActions) {
\r
69 CSpaceAction action = URIResourceImpl.getAction(permAction.getName());
\r
70 URIResourceImpl uriRes = new URIResourceImpl(perm.getTenantId(),
\r
71 perm.getResourceName(), action);
\r
72 boolean grant = perm.getEffect().equals(EffectType.PERMIT) ? true : false;
\r
73 AuthZ.get().addPermissions(uriRes, principals.toArray(new String[0]), grant);//CSPACE-4967
\r
74 } catch (PermissionException e) {
\r
76 // Only throw the exception if it is *not* an already-exists exception
\r
78 if (e.getCause() instanceof AlreadyExistsException == false) {
\r
85 private static Connection getConnection() throws NamingException, SQLException {
\r
86 return JDBCTools.getConnection(JDBCTools.CSPACE_REPOSITORY_NAME);
\r
89 public static void createDefaultPermissions(TenantBindingConfigReaderImpl tenantBindingConfigReader)
\r
91 // For each service binding in each tenancy, get the Nuxeo document type and retrieve it's life cycle type. For
\r
92 // that life cycle type, ask Nuxeo for all the configured transitions. For each of those transitions,
\r
94 // * a URI of the form - /<service>/*/workflow/<transition>
\r
95 // * a CRUDL Permission for the URI
\r
96 // * a RL Permission for the URI
\r
97 // * a PermissionRole for admin role
\r
98 // * a PermissionRole for the reader role
\r
100 // * add a new Permission/PermissionRole tuple to the Spring AuthZ tables
\r
101 // * persist the new Permission, and PermissionRole to the cspace database
\r
105 * FIXME: REM - This method is way too big -over 300 lines! We need to break it up into
\r
106 * smaller, discrete, sub-methods.
\r
108 public static void createDefaultAccounts(TenantBindingConfigReaderImpl tenantBindingConfigReader) {
\r
109 if (logger.isDebugEnabled()) {
\r
110 logger.debug("ServiceMain.createDefaultAccounts starting...");
\r
113 Hashtable<String, TenantBindingType> tenantBindings =
\r
114 tenantBindingConfigReader.getTenantBindings();
\r
115 Hashtable<String, String> tenantInfo = new Hashtable<String, String>();
\r
116 for (TenantBindingType tenantBinding : tenantBindings.values()) {
\r
117 String tId = tenantBinding.getId();
\r
118 String tName = tenantBinding.getName();
\r
119 tenantInfo.put(tId, tName);
\r
120 if (logger.isDebugEnabled()) {
\r
121 logger.debug("createDefaultAccounts found configured tenant id: "+tId+" name: "+tName);
\r
124 Connection conn = null;
\r
125 PreparedStatement pstmt = null;
\r
126 Statement stmt = null;
\r
127 // TODO - need to put in tests for existence first.
\r
128 // We could just look for the accounts per tenant up front, and assume that
\r
129 // the rest is there if the accounts are.
\r
130 // Could add a sql script to remove these if need be - Spring only does roles,
\r
131 // and we're not touching that, so we could safely toss the
\r
132 // accounts, users, account-tenants, account-roles, and start over.
\r
134 conn = getConnection();
\r
135 // First find or create the tenants
\r
136 String queryTenantSQL =
\r
137 "SELECT id,name FROM tenants";
\r
138 stmt = conn.createStatement();
\r
139 ResultSet rs = stmt.executeQuery(queryTenantSQL);
\r
140 ArrayList<String> existingTenants = new ArrayList<String>();
\r
141 while (rs.next()) {
\r
142 String tId = rs.getString("id");
\r
143 String tName = rs.getString("name");
\r
144 if(tenantInfo.containsKey(tId)) {
\r
145 existingTenants.add(tId);
\r
146 if(!tenantInfo.get(tId).equalsIgnoreCase(tName)) {
\r
147 logger.warn("Configured name for tenant: "
\r
148 +tId+" in repository: "+tName
\r
149 +" does not match config'd name: "+ tenantInfo.get(tId));
\r
155 String insertTenantSQL =
\r
156 "INSERT INTO tenants (id,name,created_at) VALUES (?,?, now())";
\r
157 pstmt = conn.prepareStatement(insertTenantSQL); // create a statement
\r
158 for(String tId : tenantInfo.keySet()) {
\r
159 if(existingTenants.contains(tId)) {
\r
160 if (logger.isDebugEnabled()) {
\r
161 logger.debug("createDefaultAccounts: tenant exists (skipping): "
\r
162 +tenantInfo.get(tId));
\r
166 pstmt.setString(1, tId); // set id param
\r
167 pstmt.setString(2, tenantInfo.get(tId)); // set name param
\r
168 if (logger.isDebugEnabled()) {
\r
169 logger.debug("createDefaultAccounts adding entry for tenant: "+tId);
\r
171 pstmt.executeUpdate();
\r
174 // Second find or create the users
\r
175 String queryUserSQL =
\r
176 "SELECT username FROM users WHERE username LIKE '"
\r
177 +TENANT_ADMIN_ACCT_PREFIX+"%' OR username LIKE '"
\r
178 +TENANT_READER_ACCT_PREFIX+"%'";
\r
179 rs = stmt.executeQuery(queryUserSQL);
\r
180 ArrayList<String> usersInRepo = new ArrayList<String>();
\r
181 while (rs.next()) {
\r
182 String uName = rs.getString("username");
\r
183 usersInRepo.add(uName);
\r
186 String insertUserSQL =
\r
187 "INSERT INTO users (username,passwd, created_at)"
\r
188 +" VALUES (?,?, now())";
\r
189 pstmt = conn.prepareStatement(insertUserSQL); // create a statement
\r
190 for(String tName : tenantInfo.values()) {
\r
191 String adminAcctName = getDefaultAdminUserID(tName);
\r
192 if(!usersInRepo.contains(adminAcctName)) {
\r
193 String secEncPasswd = SecurityUtils.createPasswordHash(
\r
194 adminAcctName, DEFAULT_ADMIN_PASSWORD);
\r
195 pstmt.setString(1, adminAcctName); // set username param
\r
196 pstmt.setString(2, secEncPasswd); // set passwd param
\r
197 if (logger.isDebugEnabled()) {
\r
198 logger.debug("createDefaultAccounts adding user: "
\r
199 +adminAcctName+" for tenant: "+tName);
\r
201 pstmt.executeUpdate();
\r
202 } else if (logger.isDebugEnabled()) {
\r
203 logger.debug("createDefaultAccounts: user: "+adminAcctName
\r
204 +" already exists - skipping.");
\r
208 String readerAcctName = getDefaultReaderUserID(tName);
\r
209 if(!usersInRepo.contains(readerAcctName)) {
\r
210 String secEncPasswd = SecurityUtils.createPasswordHash(
\r
211 readerAcctName, DEFAULT_READER_PASSWORD);
\r
212 pstmt.setString(1, readerAcctName); // set username param
\r
213 pstmt.setString(2, secEncPasswd); // set passwd param
\r
214 if (logger.isDebugEnabled()) {
\r
215 logger.debug("createDefaultAccounts adding user: "
\r
216 +readerAcctName+" for tenant: "+tName);
\r
218 pstmt.executeUpdate();
\r
219 } else if (logger.isDebugEnabled()) {
\r
220 logger.debug("createDefaultAccounts: user: "+readerAcctName
\r
221 +" already exists - skipping.");
\r
225 // Third, create the accounts. Assume that if the users were already there,
\r
226 // then the accounts were as well
\r
227 String insertAccountSQL =
\r
228 "INSERT INTO accounts_common "
\r
229 + "(csid, email, userid, status, screen_name, metadata_protection, roles_protection, created_at) "
\r
230 + "VALUES (?,?,?,'ACTIVE',?, 'immutable', 'immutable', now())";
\r
231 Hashtable<String, String> tenantAdminAcctCSIDs = new Hashtable<String, String>();
\r
232 Hashtable<String, String> tenantReaderAcctCSIDs = new Hashtable<String, String>();
\r
233 pstmt = conn.prepareStatement(insertAccountSQL); // create a statement
\r
234 for(String tId : tenantInfo.keySet()) {
\r
235 String tName = tenantInfo.get(tId);
\r
236 String adminCSID = UUID.randomUUID().toString();
\r
237 tenantAdminAcctCSIDs.put(tId, adminCSID);
\r
238 String adminAcctName = getDefaultAdminUserID(tName);
\r
239 if(!usersInRepo.contains(adminAcctName)) {
\r
240 pstmt.setString(1, adminCSID); // set csid param
\r
241 pstmt.setString(2, adminAcctName); // set email param (bogus)
\r
242 pstmt.setString(3, adminAcctName); // set userid param
\r
243 pstmt.setString(4, "Administrator");// set screen name param
\r
244 if (logger.isDebugEnabled()) {
\r
245 logger.debug("createDefaultAccounts adding account: "
\r
246 +adminAcctName+" for tenant: "+tName);
\r
248 pstmt.executeUpdate();
\r
249 } else if (logger.isDebugEnabled()) {
\r
250 logger.debug("createDefaultAccounts: user: "+adminAcctName
\r
251 +" already exists - skipping account generation.");
\r
254 String readerCSID = UUID.randomUUID().toString();
\r
255 tenantReaderAcctCSIDs.put(tId, readerCSID);
\r
256 String readerAcctName = getDefaultReaderUserID(tName);
\r
257 if(!usersInRepo.contains(readerAcctName)) {
\r
258 pstmt.setString(1, readerCSID); // set csid param
\r
259 pstmt.setString(2, readerAcctName); // set email param (bogus)
\r
260 pstmt.setString(3, readerAcctName); // set userid param
\r
261 pstmt.setString(4, "Reader"); // set screen name param
\r
262 if (logger.isDebugEnabled()) {
\r
263 logger.debug("createDefaultAccounts adding account: "
\r
264 +readerAcctName+" for tenant: "+tName);
\r
266 pstmt.executeUpdate();
\r
267 } else if (logger.isDebugEnabled()) {
\r
268 logger.debug("createDefaultAccounts: user: "+readerAcctName
\r
269 +" already exists - skipping account creation.");
\r
273 // Fourth, bind accounts to tenants. Assume that if the users were already there,
\r
274 // then the accounts were bound to tenants correctly
\r
275 String insertAccountTenantSQL;
\r
276 DatabaseProductType databaseProductType = JDBCTools.getDatabaseProductType();
\r
277 if (databaseProductType == DatabaseProductType.MYSQL) {
\r
278 insertAccountTenantSQL =
\r
279 "INSERT INTO accounts_tenants (TENANTS_ACCOUNTSCOMMON_CSID,tenant_id) "
\r
281 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
\r
282 insertAccountTenantSQL =
\r
283 "INSERT INTO accounts_tenants (HJID, TENANTS_ACCOUNTSCOMMON_CSID,tenant_id) "
\r
284 + " VALUES(nextval('hibernate_sequence'), ?, ?)";
\r
286 throw new Exception("Unrecognized database system.");
\r
288 pstmt = conn.prepareStatement(insertAccountTenantSQL); // create a statement
\r
289 for(String tId : tenantInfo.keySet()) {
\r
290 String tName = tenantInfo.get(tId);
\r
291 if(!usersInRepo.contains(getDefaultAdminUserID(tName))) {
\r
292 String adminAcct = tenantAdminAcctCSIDs.get(tId);
\r
293 pstmt.setString(1, adminAcct); // set acct CSID param
\r
294 pstmt.setString(2, tId); // set tenant_id param
\r
295 if (logger.isDebugEnabled()) {
\r
296 logger.debug("createDefaultAccounts binding account id: "
\r
297 +adminAcct+" to tenant id: "+tId);
\r
299 pstmt.executeUpdate();
\r
301 if(!usersInRepo.contains(getDefaultReaderUserID(tName))) {
\r
302 String readerAcct = tenantReaderAcctCSIDs.get(tId);
\r
303 pstmt.setString(1, readerAcct); // set acct CSID param
\r
304 pstmt.setString(2, tId); // set tenant_id param
\r
305 if (logger.isDebugEnabled()) {
\r
306 logger.debug("createDefaultAccounts binding account id: "
\r
307 +readerAcct+" to tenant id: "+tId);
\r
309 pstmt.executeUpdate();
\r
313 // Fifth, fetch and save the default roles
\r
314 String springAdminRoleCSID = null;
\r
315 String querySpringRole =
\r
316 "SELECT csid from roles WHERE rolename='"+SPRING_ADMIN_ROLE+"'";
\r
317 rs = stmt.executeQuery(querySpringRole);
\r
319 springAdminRoleCSID = rs.getString(1);
\r
320 if (logger.isDebugEnabled()) {
\r
321 logger.debug("createDefaultAccounts found Spring Admin role: "
\r
322 +springAdminRoleCSID);
\r
325 String insertSpringAdminRoleSQL =
\r
326 "INSERT INTO roles (csid, rolename, displayName, rolegroup, created_at, tenant_id) "
\r
327 + "VALUES ('-1', 'ROLE_SPRING_ADMIN', 'SPRING_ADMIN', 'Spring Security Administrator', now(), '0')";
\r
328 stmt.executeUpdate(insertSpringAdminRoleSQL);
\r
329 springAdminRoleCSID = "-1";
\r
330 if (logger.isDebugEnabled()) {
\r
331 logger.debug("createDefaultAccounts CREATED Spring Admin role: "
\r
332 +springAdminRoleCSID);
\r
336 String getRoleCSIDSql =
\r
337 "SELECT csid from roles WHERE tenant_id=? and rolename=?";
\r
338 pstmt = conn.prepareStatement(getRoleCSIDSql); // create a statement
\r
340 Hashtable<String, String> tenantAdminRoleCSIDs = new Hashtable<String, String>();
\r
341 Hashtable<String, String> tenantReaderRoleCSIDs = new Hashtable<String, String>();
\r
342 for(String tId : tenantInfo.keySet()) {
\r
343 pstmt.setString(1, tId); // set tenant_id param
\r
344 pstmt.setString(2, getDefaultAdminRole(tId)); // set rolename param
\r
345 rs = pstmt.executeQuery();
\r
346 // extract data from the ResultSet
\r
348 throw new RuntimeException("Cannot find role: "+getDefaultAdminRole(tId)
\r
349 +" for tenant id: "+tId+" in roles!");
\r
351 String tenantAdminRoleCSID = rs.getString(1);
\r
352 if (logger.isDebugEnabled()) {
\r
353 logger.debug("createDefaultAccounts found role: "
\r
354 +getDefaultAdminRole(tId)+"("+tenantAdminRoleCSID
\r
355 +") for tenant id: "+tId);
\r
357 tenantAdminRoleCSIDs.put(tId, tenantAdminRoleCSID);
\r
358 pstmt.setString(1, tId); // set tenant_id param
\r
359 pstmt.setString(2, getDefaultReaderRole(tId)); // set rolename param
\r
361 rs = pstmt.executeQuery();
\r
362 // extract data from the ResultSet
\r
364 throw new RuntimeException("Cannot find role: "+getDefaultReaderRole(tId)
\r
365 +" for tenant id: "+tId+" in roles!");
\r
367 String tenantReaderRoleCSID = rs.getString(1);
\r
368 if (logger.isDebugEnabled()) {
\r
369 logger.debug("createDefaultAccounts found role: "
\r
370 +getDefaultReaderRole(tId)+"("+tenantReaderRoleCSID
\r
371 +") for tenant id: "+tId);
\r
373 tenantReaderRoleCSIDs.put(tId, tenantReaderRoleCSID);
\r
377 // Sixth, bind the accounts to roles. If the users already existed,
\r
378 // we'll assume they were set up correctly.
\r
379 String insertAccountRoleSQL;
\r
380 if (databaseProductType == DatabaseProductType.MYSQL) {
\r
381 insertAccountRoleSQL =
\r
382 "INSERT INTO accounts_roles(account_id, user_id, role_id, role_name, created_at)"
\r
383 +" VALUES(?, ?, ?, ?, now())";
\r
384 } else if (databaseProductType == DatabaseProductType.POSTGRESQL) {
\r
385 insertAccountRoleSQL =
\r
386 "INSERT INTO accounts_roles(HJID, account_id, user_id, role_id, role_name, created_at)"
\r
387 +" VALUES(nextval('hibernate_sequence'), ?, ?, ?, ?, now())";
\r
389 throw new Exception("Unrecognized database system.");
\r
391 if (logger.isDebugEnabled()) {
\r
392 logger.debug("createDefaultAccounts binding accounts to roles with SQL:\n"
\r
393 +insertAccountRoleSQL);
\r
395 pstmt = conn.prepareStatement(insertAccountRoleSQL); // create a statement
\r
396 for(String tId : tenantInfo.keySet()) {
\r
397 String adminUserId = getDefaultAdminUserID(tenantInfo.get(tId));
\r
398 if(!usersInRepo.contains(adminUserId)) {
\r
399 String adminAcct = tenantAdminAcctCSIDs.get(tId);
\r
400 String adminRoleId = tenantAdminRoleCSIDs.get(tId);
\r
401 pstmt.setString(1, adminAcct); // set acct CSID param
\r
402 pstmt.setString(2, adminUserId); // set user_id param
\r
403 pstmt.setString(3, adminRoleId); // set role_id param
\r
404 pstmt.setString(4, getDefaultAdminRole(tId)); // set rolename param
\r
405 if (logger.isDebugEnabled()) {
\r
406 logger.debug("createDefaultAccounts binding account: "
\r
407 +adminUserId+" to Admin role("+adminRoleId
\r
408 +") for tenant id: "+tId);
\r
410 pstmt.executeUpdate();
\r
411 // Now add the Spring Admin Role to the admin accounts
\r
412 pstmt.setString(3, springAdminRoleCSID); // set role_id param
\r
413 pstmt.setString(4, SPRING_ADMIN_ROLE); // set rolename param
\r
414 if (logger.isDebugEnabled()) {
\r
415 logger.debug("createDefaultAccounts binding account: "
\r
416 +adminUserId+" to Spring Admin role: "+springAdminRoleCSID);
\r
418 pstmt.executeUpdate();
\r
420 String readerUserId = getDefaultReaderUserID(tenantInfo.get(tId));
\r
421 if(!usersInRepo.contains(readerUserId)) {
\r
422 String readerAcct = tenantReaderAcctCSIDs.get(tId);
\r
423 String readerRoleId = tenantReaderRoleCSIDs.get(tId);
\r
424 pstmt.setString(1, readerAcct); // set acct CSID param
\r
425 pstmt.setString(2, readerUserId); // set user_id param
\r
426 pstmt.setString(3, readerRoleId); // set role_id param
\r
427 pstmt.setString(4, getDefaultReaderRole(tId)); // set rolename param
\r
428 if (logger.isDebugEnabled()) {
\r
429 logger.debug("createDefaultAccounts binding account: "
\r
430 +readerUserId+" to Reader role("+readerRoleId
\r
431 +") for tenant id: "+tId);
\r
433 pstmt.executeUpdate();
\r
438 } catch (RuntimeException rte) {
\r
439 if (logger.isDebugEnabled()) {
\r
440 logger.debug("Exception in createDefaultAccounts: "+
\r
441 rte.getLocalizedMessage());
\r
442 logger.debug(rte.getStackTrace().toString());
\r
445 } catch (SQLException sqle) {
\r
446 // SQLExceptions can be chained. We have at least one exception, so
\r
447 // set up a loop to make sure we let the user know about all of them
\r
448 // if there happens to be more than one.
\r
449 if (logger.isDebugEnabled()) {
\r
450 SQLException tempException = sqle;
\r
451 while (null != tempException) {
\r
452 logger.debug("SQL Exception: " + sqle.getLocalizedMessage());
\r
453 tempException = tempException.getNextException();
\r
455 logger.debug(sqle.getStackTrace().toString());
\r
457 throw new RuntimeException("SQL problem in createDefaultAccounts: ", sqle);
\r
458 } catch (Exception e) {
\r
459 if (logger.isDebugEnabled()) {
\r
460 logger.debug("Exception in createDefaultAccounts: "+
\r
461 e.getLocalizedMessage());
\r
471 } catch (SQLException sqle) {
\r
472 if (logger.isDebugEnabled()) {
\r
473 logger.debug("SQL Exception closing statement/connection: "
\r
474 + sqle.getLocalizedMessage());
\r
480 private static String getDefaultAdminRole(String tenantId) {
\r
481 return ROLE_PREFIX+tenantId+TENANT_ADMIN_ROLE_SUFFIX;
\r
484 private static String getDefaultReaderRole(String tenantId) {
\r
485 return ROLE_PREFIX+tenantId+TENANT_READER_ROLE_SUFFIX;
\r
488 private static String getDefaultAdminUserID(String tenantName) {
\r
489 return TENANT_ADMIN_ACCT_PREFIX+tenantName;
\r
492 private static String getDefaultReaderUserID(String tenantName) {
\r
493 return TENANT_READER_ACCT_PREFIX+tenantName;
\r