/******************************************************************************* * Cloud Foundry * Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product includes a number of subcomponents with * separate copyright notices and license terms. Your use of these * subcomponents is subject to the terms and conditions of the * subcomponent's license, as noted in the LICENSE file. *******************************************************************************/ package org.cloudfoundry.identity.uaa.provider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cloudfoundry.identity.uaa.audit.event.SystemDeletable; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.ObjectUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.RowMapper; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Date; import java.util.List; import java.util.UUID; public class JdbcIdentityProviderProvisioning implements IdentityProviderProvisioning, SystemDeletable { private static Log logger = LogFactory.getLog(JdbcIdentityProviderProvisioning.class); public static final String ID_PROVIDER_FIELDS = "id,version,created,lastmodified,name,origin_key,type,config,identity_zone_id,active"; public static final String CREATE_IDENTITY_PROVIDER_SQL = "insert into identity_provider(" + ID_PROVIDER_FIELDS + ") values (?,?,?,?,?,?,?,?,?,?)"; public static final String IDENTITY_PROVIDERS_QUERY = "select " + ID_PROVIDER_FIELDS + " from identity_provider where identity_zone_id=?"; public static final String IDENTITY_ACTIVE_PROVIDERS_QUERY = IDENTITY_PROVIDERS_QUERY + " and active=?"; public static final String ID_PROVIDER_UPDATE_FIELDS = "version,lastmodified,name,type,config,active".replace(",","=?,")+"=?"; public static final String UPDATE_IDENTITY_PROVIDER_SQL = "update identity_provider set " + ID_PROVIDER_UPDATE_FIELDS + " where id=? and identity_zone_id=?"; public static final String DELETE_IDENTITY_PROVIDER_BY_ORIGIN_SQL = "delete from identity_provider where identity_zone_id=? and origin_key = ?"; public static final String DELETE_IDENTITY_PROVIDER_BY_ZONE_SQL = "delete from identity_provider where identity_zone_id=?"; public static final String IDENTITY_PROVIDER_BY_ID_QUERY = "select " + ID_PROVIDER_FIELDS + " from identity_provider " + "where id=? and identity_zone_id=?"; public static final String IDENTITY_PROVIDER_BY_ORIGIN_QUERY = "select " + ID_PROVIDER_FIELDS + " from identity_provider " + "where origin_key=? and identity_zone_id=? "; protected final JdbcTemplate jdbcTemplate; private final RowMapper<IdentityProvider> mapper = new IdentityProviderRowMapper(); public JdbcIdentityProviderProvisioning(JdbcTemplate jdbcTemplate) { Assert.notNull(jdbcTemplate); this.jdbcTemplate = jdbcTemplate; } @Override public IdentityProvider retrieve(String id) { IdentityProvider identityProvider = jdbcTemplate.queryForObject(IDENTITY_PROVIDER_BY_ID_QUERY, mapper, id, IdentityZoneHolder.get().getId()); return identityProvider; } @Override public List<IdentityProvider> retrieveActive(String zoneId) { return jdbcTemplate.query(IDENTITY_ACTIVE_PROVIDERS_QUERY, mapper, zoneId, true); } @Override public List<IdentityProvider> retrieveAll(boolean activeOnly, String zoneId) { if (activeOnly) { return retrieveActive(zoneId); } else { return jdbcTemplate.query(IDENTITY_PROVIDERS_QUERY, mapper, zoneId); } } @Override public IdentityProvider retrieveByOrigin(String origin, String zoneId) { IdentityProvider identityProvider = jdbcTemplate.queryForObject(IDENTITY_PROVIDER_BY_ORIGIN_QUERY, mapper, origin, zoneId); return identityProvider; } @Override public IdentityProvider create(final IdentityProvider identityProvider) { validate(identityProvider); final String id = UUID.randomUUID().toString(); try { jdbcTemplate.update(CREATE_IDENTITY_PROVIDER_SQL, new PreparedStatementSetter() { @Override public void setValues(PreparedStatement ps) throws SQLException { int pos = 1; ps.setString(pos++, id); ps.setInt(pos++, identityProvider.getVersion()); ps.setTimestamp(pos++, new Timestamp(System.currentTimeMillis())); ps.setTimestamp(pos++, new Timestamp(System.currentTimeMillis())); ps.setString(pos++, identityProvider.getName()); ps.setString(pos++, identityProvider.getOriginKey()); ps.setString(pos++, identityProvider.getType()); ps.setString(pos++, JsonUtils.writeValueAsString(identityProvider.getConfig())); ps.setString(pos++, identityProvider.getIdentityZoneId()); ps.setBoolean(pos++, identityProvider.isActive()); } }); } catch (DuplicateKeyException e) { throw new IdpAlreadyExistsException(e.getMostSpecificCause().getMessage()); } return retrieve(id); } @Override public IdentityProvider update(final IdentityProvider identityProvider) { validate(identityProvider); final String zoneId = IdentityZoneHolder.get().getId(); jdbcTemplate.update(UPDATE_IDENTITY_PROVIDER_SQL, new PreparedStatementSetter() { @Override public void setValues(PreparedStatement ps) throws SQLException { int pos = 1; ps.setInt(pos++, identityProvider.getVersion() + 1); ps.setTimestamp(pos++, new Timestamp(new Date().getTime())); ps.setString(pos++, identityProvider.getName()); ps.setString(pos++, identityProvider.getType()); ps.setString(pos++, JsonUtils.writeValueAsString(identityProvider.getConfig())); ps.setBoolean(pos++, identityProvider.isActive()); ps.setString(pos++, identityProvider.getId().trim()); ps.setString(pos++, zoneId); } }); return retrieve(identityProvider.getId()); } protected void validate(IdentityProvider provider) { if (provider==null) { throw new NullPointerException("Provider can not be null."); } if (!StringUtils.hasText(provider.getIdentityZoneId())) { throw new DataIntegrityViolationException("Identity zone ID must be set."); } //ensure that SAML IDPs have reduntant fields synchronized if (OriginKeys.SAML.equals(provider.getType()) && provider.getConfig()!=null) { SamlIdentityProviderDefinition saml = ObjectUtils.castInstance(provider.getConfig(), SamlIdentityProviderDefinition.class); saml.setIdpEntityAlias(provider.getOriginKey()); saml.setZoneId(provider.getIdentityZoneId()); provider.setConfig(saml); } } @Override public int deleteByIdentityZone(String zoneId) { return jdbcTemplate.update(DELETE_IDENTITY_PROVIDER_BY_ZONE_SQL, zoneId); } @Override public int deleteByOrigin(String origin, String zoneId) { return jdbcTemplate.update(DELETE_IDENTITY_PROVIDER_BY_ORIGIN_SQL, zoneId, origin); } @Override public int deleteByClient(String clientId, String zoneId) { //no op - nothing to do here return 0; } @Override public int deleteByUser(String userId, String zoneId) { //no op - nothing to do here return 0; } @Override public Log getLogger() { return logger; } private static final class IdentityProviderRowMapper implements RowMapper<IdentityProvider> { @Override public IdentityProvider mapRow(ResultSet rs, int rowNum) throws SQLException { IdentityProvider identityProvider = new IdentityProvider(); int pos = 1; identityProvider.setId(rs.getString(pos++).trim()); identityProvider.setVersion(rs.getInt(pos++)); identityProvider.setCreated(rs.getTimestamp(pos++)); identityProvider.setLastModified(rs.getTimestamp(pos++)); identityProvider.setName(rs.getString(pos++)); identityProvider.setOriginKey(rs.getString(pos++)); identityProvider.setType(rs.getString(pos++)); String config = rs.getString(pos++); if (StringUtils.hasText(config)) { AbstractIdentityProviderDefinition definition; switch (identityProvider.getType()) { case OriginKeys.SAML : definition = JsonUtils.readValue(config, SamlIdentityProviderDefinition.class); break; case OriginKeys.OAUTH20: definition = JsonUtils.readValue(config, RawXOAuthIdentityProviderDefinition.class); break; case OriginKeys.OIDC10 : definition = JsonUtils.readValue(config, OIDCIdentityProviderDefinition.class); break; case OriginKeys.UAA : definition = JsonUtils.readValue(config, UaaIdentityProviderDefinition.class); break; case OriginKeys.LDAP : definition = JsonUtils.readValue(config, LdapIdentityProviderDefinition.class); break; case OriginKeys.KEYSTONE : definition = JsonUtils.readValue(config, KeystoneIdentityProviderDefinition.class); break; default: definition = JsonUtils.readValue(config, AbstractIdentityProviderDefinition.class); break; } if (definition!=null) { identityProvider.setConfig(definition); } } identityProvider.setIdentityZoneId(rs.getString(pos++)); identityProvider.setActive(rs.getBoolean(pos++)); return identityProvider; } } }