chore(*): restore from backup

this project was archived and never moved to my new gittea instance, until now
This commit is contained in:
john 2025-11-24 15:59:13 -07:00
commit 3e62ad946d
44 changed files with 2090 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
/target/
/build/
/.classpath
/.project
.settings/
/cryptonote.log
/.idea
/bin/
.springBeans
cryptonote.iml
mvnw
mvnw.cmd

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"conventionalCommits.scopes": [
"*"
]
}

1
README.md Normal file
View File

@ -0,0 +1 @@
Placeholder for something eventually meaningful.

96
pom.xml Normal file
View File

@ -0,0 +1,96 @@
<?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.cryptonote</groupId>
<artifactId>cryptonote-api</artifactId>
<version>2.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cryptonote-api</name>
<description>API for encrypting notes.</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>13</java.version>
<crnk.version>3.1.20191113192440</crnk.version>
<commons-codec.version>1.14</commons-codec.version>
<mapstruct.version>1.3.1.Final</mapstruct.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-web</artifactId>
</dependency>
<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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>io.crnk</groupId>
<artifactId>crnk-setup-spring-boot2</artifactId>
<version>${crnk.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>
<build>
<finalName>cryptonote-api</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,20 @@
package com.jkgroller.cryptonote;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
*/
@SpringBootApplication
public class CryptonoteApi {
/**
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(CryptonoteApi.class, args);
}
}

View File

@ -0,0 +1,64 @@
package com.jkgroller.cryptonote.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 ENCRYPTION_KEY_LENGTH;
private final int USERNAME_LENGTH;
private final int PASSWORD_LENGTH;
private final int IDENTIFIER_LENGTH;
/**
* This is how you inject a value from a properties file into a constant.
*/
public KeyGeneratorConfig(@Value("${encryption.key.length}") int encryptKeyLength
, @Value("${username.length}") int userNameLength
, @Value("${password.length}") int passwordLength
, @Value("${identifier.length}") int identifierLength) {
this.ENCRYPTION_KEY_LENGTH = encryptKeyLength;
this.USERNAME_LENGTH = userNameLength;
this.PASSWORD_LENGTH = passwordLength;
this.IDENTIFIER_LENGTH = identifierLength;
}
/**
* @return
*/
@Bean(name = "encryptionKeyGenerator")
public BytesKeyGenerator encryptionKeyGenerator() {
return KeyGenerators.secureRandom(ENCRYPTION_KEY_LENGTH);
}
/**
* @return
*/
@Bean(name = "userNameGenerator")
public BytesKeyGenerator userNameGenerator() {
return KeyGenerators.secureRandom(USERNAME_LENGTH);
}
/**
* @return
*/
@Bean(name = "passwordGenerator")
public BytesKeyGenerator passwordGenerator() {
return KeyGenerators.secureRandom(PASSWORD_LENGTH);
}
/**
* @return
*/
@Bean(name = "identifierGenerator")
public BytesKeyGenerator identifierGenerator() {
return KeyGenerators.secureRandom(IDENTIFIER_LENGTH);
}
}

View File

@ -0,0 +1,34 @@
package com.jkgroller.cryptonote.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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
*
*/
@Configuration
public class PasswordEncoderConfig {
private final int PASSWORD_ENCRYPTION_STRENGTH;
/**
* This is how you inject a value from a properties file into a constant.
*/
public PasswordEncoderConfig(@Value("${password.encryption.strength}") int passwordEncryptionStrength) {
this.PASSWORD_ENCRYPTION_STRENGTH = passwordEncryptionStrength;
}
/**
*
* @return
*/
@Bean
public PasswordEncoder createPasswordEncoderBean() {
return new BCryptPasswordEncoder(PASSWORD_ENCRYPTION_STRENGTH);
}
}

View File

@ -0,0 +1,44 @@
package com.jkgroller.cryptonote.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 {
//httpSecurity.authorizeRequests().antMatchers("/v1/userAccounts/**").permitAll();
}
/**
*
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v1/userAccounts/**", "/h2/**", "/v1/notes/**");
}
}

View File

@ -0,0 +1,15 @@
package com.jkgroller.cryptonote.converter;
import com.jkgroller.cryptonote.entity.AccountEntity;
import com.jkgroller.cryptonote.service.to.AccountTO;
import org.mapstruct.Mapper;
/**
*
*/
@Mapper(componentModel = "spring")
public interface AccountEntityConverter {
AccountTO accountEntityToAccountTO(AccountEntity accountEntity);
}

View File

@ -0,0 +1,23 @@
package com.jkgroller.cryptonote.converter;
import com.jkgroller.cryptonote.resource.UserAccountResource;
import com.jkgroller.cryptonote.service.to.AccountTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
/**
*
*/
@Mapper(componentModel = "spring")
public interface AccountTOConverter {
/**
*
* @param accountTO
* @return
*/
@Mapping(target = "credentialsAttribute.userName", source = "accountTO.userName")
@Mapping(target = "credentialsAttribute.password", source = "accountTO.password")
UserAccountResource accountTOToUserAccountResource(AccountTO accountTO);
}

View File

@ -0,0 +1,13 @@
package com.jkgroller.cryptonote.converter;
import org.mapstruct.Mapper;
/**
*
*/
@Mapper(componentModel = "spring")
public interface NoteEntityConverter {
//NoteTO noteEntityToNoteTO(NoteEntity noteEntity);
}

View File

@ -0,0 +1,20 @@
package com.jkgroller.cryptonote.converter;
import com.jkgroller.cryptonote.resource.NoteResource;
import com.jkgroller.cryptonote.service.to.CreateNoteRequestTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
/**
*
*/
@Mapper(componentModel = "spring")
public interface NoteResourceConverter {
@Mapping(target = "userName", source = "credentialsAttribute.userName")
@Mapping(target = "password", source = "credentialsAttribute.password")
@Mapping(target = "noteName", source = "name")
@Mapping(target = "noteContents", source = "contents")
CreateNoteRequestTO noteResourceToCreateEncryptedNoteRequestTO(NoteResource noteResource);
}

View File

@ -0,0 +1,15 @@
package com.jkgroller.cryptonote.converter;
import com.jkgroller.cryptonote.resource.NoteResource;
import com.jkgroller.cryptonote.service.to.NoteTO;
import org.mapstruct.Mapper;
/**
*
*/
@Mapper(componentModel = "spring")
public interface NoteTOConverter {
NoteResource noteTOToNoteResource(NoteTO noteTO);
}

View File

@ -0,0 +1,177 @@
package com.jkgroller.cryptonote.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity(name = "account")
public class AccountEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(nullable = false)
private String identifier;
@Column(nullable = false)
private String userName;
@Column(nullable = false)
private String passwordHash;
@Column(nullable = false)
private byte[] encryptedSurrogateKey;
@Column(nullable = false)
private byte[] surrogateKeySalt;
@Column(nullable = false)
private String role;
@Column(nullable = false)
private LocalDateTime creationDateTime;
@Column(nullable = false)
private LocalDateTime lastLoginDateTime;
/**
* @return
*/
public Integer getId() {
return id;
}
/**
*
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
*
* @return
*/
public String getUserName() {
return userName;
}
/**
*
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
*
* @return
*/
public String getPasswordHash() {
return passwordHash;
}
/**
*
* @param passwordHash
*/
public void setPasswordHash(String passwordHash) {
this.passwordHash = passwordHash;
}
/**
*
* @return
*/
public String getRole() {
return role;
}
/**
*
* @param role
*/
public void setRole(String role) {
this.role = role;
}
/**
*
* @return
*/
public String getIdentifier() {
return identifier;
}
/**
*
* @param identifier
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
*
* @return
*/
public LocalDateTime getCreationDateTime() {
return creationDateTime;
}
/**
*
* @param creationDateTime
*/
public void setCreationDateTime(LocalDateTime creationDateTime) {
this.creationDateTime = creationDateTime;
}
/**
*
* @return
*/
public byte[] getEncryptedSurrogateKey() {
return encryptedSurrogateKey;
}
/**
*
* @param encryptedSurrogateKey
*/
public void setEncryptedSurrogateKey(byte[] encryptedSurrogateKey) {
this.encryptedSurrogateKey = encryptedSurrogateKey;
}
/**
* @return
*/
public LocalDateTime getLastLoginDateTime() {
return lastLoginDateTime;
}
/**
* @param lastLoginDateTime
*/
public void setLastLoginDateTime(LocalDateTime lastLoginDateTime) {
this.lastLoginDateTime = lastLoginDateTime;
}
/**
*
* @return
*/
public byte[] getSurrogateKeySalt() {
return surrogateKeySalt;
}
/**
*
* @param surrogateKeySalt
*/
public void setSurrogateKeySalt(byte[] surrogateKeySalt) {
this.surrogateKeySalt = surrogateKeySalt;
}
}

