chore: restore from backup

project was archived and never imported into my new instance of gittea, until now
This commit is contained in:
john 2025-11-24 16:42:06 -07:00
commit 872850da89
34 changed files with 2206 additions and 0 deletions

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# identity-management

110
pom.xml Normal file
View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jkgroller</groupId>
<artifactId>identity-management</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>IdentityManagement</name>
<description>Authentication with JSONAPI.</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>8</java.version>
<commons.lang3.version>3.5</commons.lang3.version>
<crnk.version>3.2.20200419165537</crnk.version>
<commons.io.version>2.5</commons.io.version>
<mapstruct.version>1.3.1.Final</mapstruct.version>
<commons-codec.version>1.14</commons-codec.version>
</properties>
<repositories>
<repository>
<id>jcenter</id>
<url>https://jcenter.bintray.com/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
</dependency>
<dependency>
<groupId>io.crnk</groupId>
<artifactId>crnk-setup-spring-boot2</artifactId>
<version>${crnk.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<finalName>IdentityManagement</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,14 @@
package com.jkgroller.identitymanagement;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class IdentityManagement {
public static void main(String[] args) {
SpringApplication.run(IdentityManagement.class, args);
}
}

View File

@ -0,0 +1,38 @@
package com.jkgroller.identitymanagement.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
@Configuration
public class KeyGeneratorConfig {
private final int IDENTIFIER_LENGTH;
private final int TOKEN_LENGTH;
/**
* This is how you inject a value from a properties file into a constant.
*/
public KeyGeneratorConfig(@Value("${identifier.length}") int identifierLength, @Value("${token.length}") int tokenLength) {
this.IDENTIFIER_LENGTH = identifierLength;
this.TOKEN_LENGTH = tokenLength;
}
/**
* @return
*/
@Bean(name = "identifierGenerator")
public BytesKeyGenerator identifierGenerator() {
return KeyGenerators.secureRandom(IDENTIFIER_LENGTH);
}
@Bean(name = "tokenGenerator")
public BytesKeyGenerator tokenGenerator() {
return KeyGenerators.secureRandom(TOKEN_LENGTH);
}
}

View File

@ -0,0 +1,42 @@
package com.jkgroller.identitymanagement.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//...
}
/**
*
* @param httpSecurity
* @throws Exception
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//...
}
/**
*
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/h2/**", "/v1/**");
}
}

View File

@ -0,0 +1,24 @@
package com.jkgroller.identitymanagement.converter;
import org.mapstruct.BeforeMapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.TargetType;
import org.springframework.stereotype.Component;
import java.util.IdentityHashMap;
import java.util.Map;
@Component
public class CycleAvoidingMappingContext {
private Map<Object, Object> knownInstances = new IdentityHashMap<Object, Object>();
@BeforeMapping
public <T> T getMappedInstance(Object source, @TargetType Class<T> targetType) {
return (T) knownInstances.get( source );
}
@BeforeMapping
public void storeMappedInstance(Object source, @MappingTarget Object target) {
knownInstances.put( source, target );
}
}

View File

@ -0,0 +1,54 @@
package com.jkgroller.identitymanagement.converter;
import com.jkgroller.identitymanagement.entity.AuthenticatedSessionEntity;
import com.jkgroller.identitymanagement.entity.IndividualCustomerEntity;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import com.jkgroller.identitymanagement.meta.OnlineAccountMeta;
import com.jkgroller.identitymanagement.resource.AuthenticatedSessionResource;
import com.jkgroller.identitymanagement.resource.IndividualCustomerResource;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
import org.mapstruct.*;
import java.util.List;
@Mapper(componentModel = "spring")
public interface IdentityManagementConverter {
@Mapping(target = "id", source = "identifier")
@Mapping(target = "dbId", source = "id")
AuthenticatedSessionResource authenticatedSessionEntityToAuthenticatedSessionResource(AuthenticatedSessionEntity authenticatedSessionEntity, @Context CycleAvoidingMappingContext context);
List<AuthenticatedSessionResource> authenticatedSessionEntitiesToAuthenticatedSessionResources(List<AuthenticatedSessionEntity> authenticatedSessionEntities, @Context CycleAvoidingMappingContext context);
@Mapping(target = "identifier", source = "id")
@Mapping(target = "id", source = "dbId")
AuthenticatedSessionEntity authenticatedSessionResourceToAuthenticatedSessionEntity(AuthenticatedSessionResource authenticatedSessionResource, @Context CycleAvoidingMappingContext context);
@Mapping(target = "id", source = "identifier")
@Mapping(target = "dbId", source = "id")
IndividualCustomerResource individualCustomerEntityToIndividualCustomerResource(IndividualCustomerEntity individualCustomerEntity, @Context CycleAvoidingMappingContext context);
List<IndividualCustomerResource> individualCustomerEntitiesToIndividualCustomerResources(List<IndividualCustomerEntity> individualCustomerEntities, @Context CycleAvoidingMappingContext context);
@Mapping(target = "identifier", source = "id")
@Mapping(target = "id", source = "dbId")
IndividualCustomerEntity individualCustomerResourceToIndividualCustomerEntity(IndividualCustomerResource individualCustomerResource, @Context CycleAvoidingMappingContext context);
@Mapping(target = "identifier", source = "id")
@Mapping(target = "id", source = "dbId")
@Mapping(target = "accountStatus", source = "onlineAccountMeta.accountStatus")
@Mapping(target = "passwordStatus", source = "onlineAccountMeta.passwordStatus")
OnlineAccountEntity onlineAccountResourceToOnlineAccountEntity(OnlineAccountResource onlineAccountResource, @Context CycleAvoidingMappingContext context);
@Mapping(target = "id", source = "identifier")
@Mapping(target = "dbId", source = "id")
OnlineAccountResource onlineAccountEntityToOnlineAccountResource(OnlineAccountEntity onlineAccountEntity, @Context CycleAvoidingMappingContext context);
List<OnlineAccountResource> onlineAccountEntitiesToOnlineAccountResources(List<OnlineAccountEntity> onlineAccountEntities, @Context CycleAvoidingMappingContext context);
@AfterMapping
default void setOnlineAccountResourceMeta(OnlineAccountEntity onlineAccountEntity, @MappingTarget OnlineAccountResource onlineAccountResource) {
OnlineAccountMeta onlineAccountMeta = new OnlineAccountMeta();
onlineAccountMeta.setAccountStatus(onlineAccountEntity.getAccountStatus());
onlineAccountMeta.setPasswordStatus(onlineAccountEntity.getPasswordStatus());
onlineAccountResource.setOnlineAccountMeta(onlineAccountMeta);
}
}

View File

@ -0,0 +1,94 @@
package com.jkgroller.identitymanagement.entity;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
@Entity(name = "authenticated_session")
public class AuthenticatedSessionEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String tokenType; // Type of token/session
private String identifier; // Universally unique identifier
private String token; // The token
private String userName;
private String expiresIn;
private String levelOfAssurance;
@OneToOne
@LazyCollection(LazyCollectionOption.FALSE)
private OnlineAccountEntity onlineAccount;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
public String getLevelOfAssurance() {
return levelOfAssurance;
}
public void setLevelOfAssurance(String levelOfAssurance) {
this.levelOfAssurance = levelOfAssurance;
}
public OnlineAccountEntity getOnlineAccount() {
return onlineAccount;
}
public void setOnlineAccount(OnlineAccountEntity onlineAccount) {
this.onlineAccount = onlineAccount;
}
}

View File

@ -0,0 +1,79 @@
package com.jkgroller.identitymanagement.entity;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
@Entity
@Table(name="individual_customer")
public class IndividualCustomerEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String firstName;
@Column
private String lastName;
@Column
private String identifier;
@Column
private String emailAddress;
@OneToOne(mappedBy = "individualCustomer")
@LazyCollection(LazyCollectionOption.FALSE)
private OnlineAccountEntity onlineAccount;
public OnlineAccountEntity getOnlineAccount() {
return onlineAccount;
}
public void setOnlineAccount(OnlineAccountEntity onlineAccount) {
this.onlineAccount = onlineAccount;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}

View File

@ -0,0 +1,144 @@
package com.jkgroller.identitymanagement.entity;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
@Entity
@Table(name="online_account")
public class OnlineAccountEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String identifier;
@Column
private String creationDate;
@Column
private String lastLoginDate;
@Column
private String accountStatus;
@Column
private String passwordStatus;
@Column(unique = true)
private String userName;
@Column
private String password;
@Column
private String oneTimePassword;
@OneToOne
@LazyCollection(LazyCollectionOption.FALSE)
private IndividualCustomerEntity individualCustomer;
@OneToOne(mappedBy = "onlineAccount")
@LazyCollection(LazyCollectionOption.FALSE)
private AuthenticatedSessionEntity authenticatedSession;
public String getOneTimePassword() {
return oneTimePassword;
}
public void setOneTimePassword(String oneTimePassword) {
this.oneTimePassword = oneTimePassword;
}
public AuthenticatedSessionEntity getAuthenticatedSession() {
return authenticatedSession;
}
public void setAuthenticatedSession(AuthenticatedSessionEntity authenticatedSession) {
this.authenticatedSession = authenticatedSession;
}
public IndividualCustomerEntity getIndividualCustomer() {
return individualCustomer;
}
public void setIndividualCustomer(IndividualCustomerEntity individualCustomer) {
this.individualCustomer = individualCustomer;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public IndividualCustomerEntity getIndividualCustomerEntity() {
return individualCustomer;
}
public void setIndividualCustomerEntity(IndividualCustomerEntity individualCustomerEntity) {
this.individualCustomer = individualCustomerEntity;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCreationDate() {
return creationDate;
}
public String getAccountStatus() {
return accountStatus;
}
public void setAccountStatus(String accountStatus) {
this.accountStatus = accountStatus;
}
public String getPasswordStatus() {
return passwordStatus;
}
public void setPasswordStatus(String passwordStatus) {
this.passwordStatus = passwordStatus;
}
public void setCreationDate(String creationDate) {
this.creationDate = creationDate;
}
public String getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(String lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
}

View File

@ -0,0 +1,24 @@
package com.jkgroller.identitymanagement.exception;
import io.crnk.core.engine.document.ErrorData;
import io.crnk.core.engine.http.HttpStatus;
import io.crnk.core.exception.CrnkMappableException;
public class CreateOnlineAccountConflictException extends CrnkMappableException {
private static final long serialVersionUID = 1L;
/**
* Normally we'd use 409 for conflicts, but that tells a hacker that an account exists.
*/
public CreateOnlineAccountConflictException() {
super(HttpStatus.BAD_REQUEST_400, createErrorData());
}
private static ErrorData createErrorData() {
return ErrorData.builder().setStatus(String.valueOf(HttpStatus.BAD_REQUEST_400))
.setDetail("Unable to create online account.").build();
}
}

