diff --git a/src/main/java/it/smartcommunitylab/aac/SystemKeys.java b/src/main/java/it/smartcommunitylab/aac/SystemKeys.java index 9ad543512..ceb52905f 100644 --- a/src/main/java/it/smartcommunitylab/aac/SystemKeys.java +++ b/src/main/java/it/smartcommunitylab/aac/SystemKeys.java @@ -65,6 +65,8 @@ public class SystemKeys { public static final String RESOURCE_IDENTITY_SERVICE = "identity_service"; public static final String RESOURCE_ATTRIBUTE_SERVICE = "attribute_service"; public static final String RESOURCE_ACCOUNT_SERVICE = "account_service"; + public static final String RESOURCE_CREDENTIALS_SERVICE = "credentials_service"; + public static final String RESOURCE_TEMPLATE_PROVIDER = "template_provider"; public static final String RESOURCE_TEMPLATE = "template"; public static final String PATH_SEPARATOR = "/-/"; diff --git a/src/main/java/it/smartcommunitylab/aac/bootstrap/AACBootstrap.java b/src/main/java/it/smartcommunitylab/aac/bootstrap/AACBootstrap.java index ac6eae38d..a5d3ef443 100644 --- a/src/main/java/it/smartcommunitylab/aac/bootstrap/AACBootstrap.java +++ b/src/main/java/it/smartcommunitylab/aac/bootstrap/AACBootstrap.java @@ -1,25 +1,25 @@ package it.smartcommunitylab.aac.bootstrap; import java.util.AbstractMap; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -30,61 +30,48 @@ import it.smartcommunitylab.aac.Config; import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.common.NoSuchAuthorityException; +import it.smartcommunitylab.aac.common.NoSuchClientException; +import it.smartcommunitylab.aac.common.NoSuchProviderException; import it.smartcommunitylab.aac.common.NoSuchRealmException; +import it.smartcommunitylab.aac.common.NoSuchServiceException; import it.smartcommunitylab.aac.common.NoSuchSubjectException; import it.smartcommunitylab.aac.common.NoSuchUserException; import it.smartcommunitylab.aac.common.RegistrationException; -import it.smartcommunitylab.aac.common.SystemException; import it.smartcommunitylab.aac.core.ClientManager; -import it.smartcommunitylab.aac.core.authorities.AttributeProviderAuthority; -import it.smartcommunitylab.aac.core.authorities.CredentialsServiceAuthority; -import it.smartcommunitylab.aac.core.authorities.IdentityProviderAuthority; -import it.smartcommunitylab.aac.core.authorities.IdentityServiceAuthority; -import it.smartcommunitylab.aac.core.authorities.TemplateProviderAuthority; -import it.smartcommunitylab.aac.core.authorities.AccountServiceAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableAttributeProvider; -import it.smartcommunitylab.aac.core.model.ConfigurableCredentialsService; import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; -import it.smartcommunitylab.aac.core.model.ConfigurableIdentityService; -import it.smartcommunitylab.aac.core.model.ConfigurableAccountService; -import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.model.ConfigurableTemplateProvider; +import it.smartcommunitylab.aac.core.model.UserAccount; +import it.smartcommunitylab.aac.core.model.UserCredentials; import it.smartcommunitylab.aac.core.persistence.UserEntity; -import it.smartcommunitylab.aac.core.provider.AccountService; -import it.smartcommunitylab.aac.core.provider.AttributeProvider; -import it.smartcommunitylab.aac.core.provider.IdentityProvider; -import it.smartcommunitylab.aac.core.provider.IdentityService; -import it.smartcommunitylab.aac.core.provider.TemplateProvider; -import it.smartcommunitylab.aac.core.provider.AccountCredentialsService; -import it.smartcommunitylab.aac.core.service.AttributeProviderAuthorityService; +import it.smartcommunitylab.aac.core.provider.UserAccountService; import it.smartcommunitylab.aac.core.service.AttributeProviderService; -import it.smartcommunitylab.aac.core.service.CredentialsServiceAuthorityService; -import it.smartcommunitylab.aac.core.service.CredentialsServiceService; -import it.smartcommunitylab.aac.core.service.IdentityProviderAuthorityService; +import it.smartcommunitylab.aac.core.service.ConfigurableProviderService; import it.smartcommunitylab.aac.core.service.IdentityProviderService; -import it.smartcommunitylab.aac.core.service.IdentityServiceAuthorityService; -import it.smartcommunitylab.aac.core.service.IdentityServiceService; import it.smartcommunitylab.aac.core.service.RealmService; -import it.smartcommunitylab.aac.core.service.AccountServiceAuthorityService; -import it.smartcommunitylab.aac.core.service.AccountServiceService; import it.smartcommunitylab.aac.core.service.SubjectService; -import it.smartcommunitylab.aac.core.service.TemplateProviderAuthorityService; import it.smartcommunitylab.aac.core.service.TemplateProviderService; import it.smartcommunitylab.aac.core.service.UserEntityService; -import it.smartcommunitylab.aac.internal.InternalAccountServiceAuthority; import it.smartcommunitylab.aac.internal.persistence.InternalUserAccount; -import it.smartcommunitylab.aac.internal.provider.InternalAccountService; +import it.smartcommunitylab.aac.internal.service.InternalUserAccountService; import it.smartcommunitylab.aac.model.ClientApp; import it.smartcommunitylab.aac.model.Realm; import it.smartcommunitylab.aac.model.SpaceRole; import it.smartcommunitylab.aac.model.SubjectStatus; -import it.smartcommunitylab.aac.password.PasswordCredentialsAuthority; -import it.smartcommunitylab.aac.password.provider.PasswordCredentialsService; +import it.smartcommunitylab.aac.openid.persistence.OIDCUserAccount; +import it.smartcommunitylab.aac.openid.service.OIDCUserAccountService; +import it.smartcommunitylab.aac.password.auth.UsernamePasswordAuthenticationToken; +import it.smartcommunitylab.aac.password.persistence.InternalUserPassword; +import it.smartcommunitylab.aac.password.service.InternalUserPasswordService; import it.smartcommunitylab.aac.roles.service.SpaceRoleService; +import it.smartcommunitylab.aac.saml.persistence.SamlUserAccount; +import it.smartcommunitylab.aac.saml.service.SamlUserAccountService; import it.smartcommunitylab.aac.services.Service; -import it.smartcommunitylab.aac.services.ServicesManager; +import it.smartcommunitylab.aac.services.ServicesService; +import it.smartcommunitylab.aac.webauthn.service.WebAuthnUserCredentialsService; @Component +@Transactional public class AACBootstrap { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -113,12 +100,9 @@ public class AACBootstrap { @Qualifier("yamlObjectMapper") private ObjectMapper yamlObjectMapper; -// @Autowired + @Autowired private BootstrapConfig config; -// @Autowired -// private AuthorityManager authorityManager; - @Autowired private RealmService realmService; @@ -126,32 +110,14 @@ public class AACBootstrap { private ClientManager clientManager; @Autowired - private ServicesManager serviceManager; + private ServicesService serviceService; @Autowired - private UserEntityService userService; + private UserEntityService userEntityService; @Autowired private SubjectService subjectService; - @Autowired - private IdentityProviderAuthorityService identityProviderAuthorityService; - - @Autowired - private AttributeProviderAuthorityService attributeProviderAuthorityService; - - @Autowired - private IdentityServiceAuthorityService identityServiceAuthorityService; - - @Autowired - private AccountServiceAuthorityService accountServiceAuthorityService; - - @Autowired - private CredentialsServiceAuthorityService credentialsServiceAuthorityService; - - @Autowired - private TemplateProviderAuthorityService templateProviderAuthorityService; - @Autowired private IdentityProviderService identityProviderService; @@ -159,65 +125,55 @@ public class AACBootstrap { private AttributeProviderService attributeProviderService; @Autowired - private IdentityServiceService identityServiceService; + private TemplateProviderService templateProviderService; @Autowired - private AccountServiceService accountServiceService; + private SpaceRoleService roleService; @Autowired - private CredentialsServiceService credentialsServiceService; + private UserAccountService internalUserAccountService; @Autowired - private TemplateProviderService templateProviderService; + private UserAccountService oidcUserAccountService; @Autowired - private SpaceRoleService roleService; + private UserAccountService samlUserAccountService; @Autowired - private InternalAccountServiceAuthority internalAccountServiceAuthority; + private WebAuthnUserCredentialsService webAuthnUserCredentialsService; @Autowired - private PasswordCredentialsAuthority passwordCredentialsAuthority; + private InternalUserPasswordService internalUserPasswordService; @EventListener - public void onApplicationEvent(ApplicationReadyEvent event) { - try { - // base initialization - logger.debug("application init"); -// initServices(); + public void onApplicationEvent(ApplicationStartedEvent event) { + // build a security context as admin to bootstrap configs + // TODO remove workaround + // do note this works ONLY for asynch events which execute in their own thread + // AND breaks with parallelStream (which uses a shared forkPool)! + SecurityContext context = initContext(adminUsername); - // build a security context as admin to bootstrap configs - // initContext(adminUsername); + try { + bootstrap(); + } finally { + if (context != null) { + SecurityContextHolder.clearContext(); + } + } + } -// if (authoritiesProps.getCustom() != null) { -// bootstrapCustomAuthorities(authoritiesProps.getCustom()); -// } + public void bootstrap() { + try { + // base initialization + logger.debug("application bootstrap"); - // bootstrap providers - // TODO use a dedicated thread, or a multithread bootstrapSystemProviders(); // bootstrap admin account - // use first active service for system, from authority - // we expect a single service anyway - Optional sysInternalIdp = internalAccountServiceAuthority - .getProvidersByRealm(SystemKeys.REALM_SYSTEM).stream() - .findFirst(); - Optional sysPasswordIdp = passwordCredentialsAuthority - .getProvidersByRealm(SystemKeys.REALM_SYSTEM).stream() - .findFirst(); - - if (sysInternalIdp.isPresent() && sysPasswordIdp.isPresent()) { - bootstrapAdminUser(sysInternalIdp.get(), sysPasswordIdp.get()); - } + bootstrapAdminUser(adminUsername, adminEmail, adminPassword); - // bootstrap realm providers - bootstrapAccountServices(); - bootstrapCredentialsServices(); - bootstrapIdentityServices(); - bootstrapIdentityProviders(); - bootstrapAttributeProviders(); - bootstrapTemplateProviders(); + // validate and remediate realm provider configs + bootstrapRealmProviders(); // custom bootstrap if (apply) { @@ -233,389 +189,121 @@ public void onApplicationEvent(ApplicationReadyEvent event) { } } - private void bootstrapSystemProviders() - throws NoSuchRealmException { - // idps - Collection idps = identityProviderService.listProviders(SystemKeys.REALM_SYSTEM); - registerIdentityProviders(idps); - - // nothing else to register for system - } - - private void bootstrapAccountServices() { - // load all realm providers from storage - Collection realms = realmService.listUserRealms(); - - // we iterate by realm to load consistently each realm - // we use parallel to leverage default threadpool, loading should be thread-safe - realms.parallelStream().forEach(realm -> { - String slug = realm.getSlug(); - Collection idss = accountServiceService.listProviders(slug); - // register - registerAccountServices(idss); - }); - } - - private List> registerAccountServices( - Collection idss) { - Map> ias = accountServiceAuthorityService - .getAuthorities().stream() - .collect(Collectors.toMap(a -> a.getAuthorityId(), a -> a)); + private SecurityContext initContext(String username) { + // assign authorities + Collection authorities = Collections + .singletonList(new SimpleGrantedAuthority(Config.R_ADMIN)); - List> services = new ArrayList<>(); + // use an auth token + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, + authorities); - for (ConfigurableAccountService ids : idss) { - // try register - if (ids.isEnabled()) { - try { - // register directly with authority - AccountServiceAuthority ia = ias.get(ids.getAuthority()); - if (ia == null) { - throw new IllegalArgumentException("no authority for " + String.valueOf(ids.getAuthority())); - } + // mock a security context with token + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(token); - AccountService p = ia.registerProvider(ids); - services.add(p); - } catch (Exception e) { - logger.error("error registering provider " + ids.getProvider() + " for realm " - + ids.getRealm() + ": " + e.getMessage()); - - if (logger.isTraceEnabled()) { - e.printStackTrace(); - } - } - } - } - - return services; - } - - private void bootstrapCredentialsServices() { - // load all realm providers from storage - Collection realms = realmService.listUserRealms(); - - // we iterate by realm to load consistently each realm - // we use parallel to leverage default threadpool, loading should be thread-safe - realms.parallelStream().forEach(realm -> { - String slug = realm.getSlug(); - Collection css = credentialsServiceService.listProviders(slug); - registerCredentialsServices(css); - }); + return context; } - private List> registerCredentialsServices( - Collection css) { - Map> cas = credentialsServiceAuthorityService - .getAuthorities().stream() - .collect(Collectors.toMap(a -> a.getAuthorityId(), a -> a)); - - List> services = new ArrayList<>(); - - for (ConfigurableCredentialsService cs : css) { - // try register - if (cs.isEnabled()) { - try { - // register directly with authority - CredentialsServiceAuthority ca = cas.get(cs.getAuthority()); - if (ca == null) { - throw new IllegalArgumentException("no authority for " + String.valueOf(cs.getAuthority())); - } - - AccountCredentialsService s = ca.registerProvider(cs); - services.add(s); - } catch (Exception e) { - logger.error("error registering provider " + cs.getProvider() + " for realm " - + cs.getRealm() + ": " + e.getMessage()); - - if (logger.isTraceEnabled()) { - e.printStackTrace(); - } - } - } - } - - return services; + private void bootstrapSystemProviders() { + // bootstrap only idps + bootstrapConfigurableProviders(identityProviderService, SystemKeys.REALM_SYSTEM); } - private void bootstrapIdentityServices() { + private void bootstrapRealmProviders() { // load all realm providers from storage Collection realms = realmService.listUserRealms(); // we iterate by realm to load consistently each realm - // we use parallel to leverage default threadpool, loading should be thread-safe + // we use parallel to leverage default thread-pool, loading should be + // thread-safe + // do note that parallel uses a shared fork pool, without any security context realms.parallelStream().forEach(realm -> { - String slug = realm.getSlug(); - Collection idss = identityServiceService.listProviders(slug); - // register - registerIdentityServices(idss); + bootstrapRealmProviders(realm.getSlug()); }); } - private List> registerIdentityServices( - Collection idss) { - Map> ias = identityServiceAuthorityService - .getAuthorities().stream() - .collect(Collectors.toMap(a -> a.getAuthorityId(), a -> a)); - - List> services = new ArrayList<>(); - - for (ConfigurableIdentityService ids : idss) { - // try register - if (ids.isEnabled()) { - try { - // register directly with authority - IdentityServiceAuthority ia = ias.get(ids.getAuthority()); - if (ia == null) { - throw new IllegalArgumentException("no authority for " + String.valueOf(ids.getAuthority())); - } - - IdentityService p = ia.registerProvider(ids); - services.add(p); - } catch (Exception e) { - logger.error("error registering provider " + ids.getProvider() + " for realm " - + ids.getRealm() + ": " + e.getMessage()); - - if (logger.isTraceEnabled()) { - e.printStackTrace(); - } - } - } - } - - return services; - } - - private void bootstrapIdentityProviders() { - // load all realm providers from storage - Collection realms = realmService.listUserRealms(); - - // we iterate by realm to load consistently each realm - // we use parallel to leverage default threadpool, loading should be thread-safe - realms.parallelStream().forEach(realm -> { - Collection idps = identityProviderService.listProviders(realm.getSlug()); - // register - registerIdentityProviders(idps); - }); + private void bootstrapRealmProviders(String slug) { + // load providers in order, single thread + bootstrapConfigurableProviders(templateProviderService, slug); + bootstrapConfigurableProviders(identityProviderService, slug); + bootstrapConfigurableProviders(attributeProviderService, slug); } - private List> registerIdentityProviders( - Collection idps) { - Map> ias = identityProviderAuthorityService - .getAuthorities().stream() - .collect(Collectors.toMap(a -> a.getAuthorityId(), a -> a)); - - List> providers = new ArrayList<>(); - - for (ConfigurableIdentityProvider idp : idps) { - // try register - if (idp.isEnabled()) { + private void bootstrapConfigurableProviders(ConfigurableProviderService service, String realm) { + // register in parallel via thread-pool + service.listProviders(realm).parallelStream().forEach(cp -> { + if (cp.isEnabled()) { try { - // register directly with authority - IdentityProviderAuthority ia = ias.get(idp.getAuthority()); - if (ia == null) { - throw new IllegalArgumentException("no authority for " + String.valueOf(idp.getAuthority())); - } - - IdentityProvider p = ia.registerProvider(idp); - providers.add(p); + service.registerProvider(cp.getProvider()); } catch (Exception e) { - logger.error("error registering provider " + idp.getProvider() + " for realm " - + idp.getRealm() + ": " + e.getMessage()); + logger.error("error registering provider {}:{} for realm {}: {}", + cp.getAuthority(), cp.getProvider(), cp.getRealm(), + e.getMessage()); if (logger.isTraceEnabled()) { e.printStackTrace(); } } } - } - - return providers; - } - - private void bootstrapAttributeProviders() { - // load all realm providers from storage - Collection realms = realmService.listUserRealms(); - - // we iterate by realm to load consistently each realm - // we use parallel to leverage default threadpool, loading should be thread-safe - realms.parallelStream().forEach(realm -> { - Collection providers = attributeProviderService - .listProviders(realm.getSlug()); - - registerAttributeProviders(providers); }); } - private List> registerAttributeProviders(Collection as) { - Map> ias = attributeProviderAuthorityService - .getAuthorities().stream() - .collect(Collectors.toMap(a -> a.getAuthorityId(), a -> a)); - - List> providers = new ArrayList<>(); - - for (ConfigurableAttributeProvider ap : as) { - // try register - if (ap.isEnabled()) { - try { - // register directly with authority - AttributeProviderAuthority ia = ias.get(ap.getAuthority()); - if (ia == null) { - throw new IllegalArgumentException( - "no authority for " + String.valueOf(ap.getAuthority())); - } - - AttributeProvider p = ia.registerProvider(ap); - providers.add(p); - } catch (Exception e) { - logger.error("error registering provider " + ap.getProvider() + " for realm " - + ap.getRealm() + ": " + e.getMessage()); - - if (logger.isTraceEnabled()) { - e.printStackTrace(); - } - } - } - } - - return providers; - } - - private void bootstrapTemplateProviders() { - // load all realm providers from storage - Collection realms = realmService.listUserRealms(); - - // we iterate by realm to load consistently each realm - // we use parallel to leverage default threadpool, loading should be thread-safe - realms.parallelStream().forEach(realm -> { - Collection providers = templateProviderService - .listProviders(realm.getSlug()); - - // make sure there is always a default provider available - // use authority.realm slug as id - String id = SystemKeys.AUTHORITY_TEMPLATE + SystemKeys.SLUG_SEPARATOR + realm.getSlug(); - - if (providers.isEmpty() - || providers.stream().noneMatch(i -> (i.getAuthority().equals(SystemKeys.AUTHORITY_TEMPLATE) - && i.getProvider().equals(id)))) { - ConfigurableTemplateProvider p = new ConfigurableTemplateProvider( - SystemKeys.AUTHORITY_TEMPLATE, id, realm.getSlug()); - try { - p = templateProviderService.addProvider(realm.getSlug(), p); - } catch (RegistrationException | SystemException | NoSuchAuthorityException e) { - // skip - logger.error("error creating provider " + p.getProvider() + " for realm " - + p.getRealm() + ": " + e.getMessage()); - if (logger.isTraceEnabled()) { - e.printStackTrace(); - } - } - - // re-read list - providers = templateProviderService.listProviders(realm.getSlug()); - } - registerTemplateProviders(providers); - }); - } + @Transactional(propagation = Propagation.REQUIRES_NEW) + private void bootstrapAdminUser(String username, String email, String password) + throws RegistrationException, NoSuchUserException { - private List> registerTemplateProviders(Collection tps) { - Map> tas = templateProviderAuthorityService - .getAuthorities().stream() - .collect(Collectors.toMap(a -> a.getAuthorityId(), a -> a)); + // create admin as superuser for system + logger.debug("create internal admin user for realm system", username); - List> providers = new ArrayList<>(); + String realm = SystemKeys.REALM_SYSTEM; - for (ConfigurableTemplateProvider tp : tps) { - // try register - if (tp.isEnabled()) { - try { - // register directly with authority - TemplateProviderAuthority ta = tas.get(tp.getAuthority()); - if (ta == null) { - throw new IllegalArgumentException( - "no authority for " + String.valueOf(tp.getAuthority())); - } + String userId = null; + UserEntity user = null; - TemplateProvider p = ta.registerProvider(tp); - providers.add(p); - } catch (Exception e) { - logger.error("error registering provider " + tp.getProvider() + " for realm " - + tp.getRealm() + ": " + e.getMessage()); + InternalUserAccount account = internalUserAccountService.findAccountById(realm, username); + if (account == null) { + // register as new user + userId = userEntityService.createUser(realm).getUuid(); + user = userEntityService.addUser(userId, realm, username, email); - if (logger.isTraceEnabled()) { - e.printStackTrace(); - } - } - } - } + // register account + account = new InternalUserAccount(); + account.setProvider(realm); + account.setRealm(realm); + account.setUsername(username); + account.setUserId(userId); + account.setEmail(email); + account.setStatus(SubjectStatus.ACTIVE.getValue()); + account.setConfirmed(true); + account = internalUserAccountService.addAccount(realm, username, account); + } else { + userId = account.getUserId(); - return providers; - } + // update account + account.setEmail(email); - @Transactional(propagation = Propagation.REQUIRES_NEW) - private void bootstrapAdminUser(InternalAccountService idp, PasswordCredentialsService service) - throws RegistrationException, NoSuchUserException { - // create admin as superuser for system - // TODO rewrite via idp - logger.debug("create internal admin user " + adminUsername); - UserEntity user = null; - InternalUserAccount account = idp.findAccount(adminUsername); - if (account != null) { -// // check if sub exists, recreate if needed -// String uuid = account.getUuid(); -// if (!StringUtils.hasText(uuid)) { -// // generate uuid and register as subject -// uuid = subjectService.generateUuid(SystemKeys.RESOURCE_ACCOUNT); -// } -// Subject s = subjectService.findSubject(uuid); -// if (s == null) { -// s = subjectService.addSubject(uuid, SystemKeys.REALM_SYSTEM, SystemKeys.RESOURCE_ACCOUNT, -// adminUsername); -// } -// account.setUuid(uuid); + // override confirm + account.setConfirmed(true); + account.setConfirmationDeadline(null); + account.setConfirmationKey(null); - // check if user exists, recreate if needed - user = userService.findUser(account.getUserId()); - if (user == null) { - user = userService.addUser(userService.createUser(SystemKeys.REALM_SYSTEM).getUuid(), - SystemKeys.REALM_SYSTEM, adminUsername, adminEmail); - } - } else { - // register as new - user = userService.addUser(userService.createUser(SystemKeys.REALM_SYSTEM).getUuid(), - SystemKeys.REALM_SYSTEM, adminUsername, adminEmail); - String userId = user.getUuid(); + // set as active + account.setStatus(SubjectStatus.ACTIVE.getValue()); - // generate uuid - String uuid = subjectService.generateUuid(SystemKeys.RESOURCE_ACCOUNT); + account = internalUserAccountService.updateAccount(realm, username, account); - account = new InternalUserAccount(); - account.setProvider(idp.getProvider()); - account.setUserId(userId); - account.setUuid(uuid); - account.setRealm(SystemKeys.REALM_SYSTEM); - account.setUsername(adminUsername); - account.setEmail(adminEmail); - account.setStatus(SubjectStatus.ACTIVE.getValue()); - account = idp.createAccount(userId, account); + // update user + user = userEntityService.updateUser(userId, username, email); } - String userId = account.getUserId(); - try { - // update username - user = userService.updateUser(userId, adminUsername, adminEmail); // ensure user is active - user = userService.activateUser(userId); + user = userEntityService.activateUser(userId); - // re-set password if needed - if (!service.verifyPassword(adminUsername, adminPassword)) { - // direct set - service.setPassword(adminUsername, adminPassword, false); - } - - // ensure account is confirmed and unlocked - account = idp.confirmAccount(adminUsername); - account = idp.unlockAccount(adminUsername); + // always reset password + internalUserPasswordService.deletePassword(realm, username); + internalUserPasswordService.setPassword(realm, username, password, false, -1, 0); // assign authorities to subject String subjectId = user.getUuid(); @@ -640,9 +328,9 @@ private void bootstrapAdminUser(InternalAccountService idp, PasswordCredentialsS roleService.addRole(subjectId, null, "", Config.R_PROVIDER); } - logger.debug("admin user id " + String.valueOf(account.getId())); - logger.debug("admin user " + user.toString()); - logger.debug("admin account " + account.toString()); + logger.debug("admin user id {}", userId); + logger.debug("admin user {}", user.toString()); + logger.debug("admin account {}", account.toString()); } catch (NoSuchUserException | NoSuchSubjectException e) { logger.error("error updating admin account: " + e.getMessage()); } @@ -650,7 +338,6 @@ private void bootstrapAdminUser(InternalAccountService idp, PasswordCredentialsS @Transactional(propagation = Propagation.REQUIRES_NEW) public void bootstrapConfig() throws Exception { - // read configuration Resource res = resourceLoader.getResource(source); if (!res.exists()) { @@ -661,238 +348,613 @@ public void bootstrapConfig() throws Exception { // read config config = yamlObjectMapper.readValue(res.getInputStream(), BootstrapConfig.class); - // TODO validation on imported beans - /* * Realms creation */ - logger.debug("create bootstrap realms"); + logger.debug("bootstrap realms from config"); + if (config.getRealms() == null) { + return; + } - // keep a cache of bootstrapped realms, we - // will process only content related to these realms - Map realms = new HashMap<>(); + config.getRealms().parallelStream().forEach(rc -> { + if (rc.getRealm() == null || !StringUtils.hasText(rc.getRealm().getSlug())) { + // we ask id to be provided otherwise we would create a new one every time + logger.error("error creating realm, missing slug"); + return; + } - for (Realm r : config.getRealms()) { + String slug = rc.getRealm().getSlug(); try { - if (!StringUtils.hasText(r.getSlug())) { - // we ask id to be provided otherwise we create a new one every time - logger.error("error creating realm, missing slug"); - throw new IllegalArgumentException("missing slug"); - } - - logger.debug("create or update realm " + r.getSlug()); + Realm r = rc.getRealm(); + logger.debug("create realm {}", r.getSlug()); Realm realm = realmService.findRealm(r.getSlug()); if (realm == null) { + logger.debug("add realm {}", r.getSlug()); + realm = realmService.addRealm(r.getSlug(), r.getName(), r.isEditable(), r.isPublic()); } else { + logger.debug("update realm {}", r.getSlug()); + // skip config maps // TODO put in dedicated providers + config realm = realmService.updateRealm(r.getSlug(), r.getName(), r.isEditable(), r.isPublic(), null); } - // keep in cache - realms.put(realm.getSlug(), realm); - - } catch (Exception e) { - logger.error("error creating provider " + String.valueOf(r.getSlug()) + ": " + e.getMessage()); - e.printStackTrace(); - } - } + /* + * IdP + */ + if (rc.getIdentityProviders() != null) { + rc.getIdentityProviders().forEach(cp -> { + + logger.debug("create identity provider for realm {}", String.valueOf(cp.getRealm())); + + // validate realm match + if (StringUtils.hasText(cp.getRealm()) && !slug.equals(cp.getRealm())) { + logger.error("error creating identity provider, realm mismatch"); + return; + } + + // enforce realm + cp.setRealm(slug); + + // validate id + if (!StringUtils.hasText(cp.getProvider())) { + // we ask id to be provided otherwise we would create a new one every time + logger.error("error creating identity provider, missing id"); + throw new IllegalArgumentException("missing id"); + } + + if (logger.isTraceEnabled()) { + logger.trace("provider: {}", String.valueOf(cp)); + } + + try { + // add or update via service + String providerId = cp.getProvider(); + ConfigurableIdentityProvider p = identityProviderService.findProvider(providerId); + if (p == null) { + logger.debug("add identity provider {} for realm {}", providerId, + String.valueOf(cp.getRealm())); + + p = identityProviderService.addProvider(slug, cp); + } else { + // check again realm match over existing + if (!slug.equals(p.getRealm())) { + logger.error("error creating identity provider, realm mismatch"); + return; + } + + logger.debug("update identity provider {} for realm {}", providerId, + String.valueOf(cp.getRealm())); + + if (p.isEnabled()) { + identityProviderService.unregisterProvider(providerId); + } + + p = identityProviderService.updateProvider(providerId, cp); + } + + if (p.isEnabled()) { + // register + identityProviderService.registerProvider(p.getProvider()); + } + } catch (RegistrationException | NoSuchRealmException | NoSuchProviderException + | NoSuchAuthorityException e) { + logger.error( + "error creating identity provider " + String.valueOf(cp.getProvider()) + ": " + + e.getMessage()); + } + }); + } - /* - * IdP - */ - // keep a cache, we'll load users only to these providers - Map providers = new HashMap<>(); - for (ConfigurableProvider cp : config.getProviders()) { + /* + * Attribute providers + */ + if (rc.getAttributeProviders() != null) { + rc.getAttributeProviders().forEach(cp -> { + logger.debug("create attribute provider for realm {}", String.valueOf(cp.getRealm())); + + // validate realm match + if (StringUtils.hasText(cp.getRealm()) && !slug.equals(cp.getRealm())) { + logger.error("error creating attribute provider, realm mismatch"); + return; + } + + // enforce realm + cp.setRealm(slug); + + // validate id + if (!StringUtils.hasText(cp.getProvider())) { + // we ask id to be provided otherwise we would create a new one every time + logger.error("error creating attribute provider, missing id"); + throw new IllegalArgumentException("missing id"); + } + + if (logger.isTraceEnabled()) { + logger.trace("provider: {}", String.valueOf(cp)); + } + + try { + // add or update via service + String providerId = cp.getProvider(); + ConfigurableAttributeProvider p = attributeProviderService.findProvider(providerId); + if (p == null) { + logger.debug("add attribute provider {} for realm {}", providerId, + String.valueOf(cp.getRealm())); + p = attributeProviderService.addProvider(slug, cp); + } else { + // check again realm match over existing + if (!slug.equals(p.getRealm())) { + logger.error("error creating attribute provider, realm mismatch"); + return; + } + + logger.debug("update attribute provider {} for realm {}", providerId, + String.valueOf(cp.getRealm())); + + if (p.isEnabled()) { + attributeProviderService.unregisterProvider(providerId); + } + + p = attributeProviderService.updateProvider(providerId, cp); + } + + if (p.isEnabled()) { + // register + attributeProviderService.registerProvider(p.getProvider()); + } + } catch (RegistrationException | NoSuchRealmException | NoSuchProviderException + | NoSuchAuthorityException e) { + logger.error( + "error creating attribute provider " + String.valueOf(cp.getProvider()) + ": " + + e.getMessage()); + } + }); + } - try { - if (!realms.containsKey(cp.getRealm())) { - // not managed here, skip - continue; + /* + * Template provider + */ + if (rc.getTemplates() != null) { + ConfigurableTemplateProvider cp = rc.getTemplates(); + logger.debug("create template provider for realm {}", String.valueOf(cp.getRealm())); + + // validate realm match + if (StringUtils.hasText(cp.getRealm()) && !slug.equals(cp.getRealm())) { + logger.error("error creating template provider, realm mismatch"); + } else { + // enforce realm + cp.setRealm(slug); + + if (logger.isTraceEnabled()) { + logger.trace("provider: {}", String.valueOf(cp)); + } + try { + // add or update via service + String providerId = cp.getProvider(); + ConfigurableTemplateProvider p = templateProviderService.findProvider(providerId); + if (p == null) { + logger.debug("add template provider {} for realm {}", providerId, + String.valueOf(cp.getRealm())); + + p = templateProviderService.addProvider(slug, cp); + } else { + // check again realm match over existing + if (!slug.equals(p.getRealm())) { + logger.error("error creating template provider, realm mismatch"); + return; + } + + logger.debug("update template provider {} for realm {}", providerId, + String.valueOf(cp.getRealm())); + + templateProviderService.unregisterProvider(providerId); + p = templateProviderService.updateProvider(providerId, cp); + } + + // register + templateProviderService.registerProvider(p.getProvider()); + } catch (RegistrationException | NoSuchRealmException | NoSuchProviderException + | NoSuchAuthorityException e) { + logger.error( + "error creating template provider " + String.valueOf(cp.getProvider()) + ": " + + e.getMessage()); + } + } } - if (!StringUtils.hasText(cp.getProvider())) { - // we ask id to be provided otherwise we create a new one every time - logger.error("error creating provider, missing id"); - throw new IllegalArgumentException("missing id"); + + /* + * Services + */ + if (rc.getServices() != null) { + rc.getServices().forEach(s -> { + logger.debug("create service for realm {}", String.valueOf(s.getRealm())); + + // validate realm match + if (!StringUtils.hasText(s.getRealm()) || !slug.equals(s.getRealm())) { + logger.error("error creating service, realm mismatch"); + return; + } + + // enforce realm + s.setRealm(slug); + + // validate id + if (!StringUtils.hasText(s.getServiceId())) { + // we ask id to be provided otherwise we create a new one every time + logger.error("error creating service, missing serviceId"); + throw new IllegalArgumentException("missing serviceId"); + } + + // validate namespace + if (!StringUtils.hasText(s.getNamespace())) { + logger.error("error creating service, missing namespace"); + throw new IllegalArgumentException("missing namespace"); + } + + if (logger.isTraceEnabled()) { + logger.trace("service: {}", String.valueOf(s)); + } + + try { + // add or update via service + String id = s.getServiceId(); + Service service = serviceService.findService(id); + if (service == null) { + logger.debug("add service {} for realm {}", id, String.valueOf(s.getRealm())); + + service = serviceService.addService(s.getRealm(), id, s.getNamespace(), s.getName(), + s.getDescription()); + } else { + // check again realm match over existing + if (!slug.equals(service.getRealm())) { + logger.error("error creating service, realm mismatch"); + return; + } + + logger.debug("update service {} for realm {}", id, String.valueOf(s.getRealm())); + + service = serviceService.updateService(id, s.getName(), s.getDescription(), null); + } + + } catch (RegistrationException | NoSuchServiceException e) { + logger.error( + "error creating service " + String.valueOf(s.getServiceId()) + ": " + + e.getMessage()); + } + }); } - // we support only idp for now - // TODO refactor, this doesn't work oob -// if (SystemKeys.RESOURCE_IDENTITY.equals(cp.getType())) { -// ConfigurableIdentityProvider ip = (ConfigurableIdentityProvider) cp; -// logger.debug("create or update provider " + cp.getProvider()); -// ConfigurableIdentityProvider provider = identityProviderManager.findProvider(cp.getRealm(), -// cp.getProvider()); + +// /* +// * ClientApp +// * +// */ +// if (rc.getClientApps() != null) { +// rc.getClientApps().forEach(app -> { +// logger.debug("create client app for realm {}", String.valueOf(app.getRealm())); // -// if (provider == null) { -// provider = identityProviderManager.addProvider(cp.getRealm(), ip); -// } else { -// provider = identityProviderManager.unregisterProvider(cp.getRealm(), cp.getProvider()); -// provider = identityProviderManager.updateProvider(cp.getRealm(), cp.getProvider(), ip); -// } +// // validate realm match +// if (!StringUtils.hasText(app.getRealm()) || !slug.equals(app.getRealm())) { +// logger.error("error creating service, realm mismatch"); +// return; +// } +// +// // enforce realm +// app.setRealm(slug); // -// if (cp.isEnabled()) { -// // register -// if (!identityProviderManager.isProviderRegistered(cp.getRealm(), provider)) { -// provider = identityProviderManager.registerProvider(provider.getRealm(), -// provider.getProvider()); +// if (!StringUtils.hasText(app.getClientId())) { +// // we ask id to be provided otherwise we create a new one every time +// logger.error("error creating client, missing clientId"); +// throw new IllegalArgumentException("missing clientId"); // } -// } // -// // keep in cache -// providers.put(provider.getProvider(), provider); +// if (logger.isTraceEnabled()) { +// logger.trace("app: {}", String.valueOf(app)); +// } +// +// try { +// String clientId = app.getClientId(); +// ClientApp client = clientManager.findClientApp(app.getRealm(), clientId); +// +// if (client == null) { +// logger.debug("add client app {} for realm {}", clientId, +// String.valueOf(app.getRealm())); +// +// client = clientManager.registerClientApp(app.getRealm(), app); +// } else { +// logger.debug("update client app {} for realm {}", clientId, +// String.valueOf(app.getRealm())); // +// client = clientManager.updateClientApp(app.getRealm(), app.getClientId(), app); +// } +// +// } catch (RegistrationException | NoSuchClientException | NoSuchRealmException e) { +// logger.error( +// "error creating client app " + String.valueOf(app.getClientId()) + ": " +// + e.getMessage()); +// } +// }); // } - } catch (Exception e) { - logger.error("error creating provider " + String.valueOf(cp.getProvider()) + ": " + e.getMessage()); - e.printStackTrace(); - } - } - - /* - * Services - */ - for (Service s : config.getServices()) { - - try { - if (!StringUtils.hasText(s.getRealm()) || !realms.containsKey(s.getRealm())) { - // not managed here, skip - continue; - } - if (!StringUtils.hasText(s.getServiceId())) { - // we ask id to be provided otherwise we create a new one every time - logger.error("error creating service, missing serviceId"); - throw new IllegalArgumentException("missing serviceId"); + /* + * User Accounts + * + * TODO use User model with roles+authorities+groups + */ + if (rc.getUsers() != null) { + rc.getUsers().forEach(u -> { + // validate authority + if (!StringUtils.hasText(u.getAuthority())) { + logger.error("error creating user, invalid authority"); + return; + } + + logger.debug("create {} user for realm {}", String.valueOf(u.getAuthority()), + String.valueOf(u.getRealm())); + + // validate realm match + if (StringUtils.hasText(u.getRealm()) && !slug.equals(u.getRealm())) { + logger.error("error creating user, realm mismatch"); + return; + } + + // enforce realm + u.setRealm(slug); + + // validate id + if (!StringUtils.hasText(u.getAccountId())) { + // we ask id to be provided otherwise we would create a new one every time + logger.error("error creating user, missing id"); + throw new IllegalArgumentException("missing id"); + } + + if (logger.isTraceEnabled()) { + logger.trace("{} user account: {}", String.valueOf(u.getAuthority()), String.valueOf(u)); + } + + try { + // add or update via service + String id = u.getAccountId(); + String providerId = u.getProvider(); + + // user entity + String userId = u.getUserId(); + UserEntity user = null; + if (StringUtils.hasText(userId)) { + user = userEntityService.findUser(userId); + } + + if (user == null) { + // register as new user + userId = userEntityService.createUser(slug).getUuid(); + user = userEntityService.addUser(userId, slug, u.getUsername(), u.getEmailAddress()); + } + + u.setUserId(userId); + + // set status as active by default + if (!StringUtils.hasText(u.getStatus())) { + u.setStatus(SubjectStatus.ACTIVE.getValue()); + } + + UserAccount account = null; + + // TODO refactor with a single method + if (u instanceof InternalUserAccount) { + // cast + InternalUserAccount ua = (InternalUserAccount) u; + // validate provider + if (!StringUtils.hasText(ua.getProvider())) { + // use realm as default + providerId = slug; + ua.setProvider(slug); + } + + // set confirmed by default + if (!ua.isConfirmed()) { + ua.setConfirmed(true); + } + + account = internalUserAccountService.findAccountById(providerId, id); + if (account == null) { + logger.debug("add internal account {} for realm {}", id, slug); + + // register account + account = internalUserAccountService.addAccount(slug, ua.getAccountId(), ua); + } else { + // check again realm match over existing + if (!slug.equals(account.getRealm())) { + logger.error("error creating internal account, realm mismatch"); + return; + } + + logger.debug("update internal account {} for realm {}", id, slug); + account = internalUserAccountService.updateAccount(slug, ua.getAccountId(), ua); + } + } + + if (u instanceof OIDCUserAccount) { + // cast + OIDCUserAccount ua = (OIDCUserAccount) u; + + // validate provider + if (!StringUtils.hasText(ua.getProvider())) { + // we ask id to be provided otherwise we would create a new one every time + logger.error("error creating user, missing provider"); + throw new IllegalArgumentException("missing provider"); + } + + account = oidcUserAccountService.findAccountById(providerId, id); + if (account == null) { + logger.debug("add oidc account {} for realm {}", id, slug); + + // register account + account = oidcUserAccountService.addAccount(slug, ua.getAccountId(), ua); + } else { + // check again realm match over existing + if (!slug.equals(account.getRealm())) { + logger.error("error creating oidc account, realm mismatch"); + return; + } + + logger.debug("update oidc account {} for realm {}", id, slug); + account = oidcUserAccountService.updateAccount(slug, ua.getAccountId(), ua); + } + } + + if (u instanceof SamlUserAccount) { + // cast + SamlUserAccount ua = (SamlUserAccount) u; + + // validate provider + if (!StringUtils.hasText(ua.getProvider())) { + // we ask id to be provided otherwise we would create a new one every time + logger.error("error creating user, missing provider"); + throw new IllegalArgumentException("missing provider"); + } + + account = samlUserAccountService.findAccountById(providerId, id); + if (account == null) { + logger.debug("add saml account {} for realm {}", id, slug); + + // register account + account = samlUserAccountService.addAccount(slug, ua.getAccountId(), ua); + } else { + // check again realm match over existing + if (!slug.equals(account.getRealm())) { + logger.error("error creating saml account, realm mismatch"); + return; + } + + logger.debug("update saml account {} for realm {}", id, slug); + account = samlUserAccountService.updateAccount(slug, ua.getAccountId(), ua); + } + } + + if (logger.isTraceEnabled()) { + logger.trace("{} user account: {}", String.valueOf(account.getAuthority()), + String.valueOf(account)); + } + + } catch (RegistrationException | NoSuchUserException e) { + logger.error("error creating {} user {}: {}", String.valueOf(u.getAuthority()), + String.valueOf(u.getAccountId()), e.getMessage()); + } + }); } - if (!StringUtils.hasText(s.getNamespace())) { - logger.error("error creating service, missing namespace"); - throw new IllegalArgumentException("missing namespace"); - } - - logger.debug("create or update service " + s.getServiceId()); - Service service = serviceManager.findService(s.getRealm(), s.getServiceId()); - - if (service == null) { - service = serviceManager.addService(s.getRealm(), s); - } else { - service = serviceManager.updateService(s.getRealm(), s.getServiceId(), s); + /* + * Credentials + */ + if (rc.getCredentials() != null) { + rc.getCredentials().forEach(c -> { + // validate authority + if (!StringUtils.hasText(c.getAuthority())) { + logger.error("error creating credentials, invalid authority"); + return; + } + + // validate type + if (!StringUtils.hasText(c.getType())) { + logger.error("error creating credentials, invalid type"); + return; + } + + logger.debug("create {} credential for realm {}", String.valueOf(c.getType()), + String.valueOf(c.getRealm())); + + // validate realm match + if (StringUtils.hasText(c.getRealm()) && !slug.equals(c.getRealm())) { + logger.error("error creating credentials, realm mismatch"); + return; + } + + // enforce realm + c.setRealm(slug); + + // validate account id + if (!StringUtils.hasText(c.getAccountId())) { + logger.error("error creating credentials, missing account id"); + throw new IllegalArgumentException("missing account id"); + } + + if (logger.isTraceEnabled()) { + logger.trace("{} user credentials: {}", String.valueOf(c.getType()), String.valueOf(c)); + } + + try { + // add or update via service + String id = c.getId(); + String providerId = c.getProvider(); + + // we skip account check + + // set status as active by default + if (!StringUtils.hasText(c.getStatus())) { + c.setStatus(SubjectStatus.ACTIVE.getValue()); + } + + UserCredentials credentials = null; + + // TODO refactor with a single method + // TODO refactor password services over repo + // TODO support webauthn + if ("credentials_password".equals(c.getType())) { + // cast + InternalUserPassword uc = (InternalUserPassword) c; + + if (!StringUtils.hasText(uc.getProvider())) { + providerId = slug; + } + + // extract password and encode if required + String password = uc.getPassword(); + // TODO encode and set via new service + if (id != null) { + credentials = internalUserPasswordService.findPasswordById(id); + } + + if (credentials == null) { + logger.debug("add password {} for realm {} account {}", id, slug, + uc.getAccountId()); + + credentials = internalUserPasswordService.setPassword(providerId, uc.getUsername(), + password, false, null, 0); + } else { + // check again realm match over existing + if (!slug.equals(credentials.getRealm())) { + logger.error("error creating {} password, realm mismatch"); + return; + } + + logger.debug("update password {} for realm {} account {}", id, slug, + uc.getAccountId()); + credentials = internalUserPasswordService.setPassword(providerId, uc.getUsername(), + password, false, null, 0); + } + } + + if (logger.isTraceEnabled()) { + logger.trace("{} user credentials: {}", String.valueOf(credentials.getType()), + String.valueOf(credentials)); + } + + } catch (RegistrationException | NoSuchUserException e) { + logger.error("error creating {} user credentials {}: {}", String.valueOf(c.getAuthority()), + String.valueOf(c.getId()), e.getMessage()); + } + }); } } catch (Exception e) { - logger.error("error creating service " + String.valueOf(s.getServiceId()) + ": " + e.getMessage()); - e.printStackTrace(); - } - } - - /* - * ClientApp - */ - for (ClientApp ca : config.getClients()) { - - try { - if (!StringUtils.hasText(ca.getRealm()) || !realms.containsKey(ca.getRealm())) { - // not managed here, skip - continue; + logger.error("error creating realm " + String.valueOf(slug) + ": " + e.getMessage()); + if (logger.isTraceEnabled()) { + e.printStackTrace(); } - if (!StringUtils.hasText(ca.getClientId())) { - // we ask id to be provided otherwise we create a new one every time - logger.error("error creating client, missing clientId"); - throw new IllegalArgumentException("missing clientId"); - } - - logger.debug("create or update client " + ca.getClientId()); - ClientApp client = clientManager.findClientApp(ca.getRealm(), ca.getClientId()); - - if (client == null) { - client = clientManager.registerClientApp(ca.getRealm(), ca); - } else { - client = clientManager.updateClientApp(ca.getRealm(), ca.getClientId(), ca); - } - - } catch (Exception e) { - logger.error("error creating client " + String.valueOf(ca.getClientId()) + ": " + e.getMessage()); - e.printStackTrace(); } - } - -// // Internal users -// PasswordHash hasher = new PasswordHash(); -// for (InternalUserAccount ua : config.getUsers().getInternal()) { -// try { -// if (!StringUtils.hasText(ua.getRealm()) || !StringUtils.hasText(ua.getProvider())) { -// // invalid, skip -// continue; -// } -// if (!realms.containsKey(ua.getRealm()) || !providers.containsKey(ua.getProvider())) { -// // not managed here, skip -// continue; -// } -// if (!StringUtils.hasText(ua.getSubject())) { -// // we ask id to be provided otherwise we create a new one every time -// logger.error("error creating user, missing subjectId"); -// throw new IllegalArgumentException("missing subjectId"); -// } -// if (!StringUtils.hasText(ua.getUsername())) { -// // we ask id to be provided otherwise we create a new one every time -// logger.error("error creating user, missing username"); -// throw new IllegalArgumentException("missing username"); -// } -// if (!StringUtils.hasText(ua.getPassword())) { -// // we ask id to be provided otherwise we create a new one every time -// logger.error("error creating user, missing password"); -// throw new IllegalArgumentException("missing password"); -// } -// -// logger.debug("create or update user " + ua.getSubject() + " with authority internal"); -// -// // check if user exists, recreate if needed -// UserEntity user = userService.findUser(ua.getSubject()); -// if (user == null) { -// user = userService.addUser(ua.getSubject(), ua.getRealm(), ua.getUsername(), ua.getEmail()); -// } else { -// // check match -// if (!user.getRealm().equals(ua.getRealm())) { -// logger.error("error creating user, realm mismatch"); -// throw new IllegalArgumentException("realm mismatch"); -// } -// -// user = userService.updateUser(ua.getSubject(), ua.getUsername(), ua.getEmail()); -// } -// -// InternalUserAccount account = internalUserService.findAccountByUsername(ua.getRealm(), -// ua.getUsername()); -// -// if (account == null) { -// account = internalUserService.addAccount(ua); -// } else { -// account = internalUserService.updateAccount(account.getId(), account); -// } -// -// // re-set password -// String hash = hasher.createHash(ua.getPassword()); -// account.setPassword(hash); -// account.setChangeOnFirstAccess(false); -// -// // ensure account is unlocked -// account.setConfirmed(true); -// account.setConfirmationKey(null); -// account.setConfirmationDeadline(null); -// account.setResetKey(null); -// account.setResetDeadline(null); -// account = internalUserService.updateAccount(account.getId(), account); -// -// } catch (Exception e) { -// logger.error("error creating user " + String.valueOf(ua.getSubject()) + ": " + e.getMessage()); -// e.printStackTrace(); -// } -// } - - // TODO oidc/saml users - // requires accountService extracted from idp to detach from repo + }); /* * Migrations? @@ -904,7 +966,7 @@ public void bootstrapConfig() throws Exception { * Call init on each service we expect services to be independent and to execute * in their own transaction to avoid rollback issues across services */ - public void initServices() throws Exception { +// public void initServices() throws Exception { // /* // * Base user // */ @@ -932,11 +994,39 @@ public void initServices() throws Exception { // logger.trace("init client"); // clientManager.init(); - } +// } // // public void executeMigrations() { // // } +// public List> registerProviders(String type, +// Collection cps) throws NoSuchProviderException { +// AuthorityService pas = authorityService.getAuthorityService(type); +// +// List> configs = new ArrayList<>(); +// +// for (ConfigurableProvider cp : cps) { +// // try register +// if (cp.isEnabled()) { +// try { +// // register directly with authority +// ProviderConfig c = pas.getAuthority(cp.getAuthority()).registerProvider(cp); +// configs.add(c); +// } catch (Exception e) { +// logger.error("error registering provider {} {} for realm {}: {}", +// type, cp.getProvider(), cp.getRealm(), +// e.getMessage()); +// +// if (logger.isTraceEnabled()) { +// e.printStackTrace(); +// } +// } +// } +// } +// +// return configs; +// } + } diff --git a/src/main/java/it/smartcommunitylab/aac/bootstrap/BootstrapConfig.java b/src/main/java/it/smartcommunitylab/aac/bootstrap/BootstrapConfig.java index f6a75ee76..1a2fe7735 100644 --- a/src/main/java/it/smartcommunitylab/aac/bootstrap/BootstrapConfig.java +++ b/src/main/java/it/smartcommunitylab/aac/bootstrap/BootstrapConfig.java @@ -5,22 +5,20 @@ import javax.validation.Valid; -import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import org.springframework.validation.annotation.Validated; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import it.smartcommunitylab.aac.core.model.ConfigurableProvider; -import it.smartcommunitylab.aac.internal.persistence.InternalUserAccount; -import it.smartcommunitylab.aac.model.ClientApp; -import it.smartcommunitylab.aac.model.Realm; -import it.smartcommunitylab.aac.services.Service; +import it.smartcommunitylab.aac.dto.RealmConfig; -//@Configuration -//@PropertySource(factory = YamlPropertySourceFactory.class, ignoreResourceNotFound = true, value = "${bootstrap.file}") -//@ConfigurationProperties(prefix = "bootstrap") +@Configuration +@PropertySource(factory = YamlPropertySourceFactory.class, ignoreResourceNotFound = true, value = "${bootstrap.file}") +@ConfigurationProperties(prefix = "bootstrap") @Validated @Valid @JsonInclude(Include.ALWAYS) @@ -28,82 +26,19 @@ public class BootstrapConfig { // @NestedConfigurationProperty - private List realms; - -// @NestedConfigurationProperty - private List providers; - -// @NestedConfigurationProperty - private List clients; - - private List services; - -// @NestedConfigurationProperty - private UsersConfig users; + private List realms; public BootstrapConfig() { this.realms = new ArrayList<>(); - this.clients = new ArrayList<>(); - this.providers = new ArrayList<>(); - this.users = new UsersConfig(); + } - public List getRealms() { + public List getRealms() { return realms; } - public void setRealms(List realms) { + public void setRealms(List realms) { this.realms = realms; } - public List getProviders() { - return providers; - } - - public void setProviders(List providers) { - this.providers = providers; - } - - public List getClients() { - return clients; - } - - public void setClients(List clients) { - this.clients = clients; - } - - public List getServices() { - return services; - } - - public void setServices(List services) { - this.services = services; - } - - public UsersConfig getUsers() { - return users; - } - - public void setUsers(UsersConfig users) { - this.users = users; - } - - public class UsersConfig { - @NestedConfigurationProperty - private List internal; - - public UsersConfig() { - this.internal = new ArrayList<>(); - } - - public List getInternal() { - return internal; - } - - public void setInternal(List internal) { - this.internal = internal; - } - - } - } diff --git a/src/main/java/it/smartcommunitylab/aac/config/PersistenceConfig.java b/src/main/java/it/smartcommunitylab/aac/config/PersistenceConfig.java index 60c818444..f1ef00c6b 100644 --- a/src/main/java/it/smartcommunitylab/aac/config/PersistenceConfig.java +++ b/src/main/java/it/smartcommunitylab/aac/config/PersistenceConfig.java @@ -128,7 +128,8 @@ protected YAMLGenerator _createGenerator(Writer out, IOContext ctxt) throws IOEx return new CustomYAMLFactory() .configure(YAMLGenerator.Feature.WRITE_DOC_START_MARKER, false) .configure(YAMLGenerator.Feature.MINIMIZE_QUOTES, false) - .configure(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE, true); + .configure(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE, true) + .configure(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID, false); } private YAMLGenerator yamlGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures, diff --git a/src/main/java/it/smartcommunitylab/aac/config/ProvidersProperties.java b/src/main/java/it/smartcommunitylab/aac/config/ProvidersProperties.java index 952eb8f1f..a9ee2469b 100644 --- a/src/main/java/it/smartcommunitylab/aac/config/ProvidersProperties.java +++ b/src/main/java/it/smartcommunitylab/aac/config/ProvidersProperties.java @@ -1,222 +1,26 @@ package it.smartcommunitylab.aac.config; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - import org.springframework.boot.context.properties.NestedConfigurationProperty; -import it.smartcommunitylab.aac.internal.provider.InternalAccountServiceConfigMap; -import it.smartcommunitylab.aac.openid.provider.OIDCIdentityProviderConfigMap; -import it.smartcommunitylab.aac.saml.provider.SamlIdentityProviderConfigMap; +import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; public class ProvidersProperties { @NestedConfigurationProperty - private List identity; - - @NestedConfigurationProperty - private List attributes; - - @NestedConfigurationProperty - private ProviderTemplates templates; + private List identity; public ProvidersProperties() { identity = new ArrayList<>(); - attributes = new ArrayList<>(); } - public List getIdentity() { + public List getIdentity() { return identity; } - public void setIdentity(List identity) { + public void setIdentity(List identity) { this.identity = identity; } - public List getAttributes() { - return attributes; - } - - public void setAttributes(List attributes) { - this.attributes = attributes; - } - - public ProviderTemplates getTemplates() { - return templates; - } - - public void setTemplates(ProviderTemplates templates) { - this.templates = templates; - } - - // TODO drop class in favor of configurableProvider - public static class ProviderConfiguration { - @NotBlank - private String authority; - @NotNull - private String provider; - @NotNull - private String type; - - private String realm; - - private String persistence; - - private String events; - - private String name; - - private Map title; - - private Map description; - - private Map configuration; - - private Boolean enable; - - public ProviderConfiguration() { - this.configuration = new HashMap<>(); - } - - public String getAuthority() { - return authority; - } - - public void setAuthority(String authority) { - this.authority = authority; - } - - public String getProvider() { - return provider; - } - - public void setProvider(String provider) { - this.provider = provider; - } - - public String getRealm() { - return realm; - } - - public void setRealm(String realm) { - this.realm = realm; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Map getConfiguration() { - return configuration; - } - - public void setConfiguration(Map configuration) { - this.configuration = configuration; - } - - public String getPersistence() { - return persistence; - } - - public void setPersistence(String persistence) { - this.persistence = persistence; - } - - public String getEvents() { - return events; - } - - public void setEvents(String events) { - this.events = events; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Map getTitle() { - return title; - } - - public void setTitle(Map title) { - this.title = title; - } - - public Map getDescription() { - return description; - } - - public void setDescription(Map description) { - this.description = description; - } - - public boolean isEnabled() { - return enable != null ? enable.booleanValue() : false; - } - - public Boolean getEnable() { - return enable; - } - - public void setEnable(Boolean enable) { - this.enable = enable; - } - - @Override - public String toString() { - return "ProviderConfiguration [authority=" + authority + ", provider=" + provider + ", realm=" + realm - + ", type=" + type + "]"; - } - - } - - public static class ProviderTemplates { - @NestedConfigurationProperty - private List oidc; - - @NestedConfigurationProperty - private List saml; - - @NestedConfigurationProperty - private List internal; - - public List getOidc() { - return oidc; - } - - public void setOidc(List oidc) { - this.oidc = oidc; - } - - public List getSaml() { - return saml; - } - - public void setSaml(List saml) { - this.saml = saml; - } - - public List getInternal() { - return internal; - } - - public void setInternal(List internal) { - this.internal = internal; - } - - } - } diff --git a/src/main/java/it/smartcommunitylab/aac/console/DevRealmController.java b/src/main/java/it/smartcommunitylab/aac/console/DevRealmController.java index 4c89f43a7..1dacc385a 100644 --- a/src/main/java/it/smartcommunitylab/aac/console/DevRealmController.java +++ b/src/main/java/it/smartcommunitylab/aac/console/DevRealmController.java @@ -52,6 +52,7 @@ import it.smartcommunitylab.aac.core.UserDetails; import it.smartcommunitylab.aac.core.auth.UserAuthentication; import it.smartcommunitylab.aac.core.model.ConfigurableTemplateProvider; +import it.smartcommunitylab.aac.dto.RealmConfig; import it.smartcommunitylab.aac.dto.UserSubject; import it.smartcommunitylab.aac.model.Developer; import it.smartcommunitylab.aac.model.Realm; @@ -147,7 +148,7 @@ public void deleteRealm( @PreAuthorize("hasAuthority('" + Config.R_ADMIN + "') or hasAuthority(#realm+':ROLE_ADMIN')") public void exportRealm( @PathVariable @Valid @NotNull @Pattern(regexp = SystemKeys.SLUG_PATTERN) String realm, - @RequestParam(required = false, defaultValue = "false") boolean full, + @RequestParam(required = false, defaultValue = "false") boolean config, HttpServletResponse res) throws NoSuchRealmException, SystemException, IOException { @@ -155,16 +156,10 @@ public void exportRealm( Object export = r; String key = r.getSlug(); - // TODO refactor export - if (full) { - key = r.getSlug() + "-full"; - Map> map = new HashMap<>(); - map.put("realms", Collections.singleton(r)); - map.put("identity_providers", identityProviderManager.listProviders(realm)); - map.put("attribute_providers", attributeProviderManager.listProviders(realm)); - map.put("clients", clientManager.listClientApps(realm)); - map.put("services", serviceManager.listServices(realm)); - export = map; + if (config) { + key = r.getSlug() + "-config"; + RealmConfig rc = realmManager.getRealmConfig(realm); + export = rc; } String s = yamlObjectMapper.writeValueAsString(export); diff --git a/src/main/java/it/smartcommunitylab/aac/core/AccountServiceManager.java b/src/main/java/it/smartcommunitylab/aac/core/AccountServiceManager.java index 9ebfef672..7ba98ed2f 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/AccountServiceManager.java +++ b/src/main/java/it/smartcommunitylab/aac/core/AccountServiceManager.java @@ -5,7 +5,6 @@ import it.smartcommunitylab.aac.Config; import it.smartcommunitylab.aac.core.authorities.AccountServiceAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableAccountService; -import it.smartcommunitylab.aac.core.service.AccountServiceAuthorityService; import it.smartcommunitylab.aac.core.service.AccountServiceService; @Service @@ -14,9 +13,8 @@ public class AccountServiceManager extends ConfigurableProviderManager> { - public AccountServiceManager(AccountServiceService accountServiceService, - AccountServiceAuthorityService accountServiceAuthorityService) { - super(accountServiceService, accountServiceAuthorityService); + public AccountServiceManager(AccountServiceService accountServiceService) { + super(accountServiceService); } } diff --git a/src/main/java/it/smartcommunitylab/aac/core/AttributeProviderManager.java b/src/main/java/it/smartcommunitylab/aac/core/AttributeProviderManager.java index cc696d4e2..ddf9edc63 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/AttributeProviderManager.java +++ b/src/main/java/it/smartcommunitylab/aac/core/AttributeProviderManager.java @@ -5,7 +5,6 @@ import it.smartcommunitylab.aac.Config; import it.smartcommunitylab.aac.core.authorities.AttributeProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableAttributeProvider; -import it.smartcommunitylab.aac.core.service.AttributeProviderAuthorityService; import it.smartcommunitylab.aac.core.service.AttributeProviderService; @Service @@ -14,9 +13,8 @@ public class AttributeProviderManager extends ConfigurableProviderManager> { - public AttributeProviderManager( - AttributeProviderService providerService, AttributeProviderAuthorityService providerAuthorityService) { - super(providerService, providerAuthorityService); + public AttributeProviderManager(AttributeProviderService providerService) { + super(providerService); } } diff --git a/src/main/java/it/smartcommunitylab/aac/core/ConfigurableProviderManager.java b/src/main/java/it/smartcommunitylab/aac/core/ConfigurableProviderManager.java index 73011cc2f..09b37b5b8 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/ConfigurableProviderManager.java +++ b/src/main/java/it/smartcommunitylab/aac/core/ConfigurableProviderManager.java @@ -3,6 +3,7 @@ import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -15,32 +16,25 @@ import it.smartcommunitylab.aac.common.NoSuchRealmException; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.common.SystemException; -import it.smartcommunitylab.aac.core.authorities.AuthorityService; import it.smartcommunitylab.aac.core.authorities.ProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableProperties; import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.persistence.ProviderEntity; -import it.smartcommunitylab.aac.core.provider.ConfigurableResourceProvider; import it.smartcommunitylab.aac.core.service.ConfigurableProviderService; import it.smartcommunitylab.aac.core.service.RealmService; import it.smartcommunitylab.aac.model.Realm; -public abstract class ConfigurableProviderManager> { +public abstract class ConfigurableProviderManager> + implements InitializingBean { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ConfigurableProviderService providerService; - - private final AuthorityService providerAuthorityService; + private final ConfigurableProviderService providerService; private RealmService realmService; - public ConfigurableProviderManager(ConfigurableProviderService providerService, - AuthorityService providerAuthorityService) { + public ConfigurableProviderManager(ConfigurableProviderService providerService) { Assert.notNull(providerService, "provider service is required"); - Assert.notNull(providerAuthorityService, "authority service is required"); - this.providerService = providerService; - this.providerAuthorityService = providerAuthorityService; } @Autowired @@ -48,10 +42,14 @@ public void setRealmService(RealmService realmService) { this.realmService = realmService; } + @Override + public void afterPropertiesSet() throws Exception { + Assert.notNull(realmService, "realm service can not be null"); + } + /* * Configurable Providers */ - public Collection listProviders(String realm) throws NoSuchRealmException { if (SystemKeys.REALM_GLOBAL.equals(realm) || SystemKeys.REALM_SYSTEM.equals(realm)) { return providerService.listProviders(realm); @@ -87,9 +85,8 @@ public C getProvider(String realm, String providerId) // deprecated, let controllers/managers ask for status where needed // this does not pertain to configuration - boolean isActive = providerAuthorityService.getAuthority(cp.getAuthority()) - .hasProvider(cp.getProvider()); - cp.setRegistered(isActive); + boolean isRegistered = providerService.isProviderRegistered(providerId); + cp.setRegistered(isRegistered); return cp; } @@ -151,12 +148,10 @@ public void deleteProvider(String realm, String providerId) throw new IllegalArgumentException("realm does not match provider"); } - // check if active, we don't support delete for active providers - boolean isActive = providerAuthorityService.getAuthority(cp.getAuthority()) - .hasProvider(cp.getProvider()); - - if (isActive) { - throw new IllegalArgumentException("active providers can not be deleted"); + // check if registered, we don't support delete for active providers + boolean isRegistered = providerService.isProviderRegistered(providerId); + if (isRegistered) { + throw new IllegalArgumentException("registered providers can not be deleted"); } providerService.deleteProvider(providerId); @@ -180,18 +175,16 @@ public C registerProvider(String realm, String providerId) throw new IllegalArgumentException("realm does not match provider"); } - // check if active - boolean isActive = providerAuthorityService.getAuthority(ip.getAuthority()) - .hasProvider(ip.getProvider()); - if (isActive) { + // check if registered + boolean isRegistered = providerService.isProviderRegistered(ip.getProvider()); + if (isRegistered) { // make a quick unload - providerAuthorityService.getAuthority(ip.getAuthority()).unregisterProvider(ip.getProvider()); - isActive = providerAuthorityService.getAuthority(ip.getAuthority()) - .hasProvider(ip.getProvider()); + providerService.unregisterProvider(ip.getProvider()); + isRegistered = providerService.isProviderRegistered(ip.getProvider()); } - if (isActive) { - throw new IllegalArgumentException("active providers can not be registered again"); + if (isRegistered) { + throw new IllegalArgumentException("registered providers can not be registered again"); } // check if already enabled in config, or update @@ -200,10 +193,10 @@ public C registerProvider(String realm, String providerId) ip = providerService.updateProvider(providerId, ip); } - ConfigurableResourceProvider idp = providerAuthorityService.getAuthority(ip.getAuthority()) - .registerProvider(ip); - isActive = idp != null; - ip.setRegistered(isActive); + // register + providerService.registerProvider(providerId); + isRegistered = providerService.isProviderRegistered(ip.getProvider()); + ip.setRegistered(isRegistered); return ip; } @@ -232,14 +225,13 @@ public C unregisterProvider(String realm, String providerId) cp = providerService.updateProvider(providerId, cp); } - // check if active - boolean isActive = providerAuthorityService.getAuthority(cp.getAuthority()) - .hasProvider(cp.getProvider()); + // check if registered + boolean isRegistered = providerService.isProviderRegistered(providerId); - if (isActive) { - providerAuthorityService.getAuthority(cp.getAuthority()).unregisterProvider(cp.getProvider()); - isActive = false; - cp.setRegistered(isActive); + if (isRegistered) { + providerService.unregisterProvider(cp.getProvider()); + isRegistered = false; + cp.setRegistered(isRegistered); } return cp; @@ -251,10 +243,13 @@ public C unregisterProvider(String realm, String providerId) * Support checking registration status */ public boolean isProviderRegistered(String realm, C provider) throws SystemException { + if (provider == null) { + return false; + } + try { - return providerAuthorityService.getAuthority(provider.getAuthority()) - .hasProvider(provider.getProvider()); - } catch (NoSuchAuthorityException e) { + return providerService.isProviderRegistered(provider.getProvider()); + } catch (NoSuchAuthorityException | NoSuchProviderException e) { return false; } } diff --git a/src/main/java/it/smartcommunitylab/aac/core/IdentityProviderManager.java b/src/main/java/it/smartcommunitylab/aac/core/IdentityProviderManager.java index f6a6ffd11..7d1d2a6e7 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/IdentityProviderManager.java +++ b/src/main/java/it/smartcommunitylab/aac/core/IdentityProviderManager.java @@ -5,7 +5,6 @@ import it.smartcommunitylab.aac.Config; import it.smartcommunitylab.aac.core.authorities.IdentityProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; -import it.smartcommunitylab.aac.core.service.IdentityProviderAuthorityService; import it.smartcommunitylab.aac.core.service.IdentityProviderService; @Service @@ -14,9 +13,8 @@ public class IdentityProviderManager extends ConfigurableProviderManager> { - public IdentityProviderManager(IdentityProviderService identityProviderService, - IdentityProviderAuthorityService identityProviderAuthorityService) { - super(identityProviderService, identityProviderAuthorityService); + public IdentityProviderManager(IdentityProviderService identityProviderService) { + super(identityProviderService); } } diff --git a/src/main/java/it/smartcommunitylab/aac/core/RealmManager.java b/src/main/java/it/smartcommunitylab/aac/core/RealmManager.java index c8b989a65..52bdff48c 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/RealmManager.java +++ b/src/main/java/it/smartcommunitylab/aac/core/RealmManager.java @@ -1,6 +1,7 @@ package it.smartcommunitylab.aac.core; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -11,7 +12,6 @@ import javax.validation.Valid; import javax.validation.constraints.NotBlank; - import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; import org.slf4j.Logger; @@ -30,7 +30,6 @@ import it.smartcommunitylab.aac.common.NoSuchAttributeSetException; import it.smartcommunitylab.aac.common.NoSuchAuthorityException; import it.smartcommunitylab.aac.common.NoSuchClientException; -import it.smartcommunitylab.aac.common.NoSuchGroupException; import it.smartcommunitylab.aac.common.NoSuchProviderException; import it.smartcommunitylab.aac.common.NoSuchRealmException; import it.smartcommunitylab.aac.common.NoSuchServiceException; @@ -38,21 +37,36 @@ import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.common.SystemException; import it.smartcommunitylab.aac.config.ApplicationProperties; +import it.smartcommunitylab.aac.core.base.AbstractAccount; import it.smartcommunitylab.aac.core.entrypoint.RealmAwareUriBuilder; import it.smartcommunitylab.aac.core.model.AttributeSet; import it.smartcommunitylab.aac.core.model.Client; import it.smartcommunitylab.aac.core.model.ConfigurableAttributeProvider; import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; +import it.smartcommunitylab.aac.core.model.ConfigurableTemplateProvider; +import it.smartcommunitylab.aac.core.provider.UserAccountService; +import it.smartcommunitylab.aac.core.service.AttributeProviderService; +import it.smartcommunitylab.aac.core.service.IdentityProviderService; import it.smartcommunitylab.aac.core.service.RealmService; +import it.smartcommunitylab.aac.core.service.TemplateProviderService; import it.smartcommunitylab.aac.core.service.UserService; -import it.smartcommunitylab.aac.groups.GroupManager; +import it.smartcommunitylab.aac.dto.RealmConfig; +import it.smartcommunitylab.aac.groups.service.GroupService; +import it.smartcommunitylab.aac.internal.persistence.InternalUserAccount; +import it.smartcommunitylab.aac.model.ClientApp; import it.smartcommunitylab.aac.model.Developer; import it.smartcommunitylab.aac.model.Group; import it.smartcommunitylab.aac.model.Realm; import it.smartcommunitylab.aac.model.RealmRole; import it.smartcommunitylab.aac.model.User; +import it.smartcommunitylab.aac.openid.persistence.OIDCUserAccount; +import it.smartcommunitylab.aac.password.service.InternalUserPasswordService; import it.smartcommunitylab.aac.roles.RealmRoleManager; +import it.smartcommunitylab.aac.saml.persistence.SamlUserAccount; import it.smartcommunitylab.aac.services.ServicesManager; +import it.smartcommunitylab.aac.templates.model.TemplateModel; +import it.smartcommunitylab.aac.templates.service.TemplateService; +import it.smartcommunitylab.aac.webauthn.service.WebAuthnUserCredentialsService; @Service public class RealmManager { @@ -76,10 +90,16 @@ public class RealmManager { private UserManager userManager; @Autowired - private IdentityProviderManager identityProviderManager; + private IdentityProviderService identityProviderService; + + @Autowired + private AttributeProviderService attributeProviderService; @Autowired - private AttributeProviderManager attributeProviderManager; + private TemplateProviderService templateProviderService; + + @Autowired + private TemplateService templateService; @Autowired private ServicesManager servicesManager; @@ -91,11 +111,26 @@ public class RealmManager { protected AttributeSetsManager attributeManager; @Autowired - protected GroupManager groupManager; + protected GroupService groupService; @Autowired private RealmRoleManager roleManager; + @Autowired + private UserAccountService internalUserAccountService; + + @Autowired + private UserAccountService oidcUserAccountService; + + @Autowired + private UserAccountService samlUserAccountService; + + @Autowired + private WebAuthnUserCredentialsService webAuthnUserCredentialsService; + + @Autowired + private InternalUserPasswordService internalUserPasswordService; + // @Autowired // private SessionManager sessionManager; @@ -220,15 +255,15 @@ public void deleteRealm(String slug, boolean cleanup) throws NoSuchRealmExceptio if (realm != null && cleanup) { // remove all identity providers, will also invalidate sessions for idps - Collection idps = identityProviderManager.listProviders(slug); + Collection idps = identityProviderService.listProviders(slug); for (ConfigurableIdentityProvider provider : idps) { try { String providerId = provider.getProvider(); // stop provider, will terminate sessions - identityProviderManager.unregisterProvider(slug, providerId); + identityProviderService.unregisterProvider(providerId); // remove provider - identityProviderManager.deleteProvider(slug, providerId); + identityProviderService.deleteProvider(providerId); } catch (NoSuchProviderException | NoSuchAuthorityException | SystemException e) { // skip logger.error("Error deleting realm for provider {}: {}", provider.getProvider(), e.getMessage()); @@ -236,15 +271,31 @@ public void deleteRealm(String slug, boolean cleanup) throws NoSuchRealmExceptio } // remove all attribute providers - Collection aps = attributeProviderManager.listProviders(slug); + Collection aps = attributeProviderService.listProviders(slug); for (ConfigurableAttributeProvider provider : aps) { try { String providerId = provider.getProvider(); // stop provider - attributeProviderManager.unregisterProvider(slug, providerId); + attributeProviderService.unregisterProvider(providerId); + + // remove provider + attributeProviderService.deleteProvider(providerId); + } catch (NoSuchProviderException | NoSuchAuthorityException | SystemException e) { + // skip + logger.error("Error deleting realm for provider {}: {}", provider.getProvider(), e.getMessage()); + } + } + + // remove all template providers + Collection tps = templateProviderService.listProviders(slug); + for (ConfigurableTemplateProvider provider : tps) { + try { + String providerId = provider.getProvider(); + // stop provider + templateProviderService.unregisterProvider(providerId); // remove provider - attributeProviderManager.deleteProvider(slug, providerId); + templateProviderService.deleteProvider(providerId); } catch (NoSuchProviderException | NoSuchAuthorityException | SystemException e) { // skip logger.error("Error deleting realm for provider {}: {}", provider.getProvider(), e.getMessage()); @@ -311,16 +362,12 @@ public void deleteRealm(String slug, boolean cleanup) throws NoSuchRealmExceptio } // groups - Collection groups = groupManager.getGroups(slug); + Collection groups = groupService.listGroups(slug); for (Group group : groups) { - try { - String groupId = group.getGroupId(); + String groupId = group.getGroupId(); - // remove, should cleanup user association for leftovers - groupManager.deleteGroup(slug, groupId); - } catch (NoSuchGroupException e) { - // skip - } + // remove, should cleanup user association for leftovers + groupService.deleteGroup(slug, groupId); } // roles @@ -335,6 +382,17 @@ public void deleteRealm(String slug, boolean cleanup) throws NoSuchRealmExceptio // skip } } + + // templates + Collection templates = templateService.listTemplatesByRealm(slug); + for (TemplateModel template : templates) { + try { + String templateId = template.getId(); + templateService.deleteTemplate(templateId); + } catch (Exception e) { + // skip + } + } } // remove realm @@ -448,4 +506,53 @@ public ApplicationProperties getRealmProps(String realm) throws NoSuchRealmExcep return props; } + + /* + * Realm full config + */ + + public RealmConfig getRealmConfig(String realm) throws NoSuchRealmException { + // load realm + Realm r = realmService.getRealm(realm); + + // build config + RealmConfig rc = new RealmConfig(r); + + // providers + Collection idps = identityProviderService.listProviders(realm); + Collection aps = attributeProviderService.listProviders(realm); + Collection tps = templateProviderService.listProviders(realm); + + rc.setIdentityProviders(new ArrayList<>(idps)); + rc.setAttributeProviders(new ArrayList<>(aps)); + + if (!tps.isEmpty()) { + // pick first as config + // TODO refactor + rc.setTemplates(tps.iterator().next()); + } + + // services + List services = servicesManager.listServices(realm); + rc.setServices(services); + + // clients + Collection apps = clientManager.listClientApps(realm); + rc.setClientApps(new ArrayList<>(apps)); + + // user accounts + List internalUsers = internalUserAccountService.findAccountByRealm(realm); + List oidcUsers = oidcUserAccountService.findAccountByRealm(realm); + List samlUsers = samlUserAccountService.findAccountByRealm(realm); + + List users = Stream.of(internalUsers, oidcUsers, samlUsers) + .flatMap(l -> l.stream()) + .collect(Collectors.toList()); + + rc.setUsers(users); + // credentials + // TODO + + return rc; + } } diff --git a/src/main/java/it/smartcommunitylab/aac/core/authorities/ProviderAuthority.java b/src/main/java/it/smartcommunitylab/aac/core/authorities/ProviderAuthority.java index 67b844a7e..b932cad28 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/authorities/ProviderAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/core/authorities/ProviderAuthority.java @@ -23,7 +23,7 @@ public interface ProviderAuthority on resource + // type should match configurableProvider type public String getType(); /* @@ -44,7 +44,7 @@ public interface ProviderAuthority> implements AuthorityService { diff --git a/src/main/java/it/smartcommunitylab/aac/core/base/AbstractSingleProviderAuthority.java b/src/main/java/it/smartcommunitylab/aac/core/base/AbstractSingleProviderAuthority.java index ee963ccf6..51f675aa7 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/base/AbstractSingleProviderAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/core/base/AbstractSingleProviderAuthority.java @@ -23,10 +23,9 @@ public AbstractSingleProviderAuthority( } @Override - public S registerProvider(T cp) { + public C registerProvider(ConfigurableProvider cp) { if (cp != null - && getAuthorityId().equals(cp.getAuthority()) - && getType().equals(cp.getType())) { + && getAuthorityId().equals(cp.getAuthority())) { // enforce single per realm String realm = cp.getRealm(); if (!registrationRepository.findByRealm(realm).isEmpty()) { diff --git a/src/main/java/it/smartcommunitylab/aac/core/base/AbstractUserCredentials.java b/src/main/java/it/smartcommunitylab/aac/core/base/AbstractUserCredentials.java index 92dfbf78b..f9bb64c1d 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/base/AbstractUserCredentials.java +++ b/src/main/java/it/smartcommunitylab/aac/core/base/AbstractUserCredentials.java @@ -1,8 +1,19 @@ package it.smartcommunitylab.aac.core.base; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; + import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.core.model.UserCredentials; +import it.smartcommunitylab.aac.password.persistence.InternalUserPassword; +import it.smartcommunitylab.aac.webauthn.persistence.WebAuthnUserCredential; +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") +@JsonSubTypes({ + @Type(value = WebAuthnUserCredential.class, name = "credentials_webauthn"), + @Type(value = InternalUserPassword.class, name = "credentials_password") +}) public abstract class AbstractUserCredentials extends AbstractBaseUserResource implements UserCredentials { private static final long serialVersionUID = SystemKeys.AAC_CORE_SERIAL_VERSION; @@ -10,4 +21,11 @@ protected AbstractUserCredentials(String authority, String provider, String real super(authority, provider, realm, userId); } + public abstract String getStatus(); + + public abstract void setStatus(String status); + + public abstract void setRealm(String realm); + + public abstract void setAccountId(String accountId); } diff --git a/src/main/java/it/smartcommunitylab/aac/core/base/DefaultAccountImpl.java b/src/main/java/it/smartcommunitylab/aac/core/base/DefaultAccountImpl.java deleted file mode 100644 index 4bab3c8cd..000000000 --- a/src/main/java/it/smartcommunitylab/aac/core/base/DefaultAccountImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -package it.smartcommunitylab.aac.core.base; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import it.smartcommunitylab.aac.SystemKeys; -import it.smartcommunitylab.aac.model.SubjectStatus; - -/* - * An instantiable user account. - */ - -@JsonInclude(Include.NON_NULL) -public class DefaultAccountImpl extends AbstractAccount { - - private static final long serialVersionUID = SystemKeys.AAC_CORE_SERIAL_VERSION; - - private String id; - private String uuid; - private String username; - private String emailAddress; - private Boolean emailVerified; - private String status; - private Map attributes = new HashMap<>(); - - public DefaultAccountImpl(String authority, String provider, String realm) { - super(authority, provider, realm); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - - @Override - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - @Override - public String getEmailAddress() { - return emailAddress; - } - - public void setEmailAddress(String emailAddress) { - this.emailAddress = emailAddress; - } - - public Boolean getEmailVerified() { - return emailVerified; - } - - public void setEmailVerified(Boolean emailVerified) { - this.emailVerified = emailVerified; - } - - public boolean isEmailVerified() { - return emailVerified != null ? emailVerified.booleanValue() : false; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - - public void addAttribute(String key, String value) { - this.attributes.put(key, value); - } - - public String getAttribute(String key) { - return this.attributes.get(key); - } - - @Override - public boolean isLocked() { - return SubjectStatus.LOCKED.getValue().equals(status); - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - -} diff --git a/src/main/java/it/smartcommunitylab/aac/core/model/ConfigurableProvider.java b/src/main/java/it/smartcommunitylab/aac/core/model/ConfigurableProvider.java index d7043b74b..181e241ae 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/model/ConfigurableProvider.java +++ b/src/main/java/it/smartcommunitylab/aac/core/model/ConfigurableProvider.java @@ -15,15 +15,29 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonProperty.Access; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import it.smartcommunitylab.aac.SystemKeys; +import it.smartcommunitylab.aac.internal.persistence.InternalUserAccount; +import it.smartcommunitylab.aac.openid.persistence.OIDCUserAccount; +import it.smartcommunitylab.aac.saml.persistence.SamlUserAccount; @Valid @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) @ConstructorBinding +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") +@JsonSubTypes({ + @Type(value = ConfigurableAccountService.class, name = SystemKeys.RESOURCE_ACCOUNT), + @Type(value = ConfigurableAttributeProvider.class, name = SystemKeys.RESOURCE_ATTRIBUTES), + @Type(value = ConfigurableCredentialsService.class, name = SystemKeys.RESOURCE_CREDENTIALS), + @Type(value = ConfigurableIdentityProvider.class, name = SystemKeys.RESOURCE_IDENTITY), + @Type(value = ConfigurableTemplateProvider.class, name = SystemKeys.RESOURCE_TEMPLATE) +}) public class ConfigurableProvider implements ConfigurableProperties { @NotBlank diff --git a/src/main/java/it/smartcommunitylab/aac/core/persistence/UserEntity.java b/src/main/java/it/smartcommunitylab/aac/core/persistence/UserEntity.java index 738257c91..8ac973bf3 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/persistence/UserEntity.java +++ b/src/main/java/it/smartcommunitylab/aac/core/persistence/UserEntity.java @@ -203,4 +203,11 @@ public boolean isExpired() { return false; } + @Override + public String toString() { + return "UserEntity [uuid=" + uuid + ", realm=" + realm + ", username=" + username + ", emailAddress=" + + emailAddress + ", emailVerified=" + emailVerified + ", status=" + status + ", expirationDate=" + + expirationDate + ", createDate=" + createDate + ", modifiedDate=" + modifiedDate + "]"; + } + } diff --git a/src/main/java/it/smartcommunitylab/aac/core/provider/UserAccountService.java b/src/main/java/it/smartcommunitylab/aac/core/provider/UserAccountService.java index 996ab9ab8..c83ca9ee3 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/provider/UserAccountService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/provider/UserAccountService.java @@ -1,25 +1,31 @@ package it.smartcommunitylab.aac.core.provider; import java.util.List; + +import javax.validation.constraints.NotNull; + import it.smartcommunitylab.aac.common.NoSuchUserException; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.core.model.UserAccount; public interface UserAccountService { - public U findAccountById(String repository, String id); + public List findAccountByRealm(@NotNull String realm); + + public U findAccountById(@NotNull String repository, @NotNull String id); - public U findAccountByUuid(String repository, String uuid); + public U findAccountByUuid(@NotNull String repository, @NotNull String uuid); - public List findAccountByUsername(String repository, String username); + public List findAccountByUsername(@NotNull String repository, @NotNull String username); - public List findAccountByEmail(String repository, String email); + public List findAccountByEmail(@NotNull String repository, @NotNull String email); - public List findAccountByUser(String repository, String userId); + public List findAccountByUser(@NotNull String repository, @NotNull String userId); - public U addAccount(String repository, String id, U reg) throws RegistrationException; + public U addAccount(@NotNull String repository, @NotNull String id, @NotNull U reg) throws RegistrationException; - public U updateAccount(String repository, String id, U reg) throws NoSuchUserException, RegistrationException; + public U updateAccount(@NotNull String repository, @NotNull String id, @NotNull U reg) + throws NoSuchUserException, RegistrationException; - public void deleteAccount(String repository, String id); + public void deleteAccount(@NotNull String repository, @NotNull String id); } diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceAuthorityService.java b/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceAuthorityService.java index 6dae3a04c..c70fd4307 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceAuthorityService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceAuthorityService.java @@ -8,14 +8,14 @@ import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.core.authorities.AccountServiceAuthority; import it.smartcommunitylab.aac.core.base.AbstractAuthorityService; +import it.smartcommunitylab.aac.core.model.ConfigurableAccountService; @Service public class AccountServiceAuthorityService extends AbstractAuthorityService> implements InitializingBean { public AccountServiceAuthorityService(Collection> authorities) { - super(SystemKeys.RESOURCE_ACCOUNT); - + super(ConfigurableAccountService.class.getName()); this.setAuthorities(authorities); } diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceService.java b/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceService.java index 994eaebf9..556467dbd 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/AccountServiceService.java @@ -5,44 +5,28 @@ import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import it.smartcommunitylab.aac.common.NoSuchAuthorityException; +import it.smartcommunitylab.aac.core.authorities.AccountServiceAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableAccountService; import it.smartcommunitylab.aac.core.persistence.AccountServiceEntity; -import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @Service @Transactional public class AccountServiceService - extends ConfigurableProviderService { + extends + ConfigurableProviderService, ConfigurableAccountService, AccountServiceEntity> { - private AccountServiceAuthorityService authorityService; - - public AccountServiceService(ConfigurableProviderEntityService providerService) { - super(providerService); + public AccountServiceService(AccountServiceAuthorityService authorityService, + ConfigurableProviderEntityService providerService) { + super(authorityService, providerService); // set converters setConfigConverter(new AccountServiceConfigConverter()); setEntityConverter(new AccountServiceEntityConverter()); - - // create system services - // internal for system is exposed by internal idp by default - } - - @Autowired - public void setAuthorityService(AccountServiceAuthorityService authorityService) { - this.authorityService = authorityService; - } - - @Override - protected ConfigurationProvider getConfigurationProvider(String authority) - throws NoSuchAuthorityException { - return authorityService.getAuthority(authority).getConfigurationProvider(); } class AccountServiceConfigConverter implements Converter { diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/AttributeProviderService.java b/src/main/java/it/smartcommunitylab/aac/core/service/AttributeProviderService.java index cb93905af..de1e17f0d 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/AttributeProviderService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/AttributeProviderService.java @@ -7,43 +7,32 @@ import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import it.smartcommunitylab.aac.SystemKeys; -import it.smartcommunitylab.aac.common.NoSuchAuthorityException; import it.smartcommunitylab.aac.common.RegistrationException; +import it.smartcommunitylab.aac.core.authorities.AttributeProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableAttributeProvider; import it.smartcommunitylab.aac.core.persistence.AttributeProviderEntity; -import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @Service @Transactional public class AttributeProviderService - extends ConfigurableProviderService { + extends + ConfigurableProviderService, ConfigurableAttributeProvider, AttributeProviderEntity> { - private AttributeProviderAuthorityService authorityService; + public AttributeProviderService(AttributeProviderAuthorityService authorityService, + ConfigurableProviderEntityService providerService) { + super(authorityService, providerService); - public AttributeProviderService(ConfigurableProviderEntityService providerService) { - super(providerService); + // set converters setEntityConverter(new AttributeProviderEntityConverter()); setConfigConverter(new AttributeProviderConfigConverter()); } - @Autowired - public void setAuthorityService(AttributeProviderAuthorityService authorityService) { - this.authorityService = authorityService; - } - - @Override - protected ConfigurationProvider getConfigurationProvider(String authority) - throws NoSuchAuthorityException { - return authorityService.getAuthority(authority).getConfigurationProvider(); - } - class AttributeProviderEntityConverter implements Converter { diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/ConfigurableProviderService.java b/src/main/java/it/smartcommunitylab/aac/core/service/ConfigurableProviderService.java index 115fed4ae..cfc4eedaa 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/ConfigurableProviderService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/ConfigurableProviderService.java @@ -25,8 +25,11 @@ import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.common.NoSuchAuthorityException; import it.smartcommunitylab.aac.common.NoSuchProviderException; +import it.smartcommunitylab.aac.common.NoSuchRealmException; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.common.SystemException; +import it.smartcommunitylab.aac.core.authorities.AuthorityService; +import it.smartcommunitylab.aac.core.authorities.ProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigMap; import it.smartcommunitylab.aac.core.model.ConfigurableProperties; import it.smartcommunitylab.aac.core.model.ConfigurableProvider; @@ -34,11 +37,12 @@ import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @Transactional -public abstract class ConfigurableProviderService +public abstract class ConfigurableProviderService, C extends ConfigurableProvider, E extends ProviderEntity> implements InitializingBean { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ProviderEntityService providerService; + protected final ProviderEntityService providerService; + protected final AuthorityService authorityService; // keep a local map for system providers since these are not in db // key is providerId @@ -49,10 +53,13 @@ public abstract class ConfigurableProviderService configConverter; protected Converter entityConverter; - public ConfigurableProviderService(ProviderEntityService providerService) { + public ConfigurableProviderService(AuthorityService providerAuthorityService, + ProviderEntityService providerService) { + Assert.notNull(providerAuthorityService, "authority service is required"); Assert.notNull(providerService, "provider entity service is required"); this.providerService = providerService; + this.authorityService = providerAuthorityService; } @Override @@ -74,9 +81,14 @@ public void setEntityConverter(Converter entityConverter) { this.entityConverter = entityConverter; } - protected abstract ConfigurationProvider getConfigurationProvider(String authority) - throws NoSuchAuthorityException; + protected ConfigurationProvider getConfigurationProvider(String authority) + throws NoSuchAuthorityException { + return authorityService.getAuthority(authority).getConfigurationProvider(); + } + /* + * CRUD via entities + */ @Transactional(readOnly = true) public Collection listProviders(String realm) { logger.debug("list providers for realm {}", StringUtils.trimAllWhitespace(realm)); @@ -239,6 +251,41 @@ public void deleteProvider(String providerId) providerService.deleteProvider(pe.getProvider()); } + /* + * Config via authorities + */ + + public void registerProvider(String providerId) + throws NoSuchRealmException, NoSuchProviderException, NoSuchAuthorityException, RegistrationException { + logger.debug("register provider {}", StringUtils.trimAllWhitespace(providerId)); + + // fetch, only persisted configurations can be registered + C cp = getProvider(providerId); + + // always register and pop up errors + authorityService.getAuthority(cp.getAuthority()).registerProvider(cp); + } + + public void unregisterProvider(String providerId) + throws NoSuchProviderException, SystemException, NoSuchAuthorityException { + logger.debug("unregister provider {}", StringUtils.trimAllWhitespace(providerId)); + + // fetch, only persisted configurations can be registered + C cp = getProvider(providerId); + + // always unregister, when not active nothing will happen + authorityService.getAuthority(cp.getAuthority()).unregisterProvider(cp.getProvider()); + } + + public boolean isProviderRegistered(String providerId) throws NoSuchProviderException, NoSuchAuthorityException { + // fetch, only persisted configurations can be registered + C cp = getProvider(providerId); + + // ask authority + return authorityService.getAuthority(cp.getAuthority()).hasProvider(cp.getProvider()); + + } + /* * Configuration schemas */ diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/CredentialsServiceService.java b/src/main/java/it/smartcommunitylab/aac/core/service/CredentialsServiceService.java index e3d11301c..1f2b59cf8 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/CredentialsServiceService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/CredentialsServiceService.java @@ -5,26 +5,24 @@ import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import it.smartcommunitylab.aac.common.NoSuchAuthorityException; +import it.smartcommunitylab.aac.core.authorities.CredentialsServiceAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableCredentialsService; import it.smartcommunitylab.aac.core.persistence.CredentialsServiceEntity; -import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @Service @Transactional public class CredentialsServiceService - extends ConfigurableProviderService { + extends + ConfigurableProviderService, ConfigurableCredentialsService, CredentialsServiceEntity> { - private CredentialsServiceAuthorityService authorityService; - - public CredentialsServiceService(ConfigurableProviderEntityService providerService) { - super(providerService); + public CredentialsServiceService(CredentialsServiceAuthorityService authorityService, + ConfigurableProviderEntityService providerService) { + super(authorityService, providerService); // set converters this.setConfigConverter(new CredentialsServiceConfigConverter()); @@ -34,17 +32,6 @@ public CredentialsServiceService(ConfigurableProviderEntityService getConfigurationProvider(String authority) - throws NoSuchAuthorityException { - return authorityService.getAuthority(authority).getConfigurationProvider(); - } - class CredentialsServiceConfigConverter implements Converter { @@ -103,7 +90,7 @@ public ConfigurableCredentialsService convert(CredentialsServiceEntity pe) { cp.setName(pe.getName()); cp.setTitleMap(pe.getTitleMap()); cp.setDescriptionMap(pe.getDescriptionMap()); - + cp.setRepositoryId(pe.getRepositoryId()); cp.setConfiguration(pe.getConfigurationMap()); cp.setEnabled(pe.isEnabled()); diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/IdentityProviderService.java b/src/main/java/it/smartcommunitylab/aac/core/service/IdentityProviderService.java index 2c15c954a..20f25a7e2 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/IdentityProviderService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/IdentityProviderService.java @@ -1,9 +1,11 @@ package it.smartcommunitylab.aac.core.service; +import java.io.Serializable; import java.util.Collections; import java.util.Map; import java.util.stream.Collectors; +import org.apache.commons.lang3.RandomStringUtils; import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; import org.slf4j.Logger; @@ -13,13 +15,15 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import org.springframework.validation.DataBinder; + import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.common.NoSuchAuthorityException; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.common.SystemException; -import it.smartcommunitylab.aac.config.IdentityAuthoritiesProperties; import it.smartcommunitylab.aac.config.ProvidersProperties; -import it.smartcommunitylab.aac.config.ProvidersProperties.ProviderConfiguration; +import it.smartcommunitylab.aac.core.authorities.IdentityProviderAuthority; +import it.smartcommunitylab.aac.core.model.ConfigMap; import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; import it.smartcommunitylab.aac.core.persistence.IdentityProviderEntity; import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @@ -27,14 +31,13 @@ @Service @Transactional public class IdentityProviderService - extends ConfigurableProviderService { + extends + ConfigurableProviderService, ConfigurableIdentityProvider, IdentityProviderEntity> { private final Logger logger = LoggerFactory.getLogger(getClass()); - private IdentityProviderAuthorityService authorityService; - - public IdentityProviderService(ConfigurableProviderEntityService providerService, - IdentityAuthoritiesProperties authoritiesProperties, ProvidersProperties providers) { - super(providerService); + public IdentityProviderService(IdentityProviderAuthorityService authorityService, + ConfigurableProviderEntityService providerService) { + super(authorityService, providerService); // set converters this.setConfigConverter(new IdentityProviderConfigConverter()); @@ -59,77 +62,82 @@ public IdentityProviderService(ConfigurableProviderEntityService configProvider = getConfigurationProvider(authority); + ConfigMap configurable = configProvider.getConfigMap(cp.getConfiguration()); + + // check with validator + if (validator != null) { + DataBinder binder = new DataBinder(configurable); + validator.validate(configurable, binder.getBindingResult()); + if (binder.getBindingResult().hasErrors()) { + StringBuilder sb = new StringBuilder(); + binder.getBindingResult().getFieldErrors().forEach(e -> { + sb.append(e.getField()).append(" ").append(e.getDefaultMessage()); + }); + String errorMsg = sb.toString(); + throw new RegistrationException(errorMsg); + } } - if (providerConfig.getEnable() != null && !providerConfig.isEnabled()) { - continue; - } + Map configuration = configurable.getConfiguration(); - // we handle only system providers, add others via bootstrap - if (SystemKeys.REALM_SYSTEM.equals(providerConfig.getRealm()) - || !StringUtils.hasText(providerConfig.getRealm())) { - logger.debug( - "configure provider for " + providerConfig.getType() + " for system realm: " - + providerConfig.toString()); - - // translate config - ConfigurableIdentityProvider provider = new ConfigurableIdentityProvider( - providerConfig.getAuthority(), - providerConfig.getProvider(), SystemKeys.REALM_SYSTEM); - provider.setName(providerConfig.getName()); - provider.setTitleMap(providerConfig.getTitle()); - provider.setDescriptionMap(providerConfig.getDescription()); - provider.setEnabled(true); - for (Map.Entry entry : providerConfig.getConfiguration().entrySet()) { - provider.setConfigurationProperty(entry.getKey(), entry.getValue()); - } + // build a new config to detach from props + ConfigurableIdentityProvider cip = new ConfigurableIdentityProvider(authority, providerId, + SystemKeys.REALM_SYSTEM); + cip.setName(cp.getName()); + cip.setTitleMap(cp.getTitleMap()); + cip.setDescriptionMap(cip.getDescriptionMap()); - // by default global providers persist account + attributes - String persistenceLevel = SystemKeys.PERSISTENCE_LEVEL_REPOSITORY; - if (StringUtils.hasText(providerConfig.getPersistence())) { - // set persistence level - persistenceLevel = providerConfig.getPersistence(); - } - provider.setPersistence(persistenceLevel); + cip.setLinkable(true); + cip.setPersistence(SystemKeys.PERSISTENCE_LEVEL_REPOSITORY); + cip.setEvents(SystemKeys.EVENTS_LEVEL_DETAILS); + cip.setPosition(cp.getPosition()); - // set default event level - String eventsLevel = SystemKeys.EVENTS_LEVEL_DETAILS; - if (StringUtils.hasText(providerConfig.getEvents())) { - // set persistence level - eventsLevel = providerConfig.getEvents(); - } - provider.setEvents(eventsLevel); + cip.setEnabled(true); + cip.setConfiguration(configuration); - // register - systemProviders.put(provider.getProvider(), provider); - } - } catch (RegistrationException | SystemException | IllegalArgumentException ex) { + // register + systemProviders.put(providerId, cip); + + } catch (RegistrationException | SystemException | IllegalArgumentException + | NoSuchAuthorityException ex) { logger.error("error configuring provider :" + ex.getMessage(), ex); } } } } - @Autowired - public void setAuthorityService(IdentityProviderAuthorityService authorityService) { - this.authorityService = authorityService; - } - - @Override - protected ConfigurationProvider getConfigurationProvider(String authority) - throws NoSuchAuthorityException { - return authorityService.getAuthority(authority).getConfigurationProvider(); - } - static class IdentityProviderEntityConverter implements Converter { diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/IdentityServiceService.java b/src/main/java/it/smartcommunitylab/aac/core/service/IdentityServiceService.java index 7ef755fd0..f022eebee 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/IdentityServiceService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/IdentityServiceService.java @@ -5,62 +5,28 @@ import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import it.smartcommunitylab.aac.common.NoSuchAuthorityException; +import it.smartcommunitylab.aac.core.authorities.IdentityServiceAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableIdentityService; import it.smartcommunitylab.aac.core.persistence.IdentityServiceEntity; -import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @Service @Transactional public class IdentityServiceService - extends ConfigurableProviderService { + extends + ConfigurableProviderService, ConfigurableIdentityService, IdentityServiceEntity> { - private IdentityServiceAuthorityService authorityService; - - public IdentityServiceService(ConfigurableProviderEntityService providerService) { - super(providerService); + public IdentityServiceService(IdentityServiceAuthorityService authorityService, + ConfigurableProviderEntityService providerService) { + super(authorityService, providerService); // set converters setConfigConverter(new IdentityServiceConfigConverter()); setEntityConverter(new IdentityServiceEntityConverter()); - - // create system services - // internal service is exposed by internal idp -// // enable internal for system by default -// ConfigurableIdentityService internalConfig = new ConfigurableIdentityService( -// SystemKeys.AUTHORITY_INTERNAL, SystemKeys.AUTHORITY_INTERNAL, -// SystemKeys.REALM_SYSTEM); -// logger.debug("configure internal service for system realm"); -// systemProviders.put(internalConfig.getProvider(), internalConfig); - -// ConfigurableIdentityService oidcConfig = new ConfigurableIdentityService( -// SystemKeys.AUTHORITY_OIDC, SystemKeys.AUTHORITY_OIDC, -// SystemKeys.REALM_SYSTEM); -// logger.debug("configure oidc service for system realm"); -// systemProviders.put(oidcConfig.getProvider(), oidcConfig); -// -// ConfigurableIdentityService samlConfig = new ConfigurableIdentityService( -// SystemKeys.AUTHORITY_SAML, SystemKeys.AUTHORITY_SAML, -// SystemKeys.REALM_SYSTEM); -// logger.debug("configure saml service for system realm"); -// systemProviders.put(samlConfig.getProvider(), samlConfig); - } - - @Autowired - public void setAuthorityService(IdentityServiceAuthorityService authorityService) { - this.authorityService = authorityService; - } - - @Override - protected ConfigurationProvider getConfigurationProvider(String authority) - throws NoSuchAuthorityException { - return authorityService.getAuthority(authority).getConfigurationProvider(); } class IdentityServiceConfigConverter implements Converter { diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/ProviderAuthorityService.java b/src/main/java/it/smartcommunitylab/aac/core/service/ProviderAuthorityService.java new file mode 100644 index 000000000..a318ca751 --- /dev/null +++ b/src/main/java/it/smartcommunitylab/aac/core/service/ProviderAuthorityService.java @@ -0,0 +1,65 @@ +package it.smartcommunitylab.aac.core.service; + +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; + +import it.smartcommunitylab.aac.common.NoSuchProviderException; +import it.smartcommunitylab.aac.core.authorities.AuthorityService; + +@Service +public class ProviderAuthorityService { + private final Map> services; + + public ProviderAuthorityService(Collection> services) { + this.services = services.stream().collect(Collectors.toMap(s -> s.getType(), s -> s)); + } + + public AuthorityService findAuthorityService(String type) { + return services.get(type); + } + + public AuthorityService getAuthorityService(String type) throws NoSuchProviderException { + AuthorityService as = findAuthorityService(type); + if (as == null) { + throw new NoSuchProviderException(); + } + + return as; + } + +// public ResourceProvider registerResourceProvider(ConfigurableProvider cp) +// throws NoSuchAuthorityException, NoSuchProviderException { +// if (cp == null) { +// throw new IllegalArgumentException("invalid provider"); +// } +// +// if (!cp.isEnabled()) { +// throw new IllegalArgumentException("provider is not enabled"); +// } +// +// // authority type is the cp type +// ProviderAuthority, ?, ? extends ConfigurableProvider, ?, ?> pa = getAuthorityService( +// cp.getType()).getAuthority(cp.getAuthority()); +// +// // register directly with authority +// ResourceProvider p = pa.registerProvider(cp); +// return p; +// } +// +// public void unregisterResourceProvider(ConfigurableProvider cp) +// throws NoSuchAuthorityException, NoSuchProviderException { +// if (cp == null) { +// throw new IllegalArgumentException("invalid provider"); +// } +// +// // authority type is the cp type +// ProviderAuthority, ?, ? extends ConfigurableProvider, ?, ?> pa = getAuthorityService( +// cp.getType()).getAuthority(cp.getAuthority()); +// +// pa.unregisterProvider(cp.getProvider()); +// } + +} diff --git a/src/main/java/it/smartcommunitylab/aac/core/service/TemplateProviderService.java b/src/main/java/it/smartcommunitylab/aac/core/service/TemplateProviderService.java index f28495378..dbfd8821c 100644 --- a/src/main/java/it/smartcommunitylab/aac/core/service/TemplateProviderService.java +++ b/src/main/java/it/smartcommunitylab/aac/core/service/TemplateProviderService.java @@ -5,42 +5,28 @@ import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import it.smartcommunitylab.aac.common.NoSuchAuthorityException; +import it.smartcommunitylab.aac.core.authorities.TemplateProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableTemplateProvider; import it.smartcommunitylab.aac.core.persistence.TemplateProviderEntity; -import it.smartcommunitylab.aac.core.provider.ConfigurationProvider; @Service @Transactional public class TemplateProviderService - extends ConfigurableProviderService { + extends + ConfigurableProviderService, ConfigurableTemplateProvider, TemplateProviderEntity> { - private TemplateProviderAuthorityService authorityService; - - public TemplateProviderService(ConfigurableProviderEntityService providerService) { - super(providerService); + public TemplateProviderService(TemplateProviderAuthorityService authorityService, + ConfigurableProviderEntityService providerService) { + super(authorityService, providerService); // set converters setConfigConverter(new TemplateProviderConfigConverter()); setEntityConverter(new TemplateProviderEntityConverter()); - - } - - @Autowired - public void setAuthorityService(TemplateProviderAuthorityService authorityService) { - this.authorityService = authorityService; - } - - @Override - protected ConfigurationProvider getConfigurationProvider(String authority) - throws NoSuchAuthorityException { - return authorityService.getAuthority(authority).getConfigurationProvider(); } class TemplateProviderConfigConverter implements Converter { diff --git a/src/main/java/it/smartcommunitylab/aac/dto/RealmConfig.java b/src/main/java/it/smartcommunitylab/aac/dto/RealmConfig.java new file mode 100644 index 000000000..822821c24 --- /dev/null +++ b/src/main/java/it/smartcommunitylab/aac/dto/RealmConfig.java @@ -0,0 +1,124 @@ +package it.smartcommunitylab.aac.dto; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +import org.springframework.util.Assert; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +import it.smartcommunitylab.aac.core.base.AbstractAccount; +import it.smartcommunitylab.aac.core.base.AbstractUserCredentials; +import it.smartcommunitylab.aac.core.model.ConfigurableAttributeProvider; +import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; +import it.smartcommunitylab.aac.core.model.ConfigurableTemplateProvider; +import it.smartcommunitylab.aac.core.model.UserCredentials; +import it.smartcommunitylab.aac.model.ClientApp; +import it.smartcommunitylab.aac.model.Realm; +import it.smartcommunitylab.aac.services.Service; + +@Valid +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class RealmConfig { + + // realm + @JsonUnwrapped + @NotNull + private Realm realm; + + // providers config + private List identityProviders; + private List attributeProviders; + private ConfigurableTemplateProvider templates; + + // services + private List services; + + // clientApps + private List clientApps; + + // user accounts + private List users; + + // credentials + private List credentials; + + public RealmConfig() { + } + + public RealmConfig(Realm r) { + Assert.notNull(r, "realm can not be null"); + this.realm = r; + } + + public Realm getRealm() { + return realm; + } + + public void setRealm(Realm realm) { + this.realm = realm; + } + + public List getIdentityProviders() { + return identityProviders; + } + + public void setIdentityProviders(List identityProviders) { + this.identityProviders = identityProviders; + } + + public List getAttributeProviders() { + return attributeProviders; + } + + public void setAttributeProviders(List attributeProviders) { + this.attributeProviders = attributeProviders; + } + + public ConfigurableTemplateProvider getTemplates() { + return templates; + } + + public void setTemplates(ConfigurableTemplateProvider templates) { + this.templates = templates; + } + + public List getServices() { + return services; + } + + public void setServices(List services) { + this.services = services; + } + + public List getClientApps() { + return clientApps; + } + + public void setClientApps(List clientApps) { + this.clientApps = clientApps; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public List getCredentials() { + return credentials; + } + + public void setCredentials(List credentials) { + this.credentials = credentials; + } + +} diff --git a/src/main/java/it/smartcommunitylab/aac/internal/InternalAccountServiceAuthority.java b/src/main/java/it/smartcommunitylab/aac/internal/InternalAccountServiceAuthority.java index 641ee9da4..5f41136db 100644 --- a/src/main/java/it/smartcommunitylab/aac/internal/InternalAccountServiceAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/internal/InternalAccountServiceAuthority.java @@ -9,6 +9,7 @@ import it.smartcommunitylab.aac.core.base.AbstractSingleProviderAuthority; import it.smartcommunitylab.aac.core.entrypoint.RealmAwareUriBuilder; import it.smartcommunitylab.aac.core.model.ConfigurableAccountService; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.FilterProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.provider.UserAccountService; @@ -105,7 +106,7 @@ public FilterProvider getFilterProvider() { } @Override - public InternalAccountService registerProvider(ConfigurableAccountService cp) { + public InternalAccountServiceConfig registerProvider(ConfigurableProvider cp) { throw new IllegalArgumentException("direct registration not supported"); } diff --git a/src/main/java/it/smartcommunitylab/aac/internal/InternalIdentityServiceAuthority.java b/src/main/java/it/smartcommunitylab/aac/internal/InternalIdentityServiceAuthority.java index bb25e7574..0e8207815 100644 --- a/src/main/java/it/smartcommunitylab/aac/internal/InternalIdentityServiceAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/internal/InternalIdentityServiceAuthority.java @@ -11,6 +11,7 @@ import it.smartcommunitylab.aac.core.authorities.IdentityServiceAuthority; import it.smartcommunitylab.aac.core.base.AbstractSingleProviderAuthority; import it.smartcommunitylab.aac.core.model.ConfigurableIdentityService; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.FilterProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.service.TranslatorProviderConfigRepository; @@ -90,7 +91,7 @@ public FilterProvider getFilterProvider() { } @Override - public InternalIdentityService registerProvider(ConfigurableIdentityService cp) { + public InternalIdentityServiceConfig registerProvider(ConfigurableProvider cp) { throw new IllegalArgumentException("direct registration not supported"); } diff --git a/src/main/java/it/smartcommunitylab/aac/internal/service/InternalUserAccountService.java b/src/main/java/it/smartcommunitylab/aac/internal/service/InternalUserAccountService.java index cfc87f973..ff7af41e7 100644 --- a/src/main/java/it/smartcommunitylab/aac/internal/service/InternalUserAccountService.java +++ b/src/main/java/it/smartcommunitylab/aac/internal/service/InternalUserAccountService.java @@ -43,6 +43,16 @@ public InternalUserAccountService(InternalUserAccountRepository accountRepositor this.subjectService = subjectService; } + @Transactional(readOnly = true) + public List findAccountByRealm(String realm) { + logger.debug("find account for realm {}", String.valueOf(realm)); + + List accounts = accountRepository.findByRealm(realm); + return accounts.stream().map(a -> { + return accountRepository.detach(a); + }).collect(Collectors.toList()); + } + @Transactional(readOnly = true) public InternalUserAccount findAccountById(String repository, String username) { logger.debug("find account with username {} in repository {}", String.valueOf(username), diff --git a/src/main/java/it/smartcommunitylab/aac/openid/OIDCIdentityAuthority.java b/src/main/java/it/smartcommunitylab/aac/openid/OIDCIdentityAuthority.java index f0523eece..e163ecd4d 100644 --- a/src/main/java/it/smartcommunitylab/aac/openid/OIDCIdentityAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/openid/OIDCIdentityAuthority.java @@ -15,7 +15,7 @@ import it.smartcommunitylab.aac.claims.ScriptExecutionService; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.core.base.AbstractIdentityAuthority; -import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.provider.UserAccountService; import it.smartcommunitylab.aac.openid.auth.OIDCClientRegistrationRepository; @@ -112,35 +112,26 @@ public OIDCIdentityProvider buildProvider(OIDCIdentityProviderConfig config) { } @Override - public OIDCIdentityProvider registerProvider(ConfigurableIdentityProvider cp) { - if (cp != null - && getAuthorityId().equals(cp.getAuthority()) - && SystemKeys.RESOURCE_IDENTITY.equals(cp.getType())) { - - // fetch id from config - String providerId = cp.getProvider(); - - // register and build via super - OIDCIdentityProvider idp = super.registerProvider(cp); - - try { - // extract clientRegistration from config - ClientRegistration registration = idp.getConfig().getClientRegistration(); - - // add client registration to registry - clientRegistrationRepository.addRegistration(registration); - - return idp; - } catch (Exception ex) { - // cleanup - clientRegistrationRepository.removeRegistration(providerId); + public OIDCIdentityProviderConfig registerProvider(ConfigurableProvider cp) { + // register and build via super + OIDCIdentityProviderConfig config = super.registerProvider(cp); + + // fetch id from config + String providerId = cp.getProvider(); + try { + // extract clientRegistration from config + ClientRegistration registration = config.getClientRegistration(); + + // add client registration to registry + clientRegistrationRepository.addRegistration(registration); + + return config; + } catch (Exception ex) { + // cleanup + clientRegistrationRepository.removeRegistration(providerId); - throw new RegistrationException("invalid provider configuration: " + ex.getMessage(), ex); - } - } else { - throw new IllegalArgumentException(); + throw new RegistrationException("invalid provider configuration: " + ex.getMessage(), ex); } - } @Override diff --git a/src/main/java/it/smartcommunitylab/aac/openid/apple/AppleIdentityAuthority.java b/src/main/java/it/smartcommunitylab/aac/openid/apple/AppleIdentityAuthority.java index 7b614048f..2a9eb1ba7 100644 --- a/src/main/java/it/smartcommunitylab/aac/openid/apple/AppleIdentityAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/openid/apple/AppleIdentityAuthority.java @@ -19,7 +19,7 @@ import it.smartcommunitylab.aac.claims.ScriptExecutionService; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.core.base.AbstractSingleProviderIdentityAuthority; -import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.provider.UserAccountService; import it.smartcommunitylab.aac.openid.auth.OIDCClientRegistrationRepository; @@ -101,35 +101,27 @@ public AppleIdentityProvider buildProvider(AppleIdentityProviderConfig config) { } @Override - public AppleIdentityProvider registerProvider(ConfigurableIdentityProvider cp) { - if (cp != null - && getAuthorityId().equals(cp.getAuthority()) - && SystemKeys.RESOURCE_IDENTITY.equals(cp.getType())) { + public AppleIdentityProviderConfig registerProvider(ConfigurableProvider cp) { + // register and build via super + AppleIdentityProviderConfig config = super.registerProvider(cp); - // fetch id from config - String providerId = cp.getProvider(); + // fetch id from config + String providerId = cp.getProvider(); - // register and build via super - AppleIdentityProvider idp = super.registerProvider(cp); + try { + // extract clientRegistration from config + ClientRegistration registration = config.getClientRegistration(); - try { - // extract clientRegistration from config - ClientRegistration registration = idp.getConfig().getClientRegistration(); + // add client registration to registry + clientRegistrationRepository.addRegistration(registration); - // add client registration to registry - clientRegistrationRepository.addRegistration(registration); - - return idp; - } catch (Exception ex) { - // cleanup - clientRegistrationRepository.removeRegistration(providerId); + return config; + } catch (Exception ex) { + // cleanup + clientRegistrationRepository.removeRegistration(providerId); - throw new RegistrationException("invalid provider configuration: " + ex.getMessage(), ex); - } - } else { - throw new IllegalArgumentException(); + throw new RegistrationException("invalid provider configuration: " + ex.getMessage(), ex); } - } @Override diff --git a/src/main/java/it/smartcommunitylab/aac/openid/service/OIDCUserAccountService.java b/src/main/java/it/smartcommunitylab/aac/openid/service/OIDCUserAccountService.java index b0f839ee9..8af08deef 100644 --- a/src/main/java/it/smartcommunitylab/aac/openid/service/OIDCUserAccountService.java +++ b/src/main/java/it/smartcommunitylab/aac/openid/service/OIDCUserAccountService.java @@ -42,6 +42,16 @@ public OIDCUserAccountService(OIDCUserAccountRepository accountRepository, Subje this.subjectService = subjectService; } + @Transactional(readOnly = true) + public List findAccountByRealm(String realm) { + logger.debug("find account for realm {}", String.valueOf(realm)); + + List accounts = accountRepository.findByRealm(realm); + return accounts.stream().map(a -> { + return accountRepository.detach(a); + }).collect(Collectors.toList()); + } + @Transactional(readOnly = true) public OIDCUserAccount findAccountById(String repository, String subject) { logger.debug("find account with subject {} in repository {}", String.valueOf(subject), @@ -143,7 +153,7 @@ public OIDCUserAccount addAccount(String repository, String subject, OIDCUserAcc throw new RegistrationException("subject-mismatch"); } } - + // extract attributes and build model account = new OIDCUserAccount(reg.getAuthority()); account.setProvider(repository); diff --git a/src/main/java/it/smartcommunitylab/aac/password/PasswordCredentialsAuthority.java b/src/main/java/it/smartcommunitylab/aac/password/PasswordCredentialsAuthority.java index 56ebf1603..fb5b5f2f3 100644 --- a/src/main/java/it/smartcommunitylab/aac/password/PasswordCredentialsAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/password/PasswordCredentialsAuthority.java @@ -6,7 +6,7 @@ import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.core.base.AbstractCredentialsAuthority; import it.smartcommunitylab.aac.core.entrypoint.RealmAwareUriBuilder; -import it.smartcommunitylab.aac.core.model.ConfigurableCredentialsService; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.provider.UserAccountService; import it.smartcommunitylab.aac.core.service.TranslatorProviderConfigRepository; @@ -82,7 +82,7 @@ public PasswordCredentialsService buildProvider(PasswordCredentialsServiceConfig } @Override - public PasswordCredentialsService registerProvider(ConfigurableCredentialsService cp) { + public PasswordCredentialsServiceConfig registerProvider(ConfigurableProvider cp) { throw new IllegalArgumentException("direct registration not supported"); } diff --git a/src/main/java/it/smartcommunitylab/aac/password/persistence/InternalUserPassword.java b/src/main/java/it/smartcommunitylab/aac/password/persistence/InternalUserPassword.java index 3ce3746b2..81c719b69 100644 --- a/src/main/java/it/smartcommunitylab/aac/password/persistence/InternalUserPassword.java +++ b/src/main/java/it/smartcommunitylab/aac/password/persistence/InternalUserPassword.java @@ -36,11 +36,11 @@ public class InternalUserPassword extends AbstractUserCredentials @Id private String id; - // account id @NotBlank @Column(name = "provider_id", length = 128) private String provider; + // account id @NotBlank @Column(name = "username", length = 128) private String username; @@ -138,6 +138,10 @@ public void setUsername(String username) { this.username = username; } + public void setAccountId(String username) { + this.username = username; + } + public String getPassword() { return password; } @@ -208,4 +212,11 @@ public void eraseCredentials() { this.resetKey = null; } + @Override + public String toString() { + return "InternalUserPassword [id=" + id + ", provider=" + provider + ", username=" + username + ", status=" + + status + ", createDate=" + createDate + ", expirationDate=" + expirationDate + ", resetDeadline=" + + resetDeadline + ", changeOnFirstAccess=" + changeOnFirstAccess + "]"; + } + } diff --git a/src/main/java/it/smartcommunitylab/aac/password/service/InternalUserPasswordService.java b/src/main/java/it/smartcommunitylab/aac/password/service/InternalUserPasswordService.java index 6f95d49a5..841fd2d52 100644 --- a/src/main/java/it/smartcommunitylab/aac/password/service/InternalUserPasswordService.java +++ b/src/main/java/it/smartcommunitylab/aac/password/service/InternalUserPasswordService.java @@ -151,7 +151,7 @@ public InternalUserPassword setPassword(String repositoryId, String username, St } }); - if (isReuse) { + if (isReuse && keepNumber > 0) { throw new InvalidPasswordException("password-reuse"); } diff --git a/src/main/java/it/smartcommunitylab/aac/saml/SamlIdentityAuthority.java b/src/main/java/it/smartcommunitylab/aac/saml/SamlIdentityAuthority.java index c9967dc34..1b7bfbb61 100644 --- a/src/main/java/it/smartcommunitylab/aac/saml/SamlIdentityAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/saml/SamlIdentityAuthority.java @@ -15,7 +15,7 @@ import it.smartcommunitylab.aac.claims.ScriptExecutionService; import it.smartcommunitylab.aac.common.RegistrationException; import it.smartcommunitylab.aac.core.base.AbstractIdentityAuthority; -import it.smartcommunitylab.aac.core.model.ConfigurableIdentityProvider; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.provider.UserAccountService; import it.smartcommunitylab.aac.saml.auth.SamlRelyingPartyRegistrationRepository; @@ -113,35 +113,27 @@ public SamlIdentityProvider buildProvider(SamlIdentityProviderConfig config) { } @Override - public SamlIdentityProvider registerProvider(ConfigurableIdentityProvider cp) { - if (cp != null - && getAuthorityId().equals(cp.getAuthority()) - && SystemKeys.RESOURCE_IDENTITY.equals(cp.getType())) { + public SamlIdentityProviderConfig registerProvider(ConfigurableProvider cp) { + // register and build via super + SamlIdentityProviderConfig config = super.registerProvider(cp); - // fetch id from config - String providerId = cp.getProvider(); + // fetch id from config + String providerId = cp.getProvider(); - // register and build via super - SamlIdentityProvider idp = super.registerProvider(cp); + try { + // extract clientRegistration from config + RelyingPartyRegistration registration = config.getRelyingPartyRegistration(); - try { - // extract clientRegistration from config - RelyingPartyRegistration registration = idp.getConfig().getRelyingPartyRegistration(); + // add client registration to registry + relyingPartyRegistrationRepository.addRegistration(registration); - // add client registration to registry - relyingPartyRegistrationRepository.addRegistration(registration); - - return idp; - } catch (Exception ex) { - // cleanup - relyingPartyRegistrationRepository.removeRegistration(providerId); + return config; + } catch (Exception ex) { + // cleanup + relyingPartyRegistrationRepository.removeRegistration(providerId); - throw new RegistrationException("invalid provider configuration: " + ex.getMessage(), ex); - } - } else { - throw new IllegalArgumentException(); + throw new RegistrationException("invalid provider configuration: " + ex.getMessage(), ex); } - } @Override diff --git a/src/main/java/it/smartcommunitylab/aac/saml/service/SamlUserAccountService.java b/src/main/java/it/smartcommunitylab/aac/saml/service/SamlUserAccountService.java index 2666c717e..f7392b37e 100644 --- a/src/main/java/it/smartcommunitylab/aac/saml/service/SamlUserAccountService.java +++ b/src/main/java/it/smartcommunitylab/aac/saml/service/SamlUserAccountService.java @@ -41,6 +41,16 @@ public SamlUserAccountService(SamlUserAccountRepository accountRepository, Subje this.subjectService = subjectService; } + @Transactional(readOnly = true) + public List findAccountByRealm(String realm) { + logger.debug("find account for realm {}", String.valueOf(realm)); + + List accounts = accountRepository.findByRealm(realm); + return accounts.stream().map(a -> { + return accountRepository.detach(a); + }).collect(Collectors.toList()); + } + @Transactional(readOnly = true) public SamlUserAccount findAccountById(String repository, String subjectId) { logger.debug("find account with subjectId {} in repository {}", String.valueOf(subjectId), diff --git a/src/main/java/it/smartcommunitylab/aac/templates/TemplatesManager.java b/src/main/java/it/smartcommunitylab/aac/templates/TemplatesManager.java index 8b284c35d..09534c81b 100644 --- a/src/main/java/it/smartcommunitylab/aac/templates/TemplatesManager.java +++ b/src/main/java/it/smartcommunitylab/aac/templates/TemplatesManager.java @@ -20,6 +20,7 @@ import org.springframework.util.StringUtils; import it.smartcommunitylab.aac.Config; +import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.common.InvalidDataException; import it.smartcommunitylab.aac.common.NoSuchAuthorityException; import it.smartcommunitylab.aac.common.NoSuchProviderException; @@ -50,13 +51,12 @@ public class TemplatesManager @Autowired private TemplateProviderAuthorityService authorityService; - public TemplatesManager(TemplateProviderService templateProviderService, - TemplateProviderAuthorityService templateProviderAuthorityService) { - super(templateProviderService, templateProviderAuthorityService); + public TemplatesManager(TemplateProviderService templateProviderService) { + super(templateProviderService); } /* - * Config per realm + * Config per realm we always expose a single config per realm */ public ConfigurableTemplateProvider findProviderByRealm(String realm) throws NoSuchRealmException { // we expect a single provider per realm, so fetch first @@ -64,12 +64,22 @@ public ConfigurableTemplateProvider findProviderByRealm(String realm) throws NoS } public ConfigurableTemplateProvider getProviderByRealm(String realm) - throws NoSuchProviderException, NoSuchRealmException { + throws NoSuchProviderException, NoSuchRealmException, RegistrationException { // fetch first if available ConfigurableTemplateProvider provider = findProviderByRealm(realm); if (provider == null) { - throw new NoSuchProviderException(); + // create as new + String id = SystemKeys.AUTHORITY_TEMPLATE + SystemKeys.SLUG_SEPARATOR + realm; + ConfigurableTemplateProvider cp = new ConfigurableTemplateProvider(SystemKeys.AUTHORITY_TEMPLATE, id, + realm); + + try { + provider = addProvider(realm, cp); + } catch (NoSuchAuthorityException e) { + throw new NoSuchProviderException(); + } + } // check if languages are set, otherwise use default diff --git a/src/main/java/it/smartcommunitylab/aac/webauthn/WebAuthnCredentialsAuthority.java b/src/main/java/it/smartcommunitylab/aac/webauthn/WebAuthnCredentialsAuthority.java index 1251d2383..a6b6bc362 100644 --- a/src/main/java/it/smartcommunitylab/aac/webauthn/WebAuthnCredentialsAuthority.java +++ b/src/main/java/it/smartcommunitylab/aac/webauthn/WebAuthnCredentialsAuthority.java @@ -5,7 +5,7 @@ import org.springframework.util.Assert; import it.smartcommunitylab.aac.SystemKeys; import it.smartcommunitylab.aac.core.base.AbstractCredentialsAuthority; -import it.smartcommunitylab.aac.core.model.ConfigurableCredentialsService; +import it.smartcommunitylab.aac.core.model.ConfigurableProvider; import it.smartcommunitylab.aac.core.provider.ProviderConfigRepository; import it.smartcommunitylab.aac.core.provider.UserAccountService; import it.smartcommunitylab.aac.internal.persistence.InternalUserAccount; @@ -71,7 +71,7 @@ public WebAuthnCredentialsService buildProvider(WebAuthnCredentialsServiceConfig } @Override - public WebAuthnCredentialsService registerProvider(ConfigurableCredentialsService cp) { + public WebAuthnCredentialsServiceConfig registerProvider(ConfigurableProvider cp) { throw new IllegalArgumentException("direct registration not supported"); } diff --git a/src/main/java/it/smartcommunitylab/aac/webauthn/persistence/WebAuthnUserCredential.java b/src/main/java/it/smartcommunitylab/aac/webauthn/persistence/WebAuthnUserCredential.java index 8dcc027e5..0fed572cf 100644 --- a/src/main/java/it/smartcommunitylab/aac/webauthn/persistence/WebAuthnUserCredential.java +++ b/src/main/java/it/smartcommunitylab/aac/webauthn/persistence/WebAuthnUserCredential.java @@ -191,6 +191,10 @@ public void setUsername(String username) { this.username = username; } + public void setAccountId(String username) { + this.username = username; + } + public String getUserHandle() { return userHandle; } @@ -295,4 +299,12 @@ public void setClientData(String clientData) { public void eraseCredentials() { this.publicKeyCose = null; } + + @Override + public String toString() { + return "WebAuthnUserCredential [id=" + id + ", provider=" + provider + ", username=" + username + + ", userHandle=" + userHandle + ", displayName=" + displayName + ", credentialId=" + credentialId + + ", status=" + status + ", createDate=" + createDate + ", lastUsedDate=" + lastUsedDate + "]"; + } + } diff --git a/src/main/resources/public/js/realm.js b/src/main/resources/public/js/realm.js index b9b91846d..e8addd008 100644 --- a/src/main/resources/public/js/realm.js +++ b/src/main/resources/public/js/realm.js @@ -519,7 +519,7 @@ angular.module('aac.controllers.realm', []) }; $scope.exportRealm = function () { - window.open('console/dev/realms/' + $scope.realm.slug + '/export?full=1'); + window.open('console/dev/realms/' + $scope.realm.slug + '/export?config=1'); }; $scope.deleteRealmDlg = function () {