View File

@ -0,0 +1,145 @@
package com.jkgroller.cryptonote.entity;
import javax.persistence.*;
@Entity(name = "note")
public class NoteEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(nullable = false)
private String identifier;
@Column(nullable = false)
private byte[] key;
@Column(nullable = false)
private byte[] keySalt;
@Column(nullable = false)
private byte[] name;
@Column(nullable = false)
private byte[] nameSalt;
@Column(nullable = false)
private byte[] contents;
@Column(nullable = false)
private byte[] contentsSalt;
/**
* @return
*/
public Integer getId() {
return id;
}
/**
* @param id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* @return
*/
public String getIdentifier() {
return identifier;
}
/**
* @param identifier
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* @return
*/
public byte[] getKey() {
return key;
}
/**
* @param key
*/
public void setKey(byte[] key) {
this.key = key;
}
/**
* @return
*/
public byte[] getName() {
return name;
}
/**
* @param name
*/
public void setName(byte[] name) {
this.name = name;
}
/**
* @return
*/
public byte[] getContents() {
return contents;
}
/**
* @param contents
*/
public void setContents(byte[] contents) {
this.contents = contents;
}
/**
* @return
*/
public byte[] getKeySalt() {
return keySalt;
}
/**
* @param keySalt
*/
public void setKeySalt(byte[] keySalt) {
this.keySalt = keySalt;
}
/**
* @return
*/
public byte[] getNameSalt() {
return nameSalt;
}
/**
* @param nameSalt
*/
public void setNameSalt(byte[] nameSalt) {
this.nameSalt = nameSalt;
}
/**
* @return
*/
public byte[] getContentsSalt() {
return contentsSalt;
}
/**
* @param contentsSalt
*/
public void setContentsSalt(byte[] contentsSalt) {
this.contentsSalt = contentsSalt;
}
}