View File

@ -0,0 +1,26 @@
package com.jkgroller.identitymanagement.meta;
import io.crnk.core.resource.meta.MetaInformation;
public class OnlineAccountMeta implements MetaInformation {
private String accountStatus;
private String passwordStatus;
public String getAccountStatus() {
return accountStatus;
}
public void setAccountStatus(String accountStatus) {
this.accountStatus = accountStatus;
}
public String getPasswordStatus() {
return passwordStatus;
}
public void setPasswordStatus(String passwordStatus) {
this.passwordStatus = passwordStatus;
}
}

View File

@ -0,0 +1,50 @@
package com.jkgroller.identitymanagement.relationship.repository;
import com.jkgroller.identitymanagement.converter.CycleAvoidingMappingContext;
import com.jkgroller.identitymanagement.converter.IdentityManagementConverter;
import com.jkgroller.identitymanagement.repository.AuthenticatedSessionRepository;
import com.jkgroller.identitymanagement.resource.AuthenticatedSessionResource;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.OneRelationshipRepository;
import io.crnk.core.repository.RelationshipMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class AuthenticatedSessionToOnlineAccountRelationshipRepository implements OneRelationshipRepository<AuthenticatedSessionResource, String, OnlineAccountResource, String> {
@Autowired
private AuthenticatedSessionRepository authenticatedSessionRepository;
@Autowired
private IdentityManagementConverter identityManagementConverter;
private CycleAvoidingMappingContext cycleAvoidingMappingContext;
@Override
public RelationshipMatcher getMatcher() {
return new RelationshipMatcher().rule().target(OnlineAccountResource.class).add();
}
@Override
public void setRelation(AuthenticatedSessionResource source, String targetId, String fieldName) {
}
@Override
public Map<String, OnlineAccountResource> findOneRelations(Collection<String> collection, String s, QuerySpec querySpec) {
Map<String, OnlineAccountResource> onlineAccountResources = new HashMap<String, OnlineAccountResource>();
for (String identifier : collection) {
OnlineAccountResource onlineAccountResource = identityManagementConverter.onlineAccountEntityToOnlineAccountResource(authenticatedSessionRepository.findByIdentifier(identifier).getOnlineAccount(), cycleAvoidingMappingContext);
onlineAccountResources.put(identifier, onlineAccountResource);
}
return onlineAccountResources;
}
}

