package org.identityconnectors.oracle; import static org.identityconnectors.oracle.OracleMessages.MSG_CANNOT_EXPIRE_PASSWORD_FOR_NOT_LOCAL_AUTHENTICATION; import static org.identityconnectors.oracle.OracleMessages.MSG_CANNOT_SET_GLOBALNAME_FOR_NOT_GLOBAL_AUTHENTICATION; import static org.identityconnectors.oracle.OracleMessages.MSG_CANNOT_SET_PASSWORD_FOR_NOT_LOCAL_AUTHENTICATION; import static org.identityconnectors.oracle.OracleMessages.MSG_MISSING_DEFAULT_TABLESPACE_FOR_QUOTA; import static org.identityconnectors.oracle.OracleMessages.MSG_MISSING_GLOBALNAME_FOR_GLOBAL_AUTHENTICATION; import static org.identityconnectors.oracle.OracleMessages.MSG_MISSING_TEMPORARY_TABLESPACE_FOR_QUOTA; import static org.identityconnectors.oracle.OracleMessages.MSG_MUST_SPECIFY_PASSWORD_FOR_UNEXPIRE; import static org.identityconnectors.oracle.OracleUserAttribute.DEF_TABLESPACE; import static org.identityconnectors.oracle.OracleUserAttribute.PASSWORD; import static org.identityconnectors.oracle.OracleUserAttribute.PASSWORD_EXPIRE; import static org.identityconnectors.oracle.OracleUserAttribute.PROFILE; import static org.identityconnectors.oracle.OracleUserAttribute.TEMP_TABLESPACE; import static org.identityconnectors.oracle.OracleUserAttribute.USER; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.objects.ConnectorMessages; import org.identityconnectors.framework.spi.operations.CreateOp; import org.identityconnectors.framework.spi.operations.SPIOperation; import org.identityconnectors.framework.spi.operations.UpdateOp; /** * Builds create or alter user sql statement. Class uses * {@link OracleCaseSensitivitySetup} to format user attributes. It expects that * passed userAttributes are already normalized, so it does not normalize tokens * anymore See BSF syntax at : Create : * http://download.oracle.com/docs/cd/B12037_01 * /server.101/b10759/statements_8003.htm Alter : * http://www.stanford.edu/dept/itss * /docs/oracle/10g/server.101/b10759/statements_4003.htm * * It also tries to perform additional check logic especially that no passed * attribute is silently skip * * @author kitko * */ final class OracleCreateOrAlterStBuilder { private final OracleCaseSensitivitySetup cs; private final ConnectorMessages cm; private final ExtraAttributesPolicySetup extraAttributesPolicySetup; private final static Log LOG = Log.getLog(OracleCreateOrAlterStBuilder.class); /** Helper status where we store real set attributes. */ private static class BuilderStatus { // Real password set private GuardedString passwordSet; // Force to expire password even if no attribute was specified by user private boolean forceExpirePassword; // Real set authentication OracleAuthentication currentAuth; private final List<Pair<OracleUserAttribute, RuntimeException>> ignoredAttributes = new ArrayList<Pair<OracleUserAttribute, RuntimeException>>(2); private BuilderStatus addIgnoredAttribute(OracleUserAttribute oua, RuntimeException e) { ignoredAttributes.add(new Pair<OracleUserAttribute, RuntimeException>(oua, e)); return this; } } OracleCreateOrAlterStBuilder(OracleCaseSensitivitySetup cs, ConnectorMessages cm, ExtraAttributesPolicySetup extraAttributesPolicySetup) { this.cs = OracleConnectorHelper.assertNotNull(cs, "cs"); this.cm = OracleConnectorHelper.assertNotNull(cm, "cm"); this.extraAttributesPolicySetup = OracleConnectorHelper.assertNotNull(extraAttributesPolicySetup, "extraAttributesPolicySetup"); } OracleCreateOrAlterStBuilder(OracleConfiguration cfg) { this(cfg.getCSSetup(), cfg.getConnectorMessages(), cfg.getExtraAttributesPolicySetup()); } /** * Builds create user sql statement. * * @param userAttributes * @return */ String buildCreateUserSt(OracleUserAttributes userAttributes) { if (userAttributes.getUserName() == null) { throw new IllegalArgumentException("User not specified"); // Internal // error } String ddl = generateDDL(userAttributes, CreateOp.class, null); if (ddl == null || ddl.length() == 0) { return null; } StringBuilder builder = new StringBuilder(); builder.append("create user ").append(cs.formatToken(USER, userAttributes.getUserName())); builder.append(ddl); return builder.toString(); } String buildAlterUserSt(OracleUserAttributes userAttributes, UserRecord userRecord) { if (userAttributes.getUserName() == null) { throw new IllegalArgumentException("User not specified"); // Internal // error } if (userRecord == null) { throw new IllegalArgumentException("Must set userRecord for update"); // Internal // error } String ddl = generateDDL(userAttributes, UpdateOp.class, userRecord); if (ddl == null || ddl.length() == 0) { return null; } StringBuilder builder = new StringBuilder(); builder.append("alter user ").append(cs.formatToken(USER, userAttributes.getUserName())); builder.append(ddl); return builder.toString(); } private String generateDDL(OracleUserAttributes userAttributes, Class<? extends SPIOperation> operation, UserRecord userRecord) { StringBuilder builder = new StringBuilder(); BuilderStatus status = new BuilderStatus(); appendAuth(builder, userAttributes, operation, status, userRecord); if (userAttributes.getDefaultTableSpace() != null) { appendDefaultTableSpace(builder, userAttributes); } if (userAttributes.getTempTableSpace() != null) { appendTemporaryTableSpace(builder, userAttributes); } if (userAttributes.getDefaultTSQuota() != null) { appendDefaultTSQuota(builder, userAttributes, userRecord); } if (userAttributes.getTempTSQuota() != null) { appendTempTSQuota(builder, userAttributes, userRecord); } if (Boolean.FALSE.equals(userAttributes.getExpirePassword())) { if (status.passwordSet == null) { // If password is already not expired, just ignore attribute // that would not have any effect if (userRecord == null || OracleUserReader.isPasswordExpired(userRecord)) { throw new IllegalArgumentException(cm.format( MSG_MUST_SPECIFY_PASSWORD_FOR_UNEXPIRE, null)); } } } if (status.forceExpirePassword || Boolean.TRUE.equals(userAttributes.getExpirePassword())) { // We can expire password only for LOCAL authentication if (OracleAuthentication.LOCAL.equals(status.currentAuth)) { appendExpirePassword(builder, userAttributes); } else { IllegalArgumentException e = new IllegalArgumentException(cm.format( MSG_CANNOT_EXPIRE_PASSWORD_FOR_NOT_LOCAL_AUTHENTICATION, null)); if (ExtraAttributesPolicy.FAIL.equals(extraAttributesPolicySetup.getPolicy( PASSWORD_EXPIRE, operation))) { throw e; } else { status.addIgnoredAttribute(PASSWORD_EXPIRE, e); LOG.info("Ignoring extra password_expire attribute in operation [{0}]", operation); } } } if (userAttributes.getEnable() != null) { appendEnabled(builder, userAttributes); } if (userAttributes.getProfile() != null) { appendProfile(builder, userAttributes); } if (builder.length() == 0 && !status.ignoredAttributes.isEmpty()) { // throw the fisrt exception throw status.ignoredAttributes.get(0).getSecond(); } return builder.toString(); } private void appendProfile(StringBuilder builder, OracleUserAttributes userAttributes) { builder.append(" profile ").append(cs.formatToken(PROFILE, userAttributes.getProfile())); } private void appendEnabled(StringBuilder builder, OracleUserAttributes userAttributes) { if (userAttributes.getEnable()) { builder.append(" account unlock"); } else { builder.append(" account lock"); } } private void appendExpirePassword(StringBuilder builder, OracleUserAttributes userAttributes) { builder.append(" password expire"); } private void appendDefaultTSQuota(StringBuilder builder, OracleUserAttributes userAttributes, UserRecord userRecord) { builder.append(" quota"); if ("-1".equals(userAttributes.getDefaultTSQuota())) { builder.append(" unlimited"); } else { builder.append(' ').append(userAttributes.getDefaultTSQuota()); } builder.append(" on"); String defaultTableSpace = userAttributes.getDefaultTableSpace(); if (defaultTableSpace == null) { if (userRecord == null || userRecord.getDefaultTableSpace() == null) { throw new IllegalArgumentException(cm.format( MSG_MISSING_DEFAULT_TABLESPACE_FOR_QUOTA, null)); } defaultTableSpace = userRecord.getDefaultTableSpace(); } builder.append(' ').append(cs.formatToken(DEF_TABLESPACE, defaultTableSpace)); } private void appendTempTSQuota(StringBuilder builder, OracleUserAttributes userAttributes, UserRecord userRecord) { builder.append(" quota"); if ("-1".equals(userAttributes.getTempTSQuota())) { builder.append(" unlimited"); } else { builder.append(' ').append(userAttributes.getTempTSQuota()); } builder.append(" on"); String tempTableSpace = userAttributes.getTempTableSpace(); if (tempTableSpace == null) { if (userRecord == null || userRecord.getTemporaryTableSpace() == null) { throw new IllegalArgumentException(cm.format( MSG_MISSING_TEMPORARY_TABLESPACE_FOR_QUOTA, null)); } tempTableSpace = userRecord.getTemporaryTableSpace(); } builder.append(' ').append(cs.formatToken(TEMP_TABLESPACE, tempTableSpace)); } private void appendTemporaryTableSpace(StringBuilder builder, OracleUserAttributes userAttributes) { builder.append(" temporary tablespace ").append( cs.formatToken(TEMP_TABLESPACE, userAttributes.getTempTableSpace())); } private void appendDefaultTableSpace(StringBuilder builder, OracleUserAttributes userAttributes) { builder.append(" default tablespace ").append( cs.formatToken(DEF_TABLESPACE, userAttributes.getDefaultTableSpace())); } private void appendAuth(final StringBuilder builder, OracleUserAttributes userAttributes, Class<? extends SPIOperation> operation, BuilderStatus status, UserRecord userRecord) { status.currentAuth = userAttributes.getAuth(); if (status.currentAuth == null) { if (CreateOp.class.equals(operation)) { status.currentAuth = OracleAuthentication.LOCAL; } else { status.currentAuth = OracleUserReader.resolveAuthentication(userRecord); } } boolean appendIdentified = CreateOp.class.equals(operation) || userAttributes.getAuth() != null || userAttributes.getPassword() != null || userAttributes.getGlobalName() != null; if (!appendIdentified) { return; } if (userAttributes.getPassword() != null && !OracleAuthentication.LOCAL.equals(status.currentAuth)) { // Apply the extra attribute policy IllegalArgumentException e = new IllegalArgumentException(cm.format( MSG_CANNOT_SET_PASSWORD_FOR_NOT_LOCAL_AUTHENTICATION, null)); if (ExtraAttributesPolicy.FAIL.equals(extraAttributesPolicySetup.getPolicy(PASSWORD, operation))) { throw e; } else { LOG.info("Ignoring extra password attribute in operation [{0}]", operation); status.addIgnoredAttribute(PASSWORD, e); // If only password was set, return if (userAttributes.getAuth() == null && userAttributes.getGlobalName() == null && UpdateOp.class.equals(operation)) { appendIdentified = false; } } } if (userAttributes.getGlobalName() != null && !OracleAuthentication.GLOBAL.equals(status.currentAuth)) { throw new IllegalArgumentException(cm.format( MSG_CANNOT_SET_GLOBALNAME_FOR_NOT_GLOBAL_AUTHENTICATION, null)); } if (!appendIdentified) { return; } builder.append(" identified"); if (OracleAuthentication.LOCAL.equals(status.currentAuth)) { builder.append(" by "); status.passwordSet = userAttributes.getPassword(); if (status.passwordSet == null) { // Can we set password same as username ? , adapter did so if (CreateOp.class.equals(operation)) { // Set password to userName, it is already normalized status.passwordSet = new GuardedString(userAttributes.getUserName().toCharArray()); } else { // no password for update and local authentication // some application can send update of authentication to // local and will not send password at the update // In this case we will rather set password to user name and // set (password_expired=true) // Other option would be to throw exception, but some // application could not have // possibility to send password status.passwordSet = new GuardedString(userAttributes.getUserName().toCharArray()); status.forceExpirePassword = true; } } status.passwordSet.access(new GuardedString.Accessor() { public void access(char[] clearChars) { builder.append(cs.formatToken(PASSWORD, clearChars)); Arrays.fill(clearChars, (char) 0); } }); } else if (OracleAuthentication.EXTERNAL.equals(status.currentAuth)) { builder.append(" externally"); } else if (OracleAuthentication.GLOBAL.equals(status.currentAuth)) { if (StringUtil.isBlank(userAttributes.getGlobalName())) { throw new IllegalArgumentException(cm.format( MSG_MISSING_GLOBALNAME_FOR_GLOBAL_AUTHENTICATION, null)); } builder.append(" globally as "); builder.append(cs.formatToken(OracleUserAttribute.GLOBAL_NAME, userAttributes .getGlobalName())); } } }