View File

@ -0,0 +1,15 @@
package com.jkgroller.cryptonote.entity.repository;
import com.jkgroller.cryptonote.entity.AccountEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
*
*/
@Repository
public interface AccountEntityRepository extends JpaRepository<AccountEntity, Integer> {
AccountEntity findAccountByUserName(String userName);
}

View File

@ -0,0 +1,9 @@
package com.jkgroller.cryptonote.entity.repository;
import com.jkgroller.cryptonote.entity.NoteEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface NoteEntityRepository extends JpaRepository<NoteEntity, Integer> {
}

View File

@ -0,0 +1,9 @@
package com.jkgroller.cryptonote.enums;
/**
*
*/
public enum AccountRole {
USER,
ADMIN
}

View File

@ -0,0 +1,36 @@
package com.jkgroller.cryptonote.resource;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jkgroller.cryptonote.resource.attribute.CredentialsAttribute;
import io.crnk.core.resource.annotations.JsonApiField;
import io.crnk.core.resource.annotations.JsonApiId;
/**
* Base class for the different types of account resources there may be. Will likely be a resource in the future.
*/
public class Account {
@JsonApiId
private String identifier;
@JsonProperty("credentials")
@JsonApiField(readable = true, postable = false, patchable = false, deletable = false)
private CredentialsAttribute credentialsAttribute;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public CredentialsAttribute getCredentialsAttribute() {
return credentialsAttribute;
}
public void setCredentialsAttribute(CredentialsAttribute credentialsAttribute) {
this.credentialsAttribute = credentialsAttribute;
}
}

View File

@ -0,0 +1,83 @@
package com.jkgroller.cryptonote.resource;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jkgroller.cryptonote.resource.attribute.CredentialsAttribute;
import io.crnk.core.resource.annotations.JsonApiField;
import io.crnk.core.resource.annotations.JsonApiId;
import io.crnk.core.resource.annotations.JsonApiResource;
@JsonApiResource(type = "note", resourcePath = "notes")
public class NoteResource {
@JsonApiId
private String identifier;
private String name;
private String contents;
@JsonProperty("credentials")
@JsonApiField(readable = false, postable = true, patchable = false, deletable = false)
private CredentialsAttribute credentialsAttribute;
/**
* @return
*/
public String getIdentifier() {
return identifier;
}
/**
*
* @param identifier
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
*
* @return
*/
public String getName() {
return name;
}
/**
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
*
* @return
*/
public String getContents() {
return contents;
}
/**
* @param contents
*/
public void setContents(String contents) {
this.contents = contents;
}
/**
* @return
*/
public CredentialsAttribute getCredentialsAttribute() {
return credentialsAttribute;
}
/**
*
* @param credentialsAttribute
*/
public void setCredentialsAttribute(CredentialsAttribute credentialsAttribute) {
this.credentialsAttribute = credentialsAttribute;
}
}

View File

@ -0,0 +1,7 @@
package com.jkgroller.cryptonote.resource;
import io.crnk.core.resource.annotations.JsonApiResource;
@JsonApiResource(type = "userAccount", resourcePath = "userAccounts")
public class UserAccountResource extends Account {
}

View File