View File

@ -0,0 +1,50 @@
package com.jkgroller.identitymanagement.relationship.repository;
import com.jkgroller.identitymanagement.converter.CycleAvoidingMappingContext;
import com.jkgroller.identitymanagement.converter.IdentityManagementConverter;
import com.jkgroller.identitymanagement.repository.IndividualCustomerRepository;
import com.jkgroller.identitymanagement.resource.IndividualCustomerResource;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.OneRelationshipRepository;
import io.crnk.core.repository.RelationshipMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
//
public class IndividualCustomerToOnlineAccountRelationshipRepository implements OneRelationshipRepository<IndividualCustomerResource, String, OnlineAccountResource, String> {
@Autowired
private IndividualCustomerRepository individualCustomerRepository;
@Autowired
private IdentityManagementConverter identityManagementConverter;
private CycleAvoidingMappingContext cycleAvoidingMappingContext;
@Override
public RelationshipMatcher getMatcher() {
return new RelationshipMatcher().rule().target(OnlineAccountResource.class).add();
}
@Override
public void setRelation(IndividualCustomerResource individualCustomerResource, String s, String s2) {
// Nothing
}
@Override
public Map<String, OnlineAccountResource> findOneRelations(Collection<String> collection, String s, QuerySpec querySpec) {
Map<String, OnlineAccountResource> onlineAccountResources = new HashMap<String, OnlineAccountResource>();
for (String identifier : collection) {
OnlineAccountResource onlineAccountResource = identityManagementConverter.onlineAccountEntityToOnlineAccountResource(individualCustomerRepository.findByIdentifier(identifier).getOnlineAccount(), cycleAvoidingMappingContext);
onlineAccountResources.put(identifier, onlineAccountResource);
}
return onlineAccountResources;
}
}

View File

@ -0,0 +1,17 @@
package com.jkgroller.identitymanagement.repository;
import com.jkgroller.identitymanagement.entity.AuthenticatedSessionEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
@Transactional
public interface AuthenticatedSessionRepository extends JpaRepository<AuthenticatedSessionEntity, String> {
public AuthenticatedSessionEntity findByIdentifier(String identifier);
public List<AuthenticatedSessionEntity> findByIdentifierIn(Collection<String> identifiers);
}

View File

@ -0,0 +1,17 @@
package com.jkgroller.identitymanagement.repository;
import com.jkgroller.identitymanagement.entity.IndividualCustomerEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
@Transactional
public interface IndividualCustomerRepository extends JpaRepository<IndividualCustomerEntity, String> {
public IndividualCustomerEntity findByIdentifier(String identifier);
public List<IndividualCustomerEntity> findByIdentifierIn(Collection<String> identifiers);
}

View File

@ -0,0 +1,19 @@
package com.jkgroller.identitymanagement.repository;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
@Transactional
public interface OnlineAccountRepository extends JpaRepository<OnlineAccountEntity, String> {
public OnlineAccountEntity findByIdentifier(String identifier);
public OnlineAccountEntity findByUserName(String userName);
public List<OnlineAccountEntity> findByIdentifierIn(Collection<String> identifiers);
}

View File

@ -0,0 +1,111 @@
package com.jkgroller.identitymanagement.resource;
import io.crnk.core.resource.annotations.JsonApiField;
import io.crnk.core.resource.annotations.JsonApiId;
import io.crnk.core.resource.annotations.JsonApiRelation;
import io.crnk.core.resource.annotations.JsonApiResource;
@JsonApiResource(type = "authenticatedSession", resourcePath = "authenticatedSessions")
public class AuthenticatedSessionResource {
@JsonApiId
private String id;
@JsonApiField(postable = false, readable = true, patchable = false)
private String tokenType;
@JsonApiField(postable = false, readable = true, patchable = false)
private String expiresIn;
@JsonApiField(postable = true, readable = true, patchable = false)
private String userName;
@JsonApiField(postable = true, readable = false, patchable = false)
private String password;
@JsonApiField(postable = false, readable = true, patchable = false)
private String token;
@JsonApiField(postable = false, readable = true, patchable = false)
private String levelOfAssurance;
@JsonApiField(readable = false, postable = false, patchable = false, deletable = false)
private long dbId;
@JsonApiRelation
private OnlineAccountResource onlineAccount;
public long getDbId() {
return dbId;
}
public void setDbId(long dbId) {
this.dbId = dbId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public void setOnlineAccount(OnlineAccountResource onlineAccount) {
this.onlineAccount = onlineAccount;
}
public OnlineAccountResource getOnlineAccount() {
return onlineAccount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
public String getLevelOfAssurance() {
return levelOfAssurance;
}
public void setLevelOfAssurance(String levelOfAssurance) {
this.levelOfAssurance = levelOfAssurance;
}
}

View File

@ -0,0 +1,73 @@
package com.jkgroller.identitymanagement.resource;
import io.crnk.core.resource.annotations.JsonApiField;
import io.crnk.core.resource.annotations.JsonApiId;
import io.crnk.core.resource.annotations.JsonApiRelation;
import io.crnk.core.resource.annotations.JsonApiResource;
@JsonApiResource(type = "individualCustomer", resourcePath = "individualCustomers")
public class IndividualCustomerResource {
@JsonApiId
private String id;
private String firstName;
private String lastName;
private String emailAddress;
@JsonApiField(readable = false, postable = false, patchable = false, deletable = false)
private long dbId;
@JsonApiRelation(mappedBy="individualCustomer")
private OnlineAccountResource onlineAccount;
public OnlineAccountResource getOnlineAccount() {
return onlineAccount;
}
public void setOnlineAccount(OnlineAccountResource onlineAccount) {
this.onlineAccount = onlineAccount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public long getDbId() {
return dbId;
}
public void setDbId(long dbId) {
this.dbId = dbId;
}
}

View File

@ -0,0 +1,118 @@
package com.jkgroller.identitymanagement.resource;
import com.jkgroller.identitymanagement.meta.OnlineAccountMeta;
import io.crnk.core.resource.annotations.*;
@JsonApiResource(type = "onlineAccount", resourcePath = "onlineAccounts")
public class OnlineAccountResource {
@JsonApiId
private String id;
@JsonApiField(readable = false, postable = false, patchable = false, deletable = false)
private long dbId;
@JsonApiField(readable = true, postable = false, patchable = false, deletable = false)
private String creationDate;
@JsonApiField(readable = true, postable = false, patchable = true, deletable = false)
private String lastLoginDate;
@JsonApiField(readable = true, postable = true, patchable = true, deletable = false)
private String userName;
@JsonApiField(readable = false)
private String password;
@JsonApiField(readable = false, postable = false, patchable = true, deletable = false)
private String oneTimePassword;
@JsonApiRelation
private IndividualCustomerResource individualCustomer;
@JsonApiRelation(mappedBy = "onlineAccount")
private AuthenticatedSessionResource authenticatedSession;
@JsonApiMetaInformation
private OnlineAccountMeta onlineAccountMeta;
public AuthenticatedSessionResource getAuthenticatedSession() {
return authenticatedSession;
}
public String getOneTimePassword() {
return oneTimePassword;
}
public void setOneTimePassword(String oneTimePassword) {
this.oneTimePassword = oneTimePassword;
}
public void setAuthenticatedSession(AuthenticatedSessionResource authenticatedSession) {
this.authenticatedSession = authenticatedSession;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCreationDate() {
return creationDate;
}
public void setCreationDate(String creationDate) {
this.creationDate = creationDate;
}
public String getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(String lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public IndividualCustomerResource getIndividualCustomer() {
return individualCustomer;
}
public void setIndividualCustomer(IndividualCustomerResource individualCustomer) {
this.individualCustomer = individualCustomer;
}
public OnlineAccountMeta getOnlineAccountMeta() {
return onlineAccountMeta;
}
public void setOnlineAccountMeta(OnlineAccountMeta onlineAccountMeta) {
this.onlineAccountMeta = onlineAccountMeta;
}
public long getDbId() {
return dbId;
}
public void setDbId(long dbId) {
this.dbId = dbId;
}
}

View File

@ -0,0 +1,144 @@
package com.jkgroller.identitymanagement.resource.repository;
import com.jkgroller.identitymanagement.converter.CycleAvoidingMappingContext;
import com.jkgroller.identitymanagement.converter.IdentityManagementConverter;
import com.jkgroller.identitymanagement.entity.AuthenticatedSessionEntity;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import com.jkgroller.identitymanagement.repository.AuthenticatedSessionRepository;
import com.jkgroller.identitymanagement.resource.AuthenticatedSessionResource;
import com.jkgroller.identitymanagement.service.AuthenticationService;
import com.jkgroller.identitymanagement.to.AuthenticationRequestTO;
import io.crnk.core.exception.UnauthorizedException;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.ResourceRepository;
import io.crnk.core.resource.list.ResourceList;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Random;
@Component
public class AuthenticatedSessionResourceRepository
implements ResourceRepository<AuthenticatedSessionResource, String> {
@Autowired
private AuthenticatedSessionRepository authenticatedSessionRepository;
@Autowired
private IdentityManagementConverter identityManagementConverter;
@Autowired
private AuthenticationService authenticationService;
@Autowired
@Qualifier("identifierGenerator")
private BytesKeyGenerator identifierGenerator;
@Autowired
@Qualifier("tokenGenerator")
private BytesKeyGenerator tokenGenerator;
@Autowired
private CycleAvoidingMappingContext cycleAvoidingMappingContext;
@Override
public Class<AuthenticatedSessionResource> getResourceClass() {
return AuthenticatedSessionResource.class;
}
@Override
public AuthenticatedSessionResource findOne(String s, QuerySpec querySpec) {
return identityManagementConverter.authenticatedSessionEntityToAuthenticatedSessionResource(
authenticatedSessionRepository.findByIdentifier(s), cycleAvoidingMappingContext);
}
@Override
public ResourceList<AuthenticatedSessionResource> findAll(QuerySpec querySpec) {
return querySpec.apply(identityManagementConverter
.authenticatedSessionEntitiesToAuthenticatedSessionResources(authenticatedSessionRepository.findAll(), cycleAvoidingMappingContext));
}
@Override
public ResourceList<AuthenticatedSessionResource> findAll(Collection<String> ids, QuerySpec querySpec) {
return querySpec.apply(identityManagementConverter
.authenticatedSessionEntitiesToAuthenticatedSessionResources(authenticatedSessionRepository.findByIdentifierIn(ids), cycleAvoidingMappingContext));
}
@Override
public <S extends AuthenticatedSessionResource> S save(S s) {
AuthenticatedSessionEntity authenticatedSessionEntity = identityManagementConverter
.authenticatedSessionResourceToAuthenticatedSessionEntity(s, cycleAvoidingMappingContext);
authenticatedSessionEntity = authenticatedSessionRepository.save(authenticatedSessionEntity);
return (S) identityManagementConverter
.authenticatedSessionEntityToAuthenticatedSessionResource(authenticatedSessionEntity, cycleAvoidingMappingContext);
}
@Override
public <S extends AuthenticatedSessionResource> S create(S s) {
// Authenticate online account
AuthenticationRequestTO authenticationRequestTO = new AuthenticationRequestTO();
authenticationRequestTO.setAuthenticatedSessionResource(s);
OnlineAccountEntity onlineAccountEntity = authenticationService
.authenticateOnlineAccount(authenticationRequestTO);
if (null == onlineAccountEntity) {
throw new UnauthorizedException("Failed to authenticate.");
}
AuthenticatedSessionEntity authenticatedSessionEntity = identityManagementConverter
.authenticatedSessionResourceToAuthenticatedSessionEntity(s, cycleAvoidingMappingContext);
// Generating the identifier, and faking out everything else
authenticatedSessionEntity.setIdentifier(Hex.encodeHexString(identifierGenerator.generateKey()));
authenticatedSessionEntity.setToken(Hex.encodeHexString(tokenGenerator.generateKey()));
authenticatedSessionEntity.setLevelOfAssurance(generateLevelOfAssurance());
authenticatedSessionEntity.setExpiresIn("15");
authenticatedSessionEntity.setTokenType("sso");
// Save the resource.
authenticatedSessionEntity = authenticatedSessionRepository.save(authenticatedSessionEntity);
return (S) identityManagementConverter
.authenticatedSessionEntityToAuthenticatedSessionResource(authenticatedSessionEntity, cycleAvoidingMappingContext);
}
@Override
public void delete(String s) {
AuthenticatedSessionEntity authenticatedSessionEntity = authenticatedSessionRepository.findByIdentifier(s);
authenticatedSessionRepository.delete(authenticatedSessionEntity);
}
/**
* Generate random level of assurance.
*
* @return
*/
private String generateLevelOfAssurance() {
Random rand = new Random();
int minimum = 1;
int maximum = 4;
return String.valueOf(rand.nextInt((maximum - minimum) + 1) + minimum);
}
}

View File

@ -0,0 +1,101 @@
package com.jkgroller.identitymanagement.resource.repository;
import com.jkgroller.identitymanagement.converter.CycleAvoidingMappingContext;
import com.jkgroller.identitymanagement.converter.IdentityManagementConverter;
import com.jkgroller.identitymanagement.entity.IndividualCustomerEntity;
import com.jkgroller.identitymanagement.repository.IndividualCustomerRepository;
import com.jkgroller.identitymanagement.resource.IndividualCustomerResource;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.ResourceRepository;
import io.crnk.core.resource.list.ResourceList;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class IndividualCustomerResourceRepository implements ResourceRepository<IndividualCustomerResource, String> {
@Autowired
private IndividualCustomerRepository individualCustomerRepository;
@Autowired
private IdentityManagementConverter identityManagementConverter;
@Autowired
@Qualifier("identifierGenerator")
private BytesKeyGenerator identifierGenerator;
@Autowired
private CycleAvoidingMappingContext cycleAvoidingMappingContext;
@Override
public Class<IndividualCustomerResource> getResourceClass() {
return IndividualCustomerResource.class;
}
@Override
public IndividualCustomerResource findOne(String s, QuerySpec querySpec) {
IndividualCustomerEntity individualCustomerEntity = individualCustomerRepository.findByIdentifier(s);
return identityManagementConverter
.individualCustomerEntityToIndividualCustomerResource(individualCustomerEntity, cycleAvoidingMappingContext);
}
@Override
public ResourceList<IndividualCustomerResource> findAll(QuerySpec querySpec) {
return querySpec.apply(identityManagementConverter
.individualCustomerEntitiesToIndividualCustomerResources(individualCustomerRepository.findAll(),cycleAvoidingMappingContext));
}
@Override
public ResourceList<IndividualCustomerResource> findAll(Collection<String> ids, QuerySpec querySpec) {
return querySpec.apply(identityManagementConverter
.individualCustomerEntitiesToIndividualCustomerResources(individualCustomerRepository.findByIdentifierIn(ids), cycleAvoidingMappingContext));
}
@Override
public <S extends IndividualCustomerResource> S save(S s) {
IndividualCustomerEntity individualCustomerEntity = identityManagementConverter
.individualCustomerResourceToIndividualCustomerEntity(s, cycleAvoidingMappingContext);
individualCustomerEntity = individualCustomerRepository.save(individualCustomerEntity);
return (S) identityManagementConverter
.individualCustomerEntityToIndividualCustomerResource(individualCustomerEntity, cycleAvoidingMappingContext);
}
@Override
public <S extends IndividualCustomerResource> S create(S s) {
IndividualCustomerEntity individualCustomerEntity = identityManagementConverter
.individualCustomerResourceToIndividualCustomerEntity(s, cycleAvoidingMappingContext);
individualCustomerEntity.setIdentifier(Hex.encodeHexString(identifierGenerator.generateKey()));
individualCustomerEntity = individualCustomerRepository.save(individualCustomerEntity);
return (S) identityManagementConverter
.individualCustomerEntityToIndividualCustomerResource(individualCustomerEntity, cycleAvoidingMappingContext);
}
@Override
public void delete(String s) {
IndividualCustomerEntity individualCustomerEntity = individualCustomerRepository.findByIdentifier(s);
individualCustomerRepository.delete(individualCustomerEntity);
}
}

View File

@ -0,0 +1,145 @@
package com.jkgroller.identitymanagement.resource.repository;
import com.jkgroller.identitymanagement.converter.CycleAvoidingMappingContext;
import com.jkgroller.identitymanagement.converter.IdentityManagementConverter;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import com.jkgroller.identitymanagement.repository.OnlineAccountRepository;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
import com.jkgroller.identitymanagement.service.AuthenticationService;
import com.jkgroller.identitymanagement.service.CreateOnlineAccountService;
import com.jkgroller.identitymanagement.to.CreateOnlineAccountRequestTO;
import com.jkgroller.identitymanagement.to.CreateOnlineAccountResponseTO;
import com.jkgroller.identitymanagement.to.ValidateOneTimePasswordRequestTO;
import com.jkgroller.identitymanagement.to.ValidateOneTimePasswordResponseTO;
import io.crnk.core.exception.ResourceNotFoundException;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.ResourceRepository;
import io.crnk.core.resource.list.ResourceList;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
@Component
public class OnlineAccountResourceRepository implements ResourceRepository<OnlineAccountResource, String> {
@Autowired
private OnlineAccountRepository onlineAccountRepository;
@Autowired
private IdentityManagementConverter identityManagementConverter;
@Autowired
private CreateOnlineAccountService createOnlineAccountService;
@Autowired
private AuthenticationService authenticationService;
@Autowired
private CycleAvoidingMappingContext cycleAvoidingMappingContext;
@Autowired
private ObjectProvider<HttpServletRequest> httpServletRequestProvider;
@Autowired
private ObjectProvider<HttpServletResponse> httpServletResponseProvider;
private final String ACTION_QUERY_PARAM = "action[password]";
@Override
public Class<OnlineAccountResource> getResourceClass() {
return OnlineAccountResource.class;
}
@Override
public OnlineAccountResource findOne(String s, QuerySpec querySpec) {
return identityManagementConverter
.onlineAccountEntityToOnlineAccountResource(onlineAccountRepository.findByIdentifier(s), cycleAvoidingMappingContext);
}
@Override
public ResourceList<OnlineAccountResource> findAll(QuerySpec querySpec) {
return querySpec.apply( identityManagementConverter
.onlineAccountEntitiesToOnlineAccountResources(onlineAccountRepository.findAll(),cycleAvoidingMappingContext));
}
@Override
public ResourceList<OnlineAccountResource> findAll(Collection<String> ids, QuerySpec querySpec) {
return querySpec.apply(identityManagementConverter
.onlineAccountEntitiesToOnlineAccountResources(onlineAccountRepository.findByIdentifierIn(ids),cycleAvoidingMappingContext));
}
@Override
public <S extends OnlineAccountResource> S save(S s) {
HttpServletRequest httpServletRequest = httpServletRequestProvider.getObject();
String passwordAction = httpServletRequest.getParameter(ACTION_QUERY_PARAM);
OnlineAccountEntity onlineAccountEntity = identityManagementConverter
.onlineAccountResourceToOnlineAccountEntity(s, cycleAvoidingMappingContext);
if(StringUtils.isBlank(passwordAction)) {
onlineAccountEntity = onlineAccountRepository.save(onlineAccountEntity);
}else if("sendOneTimePassword".equals(passwordAction)){
// Send email with OTP, then set it as the online account's password. Hardcoded here for demonstration purposes. action[password]=sendOneTimePassword
onlineAccountEntity.setPasswordStatus("otp pending");
onlineAccountEntity.setOneTimePassword("12345");
onlineAccountEntity = onlineAccountRepository.save(onlineAccountEntity);
}else if("validateOneTimePassword".equals(passwordAction)) {
// Email was received. They go somewhere that allows them to enter the OTP. Patch again to this resource with action[password]=validateOneTimePassword
ValidateOneTimePasswordRequestTO validateOneTimePasswordRequestTO = new ValidateOneTimePasswordRequestTO();
validateOneTimePasswordRequestTO.setOnlineAccountResource(s);
ValidateOneTimePasswordResponseTO validateOneTimePasswordResponseTO = authenticationService.validateOnlineAccountWithOneTimePassword(validateOneTimePasswordRequestTO);
if (null != validateOneTimePasswordResponseTO.getOnlineAccountEntity()) { // One time password validated
onlineAccountEntity.setPasswordStatus("otp validated");
onlineAccountEntity = onlineAccountRepository.save(onlineAccountEntity);
}
}
// After all this, the client should be able to patch the updated password without the parameters.
return (S) identityManagementConverter.onlineAccountEntityToOnlineAccountResource(onlineAccountEntity,cycleAvoidingMappingContext);
}
@Override
public <S extends OnlineAccountResource> S create(S s) {
CreateOnlineAccountRequestTO createOnlineAccountRequestTO = new CreateOnlineAccountRequestTO();
createOnlineAccountRequestTO.setOnlineAccountResource(s);
CreateOnlineAccountResponseTO createOnlineAccountResponseTO = createOnlineAccountService.createOnlineAccount(createOnlineAccountRequestTO);
return (S) createOnlineAccountResponseTO.getOnlineAccountResource();
}
@Override
public void delete(String s) {
OnlineAccountEntity onlineAccountEntity = onlineAccountRepository.findByIdentifier(s);
if(null == onlineAccountEntity){
throw new ResourceNotFoundException("Online account does not exist.");
}
onlineAccountRepository.delete(onlineAccountEntity);
}
}

View File

@ -0,0 +1,14 @@
package com.jkgroller.identitymanagement.service;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import com.jkgroller.identitymanagement.to.AuthenticationRequestTO;
import com.jkgroller.identitymanagement.to.ValidateOneTimePasswordRequestTO;
import com.jkgroller.identitymanagement.to.ValidateOneTimePasswordResponseTO;
public interface AuthenticationService {
OnlineAccountEntity authenticateOnlineAccount(AuthenticationRequestTO authenticationRequestTO);
ValidateOneTimePasswordResponseTO validateOnlineAccountWithOneTimePassword(ValidateOneTimePasswordRequestTO validateOneTimePasswordRequestTO);
}

View File

@ -0,0 +1,44 @@
package com.jkgroller.identitymanagement.service;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import com.jkgroller.identitymanagement.repository.OnlineAccountRepository;
import com.jkgroller.identitymanagement.to.AuthenticationRequestTO;
import com.jkgroller.identitymanagement.to.ValidateOneTimePasswordRequestTO;
import com.jkgroller.identitymanagement.to.ValidateOneTimePasswordResponseTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
@Autowired
private OnlineAccountRepository onlineAccountRepository;
@Override
public OnlineAccountEntity authenticateOnlineAccount(AuthenticationRequestTO authenticationRequestTO){
OnlineAccountEntity onlineAccountEntity = onlineAccountRepository.findByUserName(authenticationRequestTO.getAuthenticatedSessionResource().getUserName());
if(null != onlineAccountEntity && authenticationRequestTO.getAuthenticatedSessionResource().getPassword().equals(onlineAccountEntity.getPassword())){
return onlineAccountEntity;
}
return null;
}
@Override
public ValidateOneTimePasswordResponseTO validateOnlineAccountWithOneTimePassword(ValidateOneTimePasswordRequestTO validateOneTimePasswordRequestTO) {
OnlineAccountEntity onlineAccountEntity = onlineAccountRepository.findByUserName(validateOneTimePasswordRequestTO.getOnlineAccountResource().getUserName());
ValidateOneTimePasswordResponseTO validateOneTimePasswordResponseTO = new ValidateOneTimePasswordResponseTO();
if(null != onlineAccountEntity && validateOneTimePasswordRequestTO.getOnlineAccountResource().getOneTimePassword().equals(onlineAccountEntity.getOneTimePassword())){
validateOneTimePasswordResponseTO.setOnlineAccountEntity(onlineAccountEntity);
return validateOneTimePasswordResponseTO;
}
return validateOneTimePasswordResponseTO;
}
}

View File

@ -0,0 +1,10 @@
package com.jkgroller.identitymanagement.service;
import com.jkgroller.identitymanagement.to.CreateOnlineAccountRequestTO;
import com.jkgroller.identitymanagement.to.CreateOnlineAccountResponseTO;
public interface CreateOnlineAccountService {
CreateOnlineAccountResponseTO createOnlineAccount(CreateOnlineAccountRequestTO createOnlineAccountRequestTO);
}

View File

@ -0,0 +1,82 @@
package com.jkgroller.identitymanagement.service;
import com.jkgroller.identitymanagement.converter.CycleAvoidingMappingContext;
import com.jkgroller.identitymanagement.converter.IdentityManagementConverter;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
import com.jkgroller.identitymanagement.exception.CreateOnlineAccountConflictException;
import com.jkgroller.identitymanagement.repository.OnlineAccountRepository;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
import com.jkgroller.identitymanagement.to.CreateOnlineAccountRequestTO;
import com.jkgroller.identitymanagement.to.CreateOnlineAccountResponseTO;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class CreateOnlineAccountServiceImpl implements CreateOnlineAccountService {
@Autowired
private OnlineAccountRepository onlineAccountRepository;
@Autowired
private IdentityManagementConverter identityManagementConverter;
@Autowired
private CycleAvoidingMappingContext cycleAvoidingMappingContext;
@Autowired
@Qualifier("identifierGenerator")
private BytesKeyGenerator identifierGenerator;
public CreateOnlineAccountResponseTO createOnlineAccount(CreateOnlineAccountRequestTO createOnlineAccountRequestTO){
// Check if username is taken.
verifyUserNameAvailable(createOnlineAccountRequestTO.getOnlineAccountResource().getUserName());
CreateOnlineAccountResponseTO createOnlineAccountResponseTO = new CreateOnlineAccountResponseTO();
// Convert resource to entity.
OnlineAccountEntity onlineAccountEntity = identityManagementConverter
.onlineAccountResourceToOnlineAccountEntity(createOnlineAccountRequestTO.getOnlineAccountResource(),cycleAvoidingMappingContext);
// Set some values related to account creation
onlineAccountEntity.setAccountStatus("active");
onlineAccountEntity.setPasswordStatus("active");
onlineAccountEntity.setIdentifier(Hex.encodeHexString(identifierGenerator.generateKey()));
onlineAccountEntity.setCreationDate(LocalDateTime.now().toString());
onlineAccountEntity.setLastLoginDate(LocalDateTime.now().toString());
// Save new account
onlineAccountEntity = onlineAccountRepository.save(onlineAccountEntity);
// Convert it back to the resource
OnlineAccountResource onlineAccountResource = identityManagementConverter.onlineAccountEntityToOnlineAccountResource(onlineAccountEntity, cycleAvoidingMappingContext);
// Set response
createOnlineAccountResponseTO.setOnlineAccountResource(onlineAccountResource);
// Return it
return createOnlineAccountResponseTO;
}
/**
* Check if username is taken. If so, throw exception.
* @param userName
*/
private void verifyUserNameAvailable(String userName){
OnlineAccountEntity onlineAccountEntity = onlineAccountRepository.findByUserName(userName);
if(null != onlineAccountEntity){
throw new CreateOnlineAccountConflictException();
}
}
}

View File

@ -0,0 +1,16 @@
package com.jkgroller.identitymanagement.to;
import com.jkgroller.identitymanagement.resource.AuthenticatedSessionResource;
public class AuthenticationRequestTO {
private AuthenticatedSessionResource authenticatedSessionResource;
public AuthenticatedSessionResource getAuthenticatedSessionResource() {
return authenticatedSessionResource;
}
public void setAuthenticatedSessionResource(AuthenticatedSessionResource authenticatedSessionResource) {
this.authenticatedSessionResource = authenticatedSessionResource;
}
}

View File

@ -0,0 +1,16 @@
package com.jkgroller.identitymanagement.to;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
public class CreateOnlineAccountRequestTO {
private OnlineAccountResource onlineAccountResource;
public OnlineAccountResource getOnlineAccountResource() {
return onlineAccountResource;
}
public void setOnlineAccountResource(OnlineAccountResource onlineAccountResource) {
this.onlineAccountResource = onlineAccountResource;
}
}

View File

@ -0,0 +1,16 @@
package com.jkgroller.identitymanagement.to;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
public class CreateOnlineAccountResponseTO {
private OnlineAccountResource onlineAccountResource;
public OnlineAccountResource getOnlineAccountResource() {
return onlineAccountResource;
}
public void setOnlineAccountResource(OnlineAccountResource onlineAccountResource) {
this.onlineAccountResource = onlineAccountResource;
}
}

View File

@ -0,0 +1,16 @@
package com.jkgroller.identitymanagement.to;
import com.jkgroller.identitymanagement.resource.OnlineAccountResource;
public class ValidateOneTimePasswordRequestTO {
private OnlineAccountResource onlineAccountResource;
public OnlineAccountResource getOnlineAccountResource() {
return onlineAccountResource;
}
public void setOnlineAccountResource(OnlineAccountResource onlineAccountResource) {
this.onlineAccountResource = onlineAccountResource;
}
}

View File

@ -0,0 +1,16 @@
package com.jkgroller.identitymanagement.to;
import com.jkgroller.identitymanagement.entity.OnlineAccountEntity;
public class ValidateOneTimePasswordResponseTO {
private OnlineAccountEntity onlineAccountEntity;
public OnlineAccountEntity getOnlineAccountEntity() {
return onlineAccountEntity;
}
public void setOnlineAccountEntity(OnlineAccountEntity onlineAccountEntity) {
this.onlineAccountEntity = onlineAccountEntity;
}
}

View File

@ -0,0 +1,31 @@
crnk:
path-prefix: /v1
return404-on-null: true
allow-unknown-parameters: true
identifier:
length: 8
token:
length: 64
spring:
datasource:
url: jdbc:h2:mem:IdentityManagement;DB_CLOSE_DELAY=-1
platform: h2
username: sa
password:
initialization-mode: ALWAYS
jpa:
show-sql: false
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
h2:
console:
enabled: true
path: /h2
settings:
web-allow-others: true

View File

@ -0,0 +1,449 @@
{
"info": {
"_postman_id": "e368ccd5-d7e6-4f94-8c94-21e36005ee44",
"name": "identity-management",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "/individualCustomers",
"item": [
{
"name": "Create Individual Customer",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"individualCustomer\",\r\n \"attributes\": {\r\n \t\"firstName\":\"John\",\r\n \t\"lastName\":\"Groller\",\r\n \t\"emailAddress\":\"john@jkgroller.com\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/individualCustomers",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"individualCustomers"
]
}
},
"response": []
},
{
"name": "Update Individual Customer",
"request": {
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"individualCustomer\",\r\n \"attributes\": {\r\n \t\"firstName\":\"jacob\",\r\n \t\"lastName\":\"Jingleheimer\",\r\n \t\"emailAddress\":\"john@jkgroller.com\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/individualCustomers/836fc5a53d82554b",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"individualCustomers",
"836fc5a53d82554b"
]
}
},
"response": []
},
{
"name": "Retrieve One Individual Customer",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8080/v1/individualCustomers/bd862d9aa270a06a",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"individualCustomers",
"bd862d9aa270a06a"
]
}
},
"response": []
},
{
"name": "Retrieve All Individual Customers",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8080/v1/individualCustomers",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"individualCustomers"
]
}
},
"response": []
},
{
"name": "Delete One Individual Customer",
"request": {
"method": "DELETE",
"header": [],
"url": {
"raw": "http://localhost:8080/v1/individualCustomers/bd862d9aa270a06a",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"individualCustomers",
"bd862d9aa270a06a"
]
}
},
"response": []
}
],
"protocolProfileBehavior": {}
},
{
"name": "/onlineAccounts",
"item": [
{
"name": "Retrieve All Online Accounts",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"authenticatedSession\",\r\n \"attributes\": {\r\n \t\"userName\":\"mreid\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts"
]
}
},
"response": []
},
{
"name": "Retrieve One Online Account",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"authenticatedSession\",\r\n \"attributes\": {\r\n \t\"userName\":\"mreid\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/d9f6d50575868b4a",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"d9f6d50575868b4a"
]
}
},
"response": []
},
{
"name": "Delete One Online Account",
"request": {
"method": "DELETE",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"authenticatedSession\",\r\n \"attributes\": {\r\n \t\"userName\":\"mreid\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/4eb9702aeff4eb7f",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"4eb9702aeff4eb7f"
]
}
},
"response": []
},
{
"name": "Create Online Account Related To Individual Customer",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"onlineAccount\",\r\n \"attributes\": {\r\n \t\"userName\":\"john\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/individualCustomers/b3ba59ba3fabf6d6/onlineAccount",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"individualCustomers",
"b3ba59ba3fabf6d6",
"onlineAccount"
]
}
},
"response": []
},
{
"name": "Update Online Account",
"request": {
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"onlineAccount\",\r\n \"attributes\": {\r\n \t\"userName\":\"mreid2\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/7878356cbaa31769",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"7878356cbaa31769"
]
}
},
"response": []
},
{
"name": "Update Online Account - Begin OTP",
"request": {
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {}\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/3ff5750dbbb18935?action[password]=sendOneTimePassword",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"3ff5750dbbb18935"
],
"query": [
{
"key": "action[password]",
"value": "sendOneTimePassword"
}
]
}
},
"response": []
},
{
"name": "Update Online Account - Validate OTP",
"request": {
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"onlineAccount\",\r\n \"attributes\": {\r\n \t\"userName\":\"john\",\r\n \t\"oneTimePassword\":\"12345\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/3ff5750dbbb18935?action[password]=validateOneTimePassword",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"3ff5750dbbb18935"
],
"query": [
{
"key": "action[password]",
"value": "validateOneTimePassword"
}
]
}
},
"response": []
},
{
"name": "Update Online Account - Password",
"request": {
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"onlineAccount\",\r\n \"attributes\": {\r\n \t\"userName\":\"john\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/3ff5750dbbb18935",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"3ff5750dbbb18935"
]
}
},
"response": []
}
],
"protocolProfileBehavior": {}
},
{
"name": "/authenticatedSessions",
"item": [
{
"name": "Create Authenticated Session Related To Online Account",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"type": "text",
"value": "application/vnd.api+json"
}
],
"body": {
"mode": "raw",
"raw": "{\r\n \"data\": {\r\n \"type\": \"authenticatedSession\",\r\n \"attributes\": {\r\n \t\"userName\":\"john\",\r\n \t\"password\":\"password\"\r\n }\r\n }\r\n}"
},
"url": {
"raw": "http://localhost:8080/v1/onlineAccounts/325682df5689397a/authenticatedSession",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"v1",
"onlineAccounts",
"325682df5689397a",
"authenticatedSession"
]
}
},
"response": []
}
],
"protocolProfileBehavior": {}
}
],
"protocolProfileBehavior": {}
}