@ -0,0 +1,43 @@
package com.jkgroller.cryptonote.resource.attribute;
/**
*
*/
public class CredentialsAttribute {
private String userName;
private String password;
/**
*
* @return
*/
public String getUserName() {
return userName;
}
/**
*
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
*
* @return
*/
public String getPassword() {
return password;
}
/**
*
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,73 @@
package com.jkgroller.cryptonote.resource.repository;
import com.jkgroller.cryptonote.converter.NoteResourceConverter;
import com.jkgroller.cryptonote.converter.NoteTOConverter;
import com.jkgroller.cryptonote.resource.NoteResource;
import com.jkgroller.cryptonote.service.NoteService;
import com.jkgroller.cryptonote.service.to.CreateNoteRequestTO;
import com.jkgroller.cryptonote.service.to.CreateNoteResponseTO;
import io.crnk.core.exception.BadRequestException;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.ResourceRepository;
import io.crnk.core.resource.list.ResourceList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
*
*/
@Component
public class NoteResourceRepository implements ResourceRepository<NoteResource, String> {
@Autowired
private NoteService noteService;
@Autowired
private NoteResourceConverter noteResourceConverter;
@Autowired
private NoteTOConverter noteTOConverter;
@Override
public Class<NoteResource> getResourceClass() {
return NoteResource.class;
}
@Override
public NoteResource findOne(String s, QuerySpec querySpec) {
throw new BadRequestException("Unsupported.");
}
@Override
public ResourceList<NoteResource> findAll(QuerySpec querySpec) {
throw new BadRequestException("Unsupported.");
}
@Override
public ResourceList<NoteResource> findAll(Collection<String> collection, QuerySpec querySpec) {
throw new BadRequestException("Unsupported.");
}
@Override
public <S extends NoteResource> S save(S s) {
throw new BadRequestException("Unsupported.");
}
@Override
public <S extends NoteResource> S create(S s) {
CreateNoteRequestTO createNoteRequestTO = noteResourceConverter.noteResourceToCreateEncryptedNoteRequestTO(s);
CreateNoteResponseTO createNoteResponseTO = noteService.createNote(createNoteRequestTO);
return (S) noteTOConverter.noteTOToNoteResource(createNoteResponseTO.getNoteTO());
}
@Override
public void delete(String s) {
throw new BadRequestException("Unsupported.");
}
}

View File

@ -0,0 +1,103 @@
package com.jkgroller.cryptonote.resource.repository;
import com.jkgroller.cryptonote.converter.AccountTOConverter;
import com.jkgroller.cryptonote.resource.UserAccountResource;
import com.jkgroller.cryptonote.service.AccountService;
import com.jkgroller.cryptonote.service.to.CreateNewUserAccountResponseTO;
import io.crnk.core.exception.BadRequestException;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.ResourceRepository;
import io.crnk.core.resource.list.ResourceList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
*
*/
@Component
public class UserAccountResourceRepository implements ResourceRepository<UserAccountResource, String> {
@Autowired
private AccountService accountService;
@Autowired
private AccountTOConverter accountTOConverter;
/**
* @return
*/
@Override
public Class<UserAccountResource> getResourceClass() {
return UserAccountResource.class;
}
/**
*
* @param s
* @param querySpec
* @return
*/
@Override
public UserAccountResource findOne(String s, QuerySpec querySpec) {
throw new BadRequestException("Unsupported.");
}
/**
*
* @param querySpec
* @return
*/
@Override
public ResourceList<UserAccountResource> findAll(QuerySpec querySpec) {
throw new BadRequestException("Unsupported.");
}
/**
*
* @param collection
* @param querySpec
* @return
*/
@Override
public ResourceList<UserAccountResource> findAll(Collection<String> collection, QuerySpec querySpec) {
throw new BadRequestException("Unsupported.");
}
/**
*
* @param s
* @param <S>
* @return
*/
@Override
public <S extends UserAccountResource> S save(S s) {
throw new BadRequestException("Unsupported.");
}
/**
*
* @param s
* @param <S>
* @return
*/
@Override
public <S extends UserAccountResource> S create(S s) {
CreateNewUserAccountResponseTO createNewUserAccountResponseTO = accountService.createNewUserAccount();
return (S) accountTOConverter.accountTOToUserAccountResource(createNewUserAccountResponseTO.getAccountTO());
}
/**
*
* @param s
*/
@Override
public void delete(String s) {
throw new BadRequestException("Unsupported.");
}
}

View File

@ -0,0 +1,22 @@
package com.jkgroller.cryptonote.service;
import com.jkgroller.cryptonote.service.to.CreateNewUserAccountResponseTO;
import com.jkgroller.cryptonote.service.to.RetrieveUserAccountRequestTO;
import com.jkgroller.cryptonote.service.to.RetrieveUserAccountResponseTO;
/**
*
*/
public interface AccountService {
/**
* @return
*/
CreateNewUserAccountResponseTO createNewUserAccount();
/**
* @return
*/
RetrieveUserAccountResponseTO retrieveUserAccount(RetrieveUserAccountRequestTO retrieveUserAccountRequestTO);
}

View File

@ -0,0 +1,138 @@
package com.jkgroller.cryptonote.service;
import com.jkgroller.cryptonote.converter.AccountEntityConverter;
import com.jkgroller.cryptonote.entity.AccountEntity;
import com.jkgroller.cryptonote.entity.repository.AccountEntityRepository;
import com.jkgroller.cryptonote.enums.AccountRole;
import com.jkgroller.cryptonote.service.to.*;
import io.crnk.core.exception.UnauthorizedException;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
*
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountEntityConverter accountEntityConverter;
@Autowired
private AccountEntityRepository accountEntityRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private CipherService cipherService;
@Autowired
@Qualifier("identifierGenerator")
private BytesKeyGenerator identifierGenerator;
@Autowired
@Qualifier("encryptionKeyGenerator")
private BytesKeyGenerator encryptionKeyGenerator;
@Autowired
@Qualifier("userNameGenerator")
private BytesKeyGenerator userNameGenerator;
@Autowired
@Qualifier("passwordGenerator")
private BytesKeyGenerator passwordGenerator;
/**
*
* @return
*/
@Override
public CreateNewUserAccountResponseTO createNewUserAccount() {
String password = generatePassword();
AccountEntity accountEntity = accountEntityRepository.save(buildAccountEntity(password, AccountRole.USER));
CreateNewUserAccountResponseTO createNewUserAccountResponseTO = new CreateNewUserAccountResponseTO();
createNewUserAccountResponseTO.setAccountTO(accountEntityConverter.accountEntityToAccountTO(accountEntity));
createNewUserAccountResponseTO.getAccountTO().setPassword(password);
return createNewUserAccountResponseTO;
}
/**
*
* @param retrieveUserAccountRequestTO
* @return
*/
@Override
public RetrieveUserAccountResponseTO retrieveUserAccount(RetrieveUserAccountRequestTO retrieveUserAccountRequestTO) {
AccountEntity accountEntity = accountEntityRepository.findAccountByUserName(retrieveUserAccountRequestTO.getUserName());
if (null != accountEntity && passwordEncoder.matches(retrieveUserAccountRequestTO.getPassword(), accountEntity.getPasswordHash())) {
RetrieveUserAccountResponseTO retrieveUserAccountResponseTO = new RetrieveUserAccountResponseTO();
AccountTO accountTO = accountEntityConverter.accountEntityToAccountTO(accountEntity);
retrieveUserAccountResponseTO.setAccountTO(accountTO);
return retrieveUserAccountResponseTO;
}
throw new UnauthorizedException("Unable to verify credentials.");
}
/**
* @return
*/
private String generatePassword() {
return Hex.encodeHexString(passwordGenerator.generateKey());
}
/**
*
* @param password
* @return
*/
private AccountEntity buildAccountEntity(String password, AccountRole accountRole) {
AccountEntity accountEntity = new AccountEntity();
LocalDateTime localDateTime = LocalDateTime.now();
accountEntity.setUserName(Hex.encodeHexString(userNameGenerator.generateKey()));
accountEntity.setPasswordHash(passwordEncoder.encode(password));
accountEntity.setRole(accountRole.name());
accountEntity.setIdentifier(Hex.encodeHexString(identifierGenerator.generateKey()));
accountEntity.setCreationDateTime(localDateTime);
accountEntity.setLastLoginDateTime(localDateTime);
EncryptResponseTO encryptResponseTO = generateEncryptedSurrogateKey(password);
accountEntity.setEncryptedSurrogateKey(encryptResponseTO.getEncrypted());
accountEntity.setSurrogateKeySalt(encryptResponseTO.getSalt());
return accountEntity;
}
/**
*
* @param password
* @return
*/
private EncryptResponseTO generateEncryptedSurrogateKey(String password){
EncryptRequestTO encryptRequest = new EncryptRequestTO(password, encryptionKeyGenerator.generateKey());
return cipherService.encrypt(encryptRequest);
}
}

View File

@ -0,0 +1,27 @@
package com.jkgroller.cryptonote.service;
import com.jkgroller.cryptonote.service.to.DecryptRequestTO;
import com.jkgroller.cryptonote.service.to.DecryptResponseTO;
import com.jkgroller.cryptonote.service.to.EncryptRequestTO;
import com.jkgroller.cryptonote.service.to.EncryptResponseTO;
/**
*
*/
public interface CipherService {
/**
*
* @param encryptRequestTO
* @return
*/
EncryptResponseTO encrypt(EncryptRequestTO encryptRequestTO);
/**
*
* @param decryptRequestTO
* @return
*/
DecryptResponseTO decrypt(DecryptRequestTO decryptRequestTO);
}

View File

@ -0,0 +1,70 @@
package com.jkgroller.cryptonote.service;
import com.jkgroller.cryptonote.service.to.DecryptRequestTO;
import com.jkgroller.cryptonote.service.to.DecryptResponseTO;
import com.jkgroller.cryptonote.service.to.EncryptRequestTO;
import com.jkgroller.cryptonote.service.to.EncryptResponseTO;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.encrypt.BytesEncryptor;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.stereotype.Service;
/**
*
*/
@Service
public class CipherServiceImpl implements CipherService {
private final int SALT_SIZE;
/**
* This is how you inject a value from a properties file into a constant.
*/
public CipherServiceImpl(@Value("${salt.size}") int saltSize) {
this.SALT_SIZE = saltSize;
}
/**
*
* @param encryptRequestTO
* @return
*/
@Override
public EncryptResponseTO encrypt(EncryptRequestTO encryptRequestTO) {
BytesKeyGenerator generator = KeyGenerators.secureRandom(SALT_SIZE);
byte[] saltBytes = generator.generateKey();
String saltString = Hex.encodeHexString(saltBytes);
BytesEncryptor bytesEncryptor = Encryptors.stronger(encryptRequestTO.getKey(), saltString);
byte[] encrypted = bytesEncryptor.encrypt(encryptRequestTO.getUnencryptedBytes());
return new EncryptResponseTO(encrypted, saltBytes);
}
/**
*
* @param decryptRequestTO
* @return
*/
@Override
public DecryptResponseTO decrypt(DecryptRequestTO decryptRequestTO) {
BytesEncryptor bytesEncryptor = Encryptors.stronger(decryptRequestTO.getKey(),
decryptRequestTO.getSaltString());
byte[] decrypted = bytesEncryptor.decrypt(decryptRequestTO.getEncrypted());
return new DecryptResponseTO(decrypted);
}
}

View File

@ -0,0 +1,13 @@
package com.jkgroller.cryptonote.service;
import com.jkgroller.cryptonote.service.to.CreateNoteRequestTO;
import com.jkgroller.cryptonote.service.to.CreateNoteResponseTO;
/**
*
*/
public interface NoteService {
CreateNoteResponseTO createNote(CreateNoteRequestTO createNoteRequestTO);
}

View File

@ -0,0 +1,123 @@
package com.jkgroller.cryptonote.service;
import com.jkgroller.cryptonote.converter.NoteEntityConverter;
import com.jkgroller.cryptonote.entity.NoteEntity;
import com.jkgroller.cryptonote.entity.repository.NoteEntityRepository;
import com.jkgroller.cryptonote.service.to.*;
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;
/**
*
*/
@Service
public class NoteServiceImpl implements NoteService {
@Autowired
private AccountService accountService;
@Autowired
private CipherService cipherService;
@Autowired
private NoteEntityRepository noteEntityRepository;
@Autowired
private NoteEntityConverter noteEntityConverter;
@Autowired
@Qualifier("encryptionKeyGenerator")
private BytesKeyGenerator encryptionKeyGenerator;
@Autowired
@Qualifier("identifierGenerator")
private BytesKeyGenerator identifierGenerator;
/**
* @param createNoteRequestTO
* @return
*/
@Override
public CreateNoteResponseTO createNote(CreateNoteRequestTO createNoteRequestTO) {
//Retrieve user account
RetrieveUserAccountRequestTO retrieveUserAccountRequestTO = new RetrieveUserAccountRequestTO();
retrieveUserAccountRequestTO.setUserName(createNoteRequestTO.getUserName());
retrieveUserAccountRequestTO.setPassword(createNoteRequestTO.getPassword());
RetrieveUserAccountResponseTO retrieveUserAccountResponseTO = accountService.retrieveUserAccount(retrieveUserAccountRequestTO);
//Get the entity ready.
NoteEntity noteEntity = new NoteEntity();
//Generate encryption Key
byte[] key = encryptionKeyGenerator.generateKey();
//Encrypt note name with key
EncryptRequestTO encryptNameRequestTO = new EncryptRequestTO(key, createNoteRequestTO.getNoteName());
EncryptResponseTO encryptNameResponseTO = cipherService.encrypt(encryptNameRequestTO);
//Encrypt note contents with key.
EncryptRequestTO encryptContentsRequestTO = new EncryptRequestTO(key, createNoteRequestTO.getNoteContents());
EncryptResponseTO encryptContentsResponseTO = cipherService.encrypt(encryptContentsRequestTO);
//Decrypt surrogate key.
DecryptRequestTO decryptSurrogateKeyRequestTO = new DecryptRequestTO(createNoteRequestTO.getPassword()
, retrieveUserAccountResponseTO.getAccountTO().getEncryptedSurrogateKey()
, retrieveUserAccountResponseTO.getAccountTO().getSurrogateKeySalt());
DecryptResponseTO decryptSurrogateKeyResponseTO = cipherService.decrypt(decryptSurrogateKeyRequestTO);
//Encrypt the key.
EncryptRequestTO encryptKeyRequestTO = new EncryptRequestTO(decryptSurrogateKeyResponseTO.getUnencrypted(), key);
EncryptResponseTO encryptKeyResponseTO = cipherService.encrypt(encryptKeyRequestTO);
//Generate an identifier.
String noteIdentifier = Hex.encodeHexString(identifierGenerator.generateKey());
//Save it.
noteEntity.setIdentifier(noteIdentifier);
noteEntity.setContents(encryptContentsResponseTO.getEncrypted());
noteEntity.setContentsSalt(encryptContentsResponseTO.getSalt());
noteEntity.setName(encryptNameResponseTO.getEncrypted());
noteEntity.setNameSalt(encryptNameResponseTO.getSalt());
noteEntity.setKey(encryptKeyResponseTO.getEncrypted());
noteEntity.setKeySalt(encryptKeyResponseTO.getSalt());
noteEntity = noteEntityRepository.save(noteEntity);
NoteTO noteTO = decryptNote(noteEntity, key);
CreateNoteResponseTO createNoteResponseTO = new CreateNoteResponseTO();
createNoteResponseTO.setNoteTO(noteTO);
return createNoteResponseTO;
}
private NoteTO decryptNote(NoteEntity noteEntity, byte[] key) {
NoteTO noteTO = new NoteTO();
DecryptRequestTO decryptNoteContentsRequestTO = new DecryptRequestTO(Hex.encodeHexString(key)
, noteEntity.getContents()
, noteEntity.getContentsSalt());
DecryptRequestTO decryptNoteNameRequestTO = new DecryptRequestTO(Hex.encodeHexString(key)
, noteEntity.getContents()
, noteEntity.getContentsSalt());
noteTO.setContents(cipherService.decrypt(decryptNoteContentsRequestTO).getUnencrypted());
noteTO.setName(cipherService.decrypt(decryptNoteNameRequestTO).getUnencrypted());
noteTO.setIdentifier(noteEntity.getIdentifier());
return noteTO;
}
}

View File

@ -0,0 +1,78 @@
package com.jkgroller.cryptonote.service.to;
import com.jkgroller.cryptonote.enums.AccountRole;
import java.time.LocalDateTime;
public class AccountTO {
private String identifier;
private String userName;
private String password;
private AccountRole role;
private LocalDateTime creationDateTime;
private byte[] encryptedSurrogateKey;
private byte[] surrogateKeySalt;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
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 AccountRole getRole() {
return role;
}
public void setRole(AccountRole role) {
this.role = role;
}
public LocalDateTime getCreationDateTime() {
return creationDateTime;
}
public void setCreationDateTime(LocalDateTime creationDateTime) {
this.creationDateTime = creationDateTime;
}
public byte[] getEncryptedSurrogateKey() {
return encryptedSurrogateKey;
}
public void setEncryptedSurrogateKey(byte[] encryptedSurrogateKey) {
this.encryptedSurrogateKey = encryptedSurrogateKey;
}
public byte[] getSurrogateKeySalt() {
return surrogateKeySalt;
}
public void setSurrogateKeySalt(byte[] surrogateKeySalt) {
this.surrogateKeySalt = surrogateKeySalt;
}
}

View File

@ -0,0 +1,18 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class CreateNewAccountResponseTO {
private AccountTO accountTO;
public AccountTO getAccountTO() {
return accountTO;
}
public void setAccountTO(AccountTO accountTO) {
this.accountTO = accountTO;
}
}

View File

@ -0,0 +1,19 @@
package com.jkgroller.cryptonote.service.to;
import com.jkgroller.cryptonote.enums.AccountRole;
/**
*
*/
public class CreateNewUserAccountResponseTO extends CreateNewAccountResponseTO {
/**
*
*/
public CreateNewUserAccountResponseTO() {
AccountTO accountTO = new AccountTO();
accountTO.setRole(AccountRole.USER);
super.setAccountTO(accountTO);
}
}

View File

@ -0,0 +1,78 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class CreateNoteRequestTO {
private String userName;
private String password;
private String noteName;
private String noteContents;
/**
* @return
*/
public String getUserName() {
return userName;
}
/**
*
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
*
* @return
*/
public String getPassword() {
return password;
}
/**
*
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/**
*
* @return
*/
public String getNoteName() {
return noteName;
}
/**
*
* @param noteName
*/
public void setNoteName(String noteName) {
this.noteName = noteName;
}
/**
*
* @return
*/
public String getNoteContents() {
return noteContents;
}
/**
*
* @param noteContents
*/
public void setNoteContents(String noteContents) {
this.noteContents = noteContents;
}
}

View File

@ -0,0 +1,23 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class CreateNoteResponseTO {
private NoteTO noteTO;
/**
* @return
*/
public NoteTO getNoteTO() {
return noteTO;
}
/**
* @param noteTO
*/
public void setNoteTO(NoteTO noteTO) {
this.noteTO = noteTO;
}
}

View File

@ -0,0 +1,60 @@
package com.jkgroller.cryptonote.service.to;
import org.apache.commons.codec.binary.Hex;
/**
*
*/
public class DecryptRequestTO {
private final String key;
private final byte[] encrypted;
private final byte[] salt;
/**
*
* @param key
* @param encrypted
* @param salt
*/
public DecryptRequestTO(String key, byte[] encrypted, byte[] salt) {
super();
this.key = key;
this.encrypted = encrypted;
this.salt = salt;
}
/**
*
* @return
*/
public String getKey() {
return key;
}
/**
*
* @return
*/
public byte[] getEncrypted() {
return encrypted;
}
/**
* @return the salt
*/
public byte[] getSalt() {
return salt;
}
/**
* @return the salt
*/
public String getSaltString() {
return Hex.encodeHexString(salt);
}
}

View File

@ -0,0 +1,35 @@
package com.jkgroller.cryptonote.service.to;
import java.nio.charset.StandardCharsets;
/**
*
*/
public class DecryptResponseTO {
private final String unencrypted;
/**
* @param unencrypted
*/
public DecryptResponseTO(String unencrypted) {
super();
this.unencrypted = unencrypted;
}
/**
* @param unencrypted
*/
public DecryptResponseTO(byte[] unencrypted) {
super();
this.unencrypted = new String(unencrypted, StandardCharsets.UTF_8);
}
/**
* @return the unencrypted
*/
public String getUnencrypted() {
return unencrypted;
}
}

View File

@ -0,0 +1,79 @@
package com.jkgroller.cryptonote.service.to;
import org.apache.commons.codec.binary.Hex;
import java.nio.charset.StandardCharsets;
/**
*
*/
public class EncryptRequestTO {
private final String key;
private final String unencrypted;
/**
* @param key
* @param unencrypted
*/
public EncryptRequestTO(String key, String unencrypted) {
super();
this.key = key;
this.unencrypted = unencrypted;
}
/**
* @param key
* @param unencrypted
*/
public EncryptRequestTO(String key, byte[] unencrypted) {
super();
this.key = key;
this.unencrypted = Hex.encodeHexString(unencrypted);
}
/**
*
* @param key
* @param unencrypted
*/
public EncryptRequestTO(byte[] key, byte[] unencrypted) {
super();
this.key = Hex.encodeHexString(key);
this.unencrypted = Hex.encodeHexString(unencrypted);
}
/**
*
* @param key
* @param unencrypted
*/
public EncryptRequestTO(byte[] key, String unencrypted) {
super();
this.key = Hex.encodeHexString(key);
this.unencrypted = unencrypted;
}
/**
* @return
*/
public String getKey() {
return key;
}
/**
* @return
*/
public String getUnencrypted() {
return unencrypted;
}
/**
* @return
*/
public byte[] getUnencryptedBytes() {
return unencrypted.getBytes(StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,44 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class EncryptResponseTO {
private final byte[] encrypted;
private final byte[] salt;
/**
* @param encrypted
* @param salt
*/
public EncryptResponseTO(byte[] encrypted, byte[] salt) {
this.encrypted = encrypted;
this.salt = salt;
}
/**
*
*/
public EncryptResponseTO() {
this.encrypted = null;
this.salt = null;
}
/**
* @return the encrypted
*/
public byte[] getEncrypted() {
return encrypted;
}
/**
*
* @return
*/
public byte[] getSalt() {
return salt;
}
}

View File

@ -0,0 +1,55 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class NoteTO {
private String identifier;
private String name;
private String contents;
/**
* @return
*/
public String getIdentifier() {
return identifier;
}
/**
* @param identifier
*/
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* @return
*/
public String getName() {
return name;
}
/**
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* @return
*/
public String getContents() {
return contents;
}
/**
* @param contents
*/
public void setContents(String contents) {
this.contents = contents;
}
}

View File

@ -0,0 +1,39 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class RetrieveUserAccountRequestTO {
private String userName;
private String password;
/**
* @return
*/
public String getUserName() {
return userName;
}
/**
* @param userName
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* @return
*/
public String getPassword() {
return password;
}
/**
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,19 @@
package com.jkgroller.cryptonote.service.to;
/**
*
*/
public class RetrieveUserAccountResponseTO {
private AccountTO accountTO;
public AccountTO getAccountTO() {
return accountTO;
}
public void setAccountTO(AccountTO accountTO) {
this.accountTO = accountTO;
}
}

View File

@ -0,0 +1,30 @@
#===============================
# = CRNK
# ===============================
crnk.path-prefix=/v1
crnk.return404-on-null=true
#===============================
# = Security & Generators
# ===============================
password.encryption.strength=12
salt.size=32
encryption.key.length=32
username.length=8
password.length=16
identifier.length=8
#===============================
# = Data Source
# ===============================
spring.datasource.url=jdbc:h2:mem:cryptonote;DB_CLOSE_DELAY=-1
spring.datasource.platform=h2
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=false
#spring.datasource.initialization-mode=ALWAYS
#spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.h2.console.settings.web-allow-others=true

58
src/test/resources/db.sql Normal file
View File

@ -0,0 +1,58 @@
-- --------------------------------------------------------
-- Host: 192.168.0.18
-- Server version: 5.7.18-0ubuntu0.16.04.1 - (Ubuntu)
-- Server OS: Linux
-- HeidiSQL Version: 9.4.0.5169
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-- Dumping database structure for spring_security
CREATE DATABASE IF NOT EXISTS `spring_security` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `spring_security`;
-- Dumping structure for table spring_security.member
CREATE TABLE IF NOT EXISTS `member` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`active` int(1) NOT NULL,
`verified` int(1) NOT NULL,
`email` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`first_name` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`surrogate_key` binary(58) NOT NULL,
`surrogate_key_salt` binary(28) NOT NULL,
`verification_key` char(36) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;
-- Data exporting was unselected.
-- Dumping structure for table spring_security.member_role
CREATE TABLE IF NOT EXISTS `member_role` (
`member_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`member_id`,`role_id`),
KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`),
CONSTRAINT `FK34g7epqlcxqloewku3aoqhhmg` FOREIGN KEY (`member_id`) REFERENCES `member` (`id`),
CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`member_id`) REFERENCES `member` (`id`),
CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `FKdiix07v86r3ntrbs3l02qr7y0` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Data exporting was unselected.
-- Dumping structure for table spring_security.role
CREATE TABLE IF NOT EXISTS `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- Data exporting was unselected.
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;