improved API key handling

This commit is contained in:
2019-03-06 01:55:33 +01:00
parent 7b315f6fd0
commit a7e845d514
14 changed files with 732 additions and 140 deletions

31
account-dao/pom.xml Normal file
View File

@ -0,0 +1,31 @@
<?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>
<parent>
<groupId>de.muehlencord</groupId>
<artifactId>shared</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-account-dao</artifactId>
<packaging>jar</packaging>
<name>shared-account-dao</name>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-util</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package de.muehlencord.shared.account.business.account.entity; package de.muehlencord.shared.account.dao;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;

View File

@ -0,0 +1,43 @@
/*
* Copyright 2019 Joern Muehlencord <joern at muehlencord.de>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.account.dao;
import de.muehlencord.shared.util.DateUtil;
import java.util.Date;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public abstract class ApiKeyUtil {
private ApiKeyUtil() {
// hide constructor for abstract class - only static methods are used
}
public static boolean isValid(ApiKeyObject apiKeyObject) {
if (apiKeyObject == null) {
return false;
}
Date validToDate = apiKeyObject.getExpiresOn();
if (validToDate == null) {
return false;
}
Date now = DateUtil.getCurrentTimeInUTC();
return validToDate.after(now);
}
}

View File

@ -40,10 +40,14 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>de.muehlencord.shared</groupId> <groupId>${project.groupId}</groupId>
<artifactId>shared-jeeutil</artifactId> <artifactId>shared-jeeutil</artifactId>
<type>jar</type> <type>jar</type>
</dependency> </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>shared-account-dao</artifactId>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View File

@ -3,17 +3,17 @@
CAUTION: Do not modify this file unless you know what you are doing. CAUTION: Do not modify this file unless you know what you are doing.
Unexpected results may occur if the code is changed deliberately. Unexpected results may occur if the code is changed deliberately.
--> -->
<dbmodel pgmodeler-ver="0.9.1" author="Joern Muehlencord" last-position="46,0" last-zoom="0.6" <dbmodel pgmodeler-ver="0.9.2-beta" last-position="0,0" last-zoom="0.9" max-obj-count="13"
default-schema="public"> default-owner="postgres">
<database name="account_test" encoding="UTF8" lc-collate="C" lc-ctype="C" is-template="false" allow-conns="true" sql-disabled="true"> <database name="account_test" encoding="UTF8" lc-collate="C" lc-ctype="C" is-template="false" allow-conns="true" sql-disabled="true">
</database> </database>
<schema name="public" fill-color="#e1e1e1" sql-disabled="true"> <schema name="public" layer="0" fill-color="#e1e1e1" sql-disabled="true">
</schema> </schema>
<table name="config" hide-ext-attribs="true"> <table name="config" layer="0" collapse-mode="1" max-obj-count="6">
<schema name="public"/> <schema name="public"/>
<position x="1655" y="520"/> <position x="1480" y="220"/>
<column name="application" not-null="true"> <column name="application" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -34,9 +34,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="application_role" hide-ext-attribs="true"> <table name="application_role" layer="0" collapse-mode="1" max-obj-count="4">
<schema name="public"/> <schema name="public"/>
<position x="5" y="450"/> <position x="100" y="260"/>
<column name="id" not-null="true"> <column name="id" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -57,9 +57,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="account" hide-ext-attribs="true"> <table name="account" layer="0" collapse-mode="1" max-obj-count="12">
<schema name="public"/> <schema name="public"/>
<position x="1020" y="795"/> <position x="1080" y="460"/>
<column name="id" not-null="true"> <column name="id" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -98,9 +98,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="account_history" hide-ext-attribs="true"> <table name="account_history" layer="0" collapse-mode="1" max-obj-count="8">
<schema name="public"/> <schema name="public"/>
<position x="230" y="930"/> <position x="180" y="640"/>
<column name="id" not-null="true"> <column name="id" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -127,9 +127,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="account_role" hide-ext-attribs="true"> <table name="account_role" layer="0" collapse-mode="1" max-obj-count="3">
<schema name="public"/> <schema name="public"/>
<position x="540" y="620"/> <position x="480" y="440"/>
<column name="account" not-null="true"> <column name="account" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -141,9 +141,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="application_permission" hide-ext-attribs="true"> <table name="application_permission" layer="0" collapse-mode="1" max-obj-count="4">
<schema name="public"/> <schema name="public"/>
<position x="1290" y="180"/> <position x="1080" y="100"/>
<column name="id" not-null="true"> <column name="id" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -164,9 +164,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="role_permission" hide-ext-attribs="true"> <table name="role_permission" layer="0" collapse-mode="1" max-obj-count="3">
<schema name="public"/> <schema name="public"/>
<position x="585" y="165"/> <position x="560" y="80"/>
<column name="application_role" not-null="true"> <column name="application_role" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -178,9 +178,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="mail_template" hide-ext-attribs="true"> <table name="mail_template" layer="0" collapse-mode="1" max-obj-count="2">
<schema name="public"/> <schema name="public"/>
<position x="290" y="1235"/> <position x="120" y="940"/>
<column name="template_name" not-null="true"> <column name="template_name" not-null="true">
<type name="character varying" length="40"/> <type name="character varying" length="40"/>
</column> </column>
@ -192,9 +192,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="application" hide-ext-attribs="true"> <table name="application" layer="0" collapse-mode="1" max-obj-count="2">
<schema name="public"/> <schema name="public"/>
<position x="840" y="415"/> <position x="720" y="320"/>
<column name="id" not-null="true"> <column name="id" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -210,9 +210,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
<schema name="public"/> <schema name="public"/>
</extension> </extension>
<table name="account_login" hide-ext-attribs="true"> <table name="account_login" layer="0" collapse-mode="1" max-obj-count="15">
<schema name="public"/> <schema name="public"/>
<position x="1670" y="755"/> <position x="1520" y="680"/>
<column name="id" not-null="true" default-value="uuid_generate_v4()"> <column name="id" not-null="true" default-value="uuid_generate_v4()">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -260,9 +260,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
</constraint> </constraint>
</table> </table>
<table name="api_key"> <table name="api_key" layer="0" collapse-mode="1" max-obj-count="7">
<schema name="public"/> <schema name="public"/>
<position x="1612.5" y="1057.5"/> <position x="680" y="760"/>
<column name="id" not-null="true"> <column name="id" not-null="true">
<type name="uuid" length="0"/> <type name="uuid" length="0"/>
</column> </column>
@ -278,6 +278,9 @@ CAUTION: Do not modify this file unless you know what you are doing.
<column name="expiration"> <column name="expiration">
<type name="smallint" length="0"/> <type name="smallint" length="0"/>
</column> </column>
<column name="expires_on" not-null="true">
<type name="timestamptz" length="0"/>
</column>
<constraint name="api_key_pk" type="pk-constr" table="public.api_key"> <constraint name="api_key_pk" type="pk-constr" table="public.api_key">
<columns names="id" ref-type="src-columns"/> <columns names="id" ref-type="src-columns"/>
</constraint> </constraint>
@ -349,62 +352,57 @@ CAUTION: Do not modify this file unless you know what you are doing.
<columns names="id" ref-type="dst-columns"/> <columns names="id" ref-type="dst-columns"/>
</constraint> </constraint>
<relationship name="rel_account_history_account" type="relfk" <relationship name="rel_config_account" type="relfk" layer="0"
custom-color="#83af1f"
src-table="public.account_history"
dst-table="public.account" reference-fk="fk_account_history_username_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_account_role_account" type="relfk"
custom-color="#5aa578"
src-table="public.account_role"
dst-table="public.account" reference-fk="fk_account_role_account"
src-required="false" dst-required="false"/>
<relationship name="rel_account_role_application_role" type="relfk"
custom-color="#4b3e56"
src-table="public.account_role"
dst-table="public.application_role" reference-fk="fk_account_role_role_name"
src-required="false" dst-required="false"/>
<relationship name="rel_role_permission_application_role" type="relfk"
custom-color="#9ac721"
src-table="public.role_permission"
dst-table="public.application_role" reference-fk="fk_role_permission_application_role"
src-required="false" dst-required="false"/>
<relationship name="rel_role_permission_application_permission" type="relfk"
custom-color="#249b49"
src-table="public.role_permission"
dst-table="public.application_permission" reference-fk="fk_role_permission_role_permission"
src-required="false" dst-required="false"/>
<relationship name="rel_config_account" type="relfk"
src-table="public.config" src-table="public.config"
dst-table="public.account" reference-fk="config_key_account_fk" dst-table="public.account" reference-fk="config_key_account_fk"
src-required="false" dst-required="false"/> src-required="false" dst-required="false"/>
<relationship name="rel_application_role_application" type="relfk" <relationship name="rel_config_application" type="relfk" layer="0"
src-table="public.application_role"
dst-table="public.application" reference-fk="application_role_app_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_application_permission_application" type="relfk"
src-table="public.application_permission"
dst-table="public.application" reference-fk="application_permission_app_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_account_login_account" type="relfk"
src-table="public.account_login"
dst-table="public.account" reference-fk="account_login_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_config_application" type="relfk"
src-table="public.config" src-table="public.config"
dst-table="public.application" reference-fk="config_application_fk" dst-table="public.application" reference-fk="config_application_fk"
src-required="false" dst-required="false"/> src-required="false" dst-required="false"/>
<relationship name="rel_api_key_account" type="relfk" <relationship name="rel_application_role_application" type="relfk" layer="0"
src-table="public.application_role"
dst-table="public.application" reference-fk="application_role_app_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_account_history_account" type="relfk" layer="0"
src-table="public.account_history"
dst-table="public.account" reference-fk="fk_account_history_username_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_account_role_account" type="relfk" layer="0"
src-table="public.account_role"
dst-table="public.account" reference-fk="fk_account_role_account"
src-required="false" dst-required="false"/>
<relationship name="rel_account_role_application_role" type="relfk" layer="0"
src-table="public.account_role"
dst-table="public.application_role" reference-fk="fk_account_role_role_name"
src-required="false" dst-required="false"/>
<relationship name="rel_application_permission_application" type="relfk" layer="0"
src-table="public.application_permission"
dst-table="public.application" reference-fk="application_permission_app_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_role_permission_application_role" type="relfk" layer="0"
src-table="public.role_permission"
dst-table="public.application_role" reference-fk="fk_role_permission_application_role"
src-required="false" dst-required="false"/>
<relationship name="rel_role_permission_application_permission" type="relfk" layer="0"
src-table="public.role_permission"
dst-table="public.application_permission" reference-fk="fk_role_permission_role_permission"
src-required="false" dst-required="false"/>
<relationship name="rel_account_login_account" type="relfk" layer="0"
src-table="public.account_login"
dst-table="public.account" reference-fk="account_login_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_api_key_account" type="relfk" layer="0"
src-table="public.api_key" src-table="public.api_key"
dst-table="public.account" reference-fk="api_key_account_fk" dst-table="public.account" reference-fk="api_key_account_fk"
src-required="false" dst-required="false"/> src-required="false" dst-required="false"/>

View File

@ -0,0 +1,407 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
CAUTION: Do not modify this file unless you know what you are doing.
Unexpected results may occur if the code is changed deliberately.
-->
<dbmodel pgmodeler-ver="0.9.2-beta" last-position="0,0" last-zoom="1" max-obj-count="13"
default-owner="postgres">
<database name="account_test" encoding="UTF8" lc-collate="C" lc-ctype="C" is-template="false" allow-conns="true" sql-disabled="true">
</database>
<schema name="public" layer="0" fill-color="#e1e1e1" sql-disabled="true">
</schema>
<table name="config" layer="0" collapse-mode="2" max-obj-count="6">
<schema name="public"/>
<position x="1655" y="520"/>
<column name="application" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="config_key" not-null="true">
<type name="varchar" length="100"/>
</column>
<column name="config_key_account" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="config_key_group">
<type name="varchar" length="200"/>
</column>
<column name="config_value">
<type name="varchar" length="200"/>
</column>
<constraint name="config_pk" type="pk-constr" table="public.config">
<columns names="application,config_key,config_key_account" ref-type="src-columns"/>
</constraint>
</table>
<table name="application_role" layer="0" collapse-mode="2" max-obj-count="4">
<schema name="public"/>
<position x="5" y="450"/>
<column name="id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="application" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="role_name" not-null="true">
<type name="character varying" length="80"/>
</column>
<column name="role_description" not-null="true">
<type name="character varying" length="200"/>
</column>
<constraint name="application_role_pk" type="pk-constr" table="public.application_role">
<columns names="id" ref-type="src-columns"/>
</constraint>
<constraint name="application_role_name_uidx" type="uq-constr" table="public.application_role">
<columns names="application,role_name" ref-type="src-columns"/>
</constraint>
</table>
<table name="account" layer="0" collapse-mode="2" max-obj-count="12">
<schema name="public"/>
<position x="1020" y="795"/>
<column name="id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="username" not-null="true">
<type name="character varying" length="32"/>
</column>
<column name="emailaddress" not-null="true">
<type name="character varying" length="200"/>
</column>
<column name="firstname" not-null="true">
<type name="character varying" length="100"/>
</column>
<column name="lastname" not-null="true">
<type name="character varying" length="100"/>
</column>
<column name="status" not-null="true" default-value="'NEW'">
<type name="character varying" length="10"/>
</column>
<column name="created_on" not-null="true" default-value="timezone('utc'::text, now())">
<type name="timestamp with time zone" length="0" with-timezone="true"/>
</column>
<column name="created_by" not-null="true">
<type name="character varying" length="32"/>
</column>
<column name="last_updated_on" not-null="true" default-value="timezone('utc'::text, now())">
<type name="timestamp with time zone" length="0" with-timezone="true"/>
</column>
<column name="last_updated_by" not-null="true">
<type name="character varying" length="32"/>
</column>
<constraint name="pk_account" type="pk-constr" table="public.account">
<columns names="id" ref-type="src-columns"/>
</constraint>
<constraint name="uidx_username" type="uq-constr" table="public.account">
<columns names="username" ref-type="src-columns"/>
</constraint>
</table>
<table name="account_history" layer="0" collapse-mode="2" max-obj-count="8">
<schema name="public"/>
<position x="230" y="930"/>
<column name="id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="account_id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="message">
<type name="character varying" length="200"/>
</column>
<column name="failure_count" not-null="true" default-value="0">
<type name="integer" length="0"/>
</column>
<column name="status" not-null="true">
<type name="character varying" length="20"/>
</column>
<column name="last_updated_on" not-null="true" default-value="timezone('utc'::text, now())">
<type name="timestamp with time zone" length="0" with-timezone="true"/>
</column>
<column name="last_updated_by" not-null="true">
<type name="character varying" length="32"/>
</column>
<constraint name="pk_account_history" type="pk-constr" table="public.account_history">
<columns names="id" ref-type="src-columns"/>
</constraint>
</table>
<table name="account_role" layer="0" collapse-mode="2" max-obj-count="3">
<schema name="public"/>
<position x="540" y="620"/>
<column name="account" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="account_role" not-null="true">
<type name="uuid" length="0"/>
</column>
<constraint name="pk_account_role" type="pk-constr" table="public.account_role">
<columns names="account,account_role" ref-type="src-columns"/>
</constraint>
</table>
<table name="application_permission" layer="0" collapse-mode="2" max-obj-count="4">
<schema name="public"/>
<position x="1290" y="180"/>
<column name="id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="application" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="permission_name" not-null="true">
<type name="character varying" length="80"/>
</column>
<column name="permission_description" not-null="true">
<type name="character varying" length="200"/>
</column>
<constraint name="pk_application_permission" type="pk-constr" table="public.application_permission">
<columns names="id" ref-type="src-columns"/>
</constraint>
<constraint name="application_permission_name_uidx" type="uq-constr" table="public.application_permission">
<columns names="application,permission_name" ref-type="src-columns"/>
</constraint>
</table>
<table name="role_permission" layer="0" collapse-mode="2" max-obj-count="3">
<schema name="public"/>
<position x="585" y="165"/>
<column name="application_role" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="role_permission" not-null="true">
<type name="uuid" length="0"/>
</column>
<constraint name="pk_role_permission_role_permission_name" type="pk-constr" table="public.role_permission">
<columns names="application_role,role_permission" ref-type="src-columns"/>
</constraint>
</table>
<table name="mail_template" layer="0" collapse-mode="2" max-obj-count="2">
<schema name="public"/>
<position x="290" y="1235"/>
<column name="template_name" not-null="true">
<type name="character varying" length="40"/>
</column>
<column name="template_value" not-null="true">
<type name="text" length="0"/>
</column>
<constraint name="mail_template_pk" type="pk-constr" table="public.mail_template">
<columns names="template_name" ref-type="src-columns"/>
</constraint>
</table>
<table name="application" layer="0" collapse-mode="2" max-obj-count="2">
<schema name="public"/>
<position x="840" y="415"/>
<column name="id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="application_name" not-null="true">
<type name="varchar" length="200"/>
</column>
<constraint name="application_pk" type="pk-constr" table="public.application">
<columns names="id" ref-type="src-columns"/>
</constraint>
</table>
<extension name="uuid-ossp" sql-disabled="true">
<schema name="public"/>
</extension>
<table name="account_login" layer="0" collapse-mode="2" max-obj-count="15">
<schema name="public"/>
<position x="1670" y="755"/>
<column name="id" not-null="true" default-value="uuid_generate_v4()">
<type name="uuid" length="0"/>
</column>
<column name="account" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="account_password" not-null="true">
<type name="varchar" length="200"/>
</column>
<column name="last_login">
<type name="timestamptz" length="0"/>
</column>
<column name="last_failed_login">
<type name="timestamptz" length="0"/>
</column>
<column name="failure_count" not-null="true" default-value="0">
<type name="integer" length="0"/>
</column>
<column name="password_reset_ongoing" not-null="true" default-value="false">
<type name="boolean" length="0"/>
</column>
<column name="password_reset_valid_to">
<type name="timestamptz" length="0"/>
</column>
<column name="password_reset_hash">
<type name="varchar" length="200"/>
</column>
<column name="created_on" not-null="true" default-value="timezone('utc'::text, now())">
<type name="timestamptz" length="0"/>
</column>
<column name="created_by" not-null="true">
<type name="varchar" length="32"/>
</column>
<column name="last_updated_on" not-null="true" default-value="timezone('utc'::text, now())">
<type name="timestamptz" length="0"/>
</column>
<column name="last_updated_by" not-null="true">
<type name="varchar" length="32"/>
</column>
<constraint name="account_login_pk" type="pk-constr" table="public.account_login">
<columns names="id" ref-type="src-columns"/>
</constraint>
<constraint name="account_login_uidx" type="uq-constr" table="public.account_login">
<columns names="account" ref-type="src-columns"/>
</constraint>
</table>
<table name="api_key" layer="0" collapse-mode="2" max-obj-count="6">
<schema name="public"/>
<position x="1612.5" y="1057.5"/>
<column name="id" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="account" not-null="true">
<type name="uuid" length="0"/>
</column>
<column name="api_key" not-null="true">
<type name="varchar" length="200"/>
</column>
<column name="issued_on" not-null="true" default-value="timezone('utc'::text, now())">
<type name="timestamptz" length="0"/>
</column>
<column name="expiration">
<type name="smallint" length="0"/>
</column>
<constraint name="api_key_pk" type="pk-constr" table="public.api_key">
<columns names="id" ref-type="src-columns"/>
</constraint>
</table>
<constraint name="config_key_account_fk" type="fk-constr" comparison-type="MATCH FULL"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.account" table="public.config">
<columns names="config_key_account" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="config_application_fk" type="fk-constr" comparison-type="MATCH FULL"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.application" table="public.config">
<columns names="application" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="application_role_app_fk" type="fk-constr" comparison-type="MATCH FULL"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.application" table="public.application_role">
<columns names="application" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="fk_account_history_username_fk" type="fk-constr" comparison-type="MATCH SIMPLE"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.account" table="public.account_history">
<columns names="account_id" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="fk_account_role_account" type="fk-constr" comparison-type="MATCH SIMPLE"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.account" table="public.account_role">
<columns names="account" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="fk_account_role_role_name" type="fk-constr" comparison-type="MATCH SIMPLE"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.application_role" table="public.account_role">
<columns names="account_role" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="application_permission_app_fk" type="fk-constr" comparison-type="MATCH FULL"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.application" table="public.application_permission">
<columns names="application" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="fk_role_permission_application_role" type="fk-constr" comparison-type="MATCH SIMPLE"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.application_role" table="public.role_permission">
<columns names="application_role" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="fk_role_permission_role_permission" type="fk-constr" comparison-type="MATCH SIMPLE"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.application_permission" table="public.role_permission">
<columns names="role_permission" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="account_login_fk" type="fk-constr" comparison-type="MATCH FULL"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.account" table="public.account_login">
<columns names="account" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<constraint name="api_key_account_fk" type="fk-constr" comparison-type="MATCH FULL"
upd-action="NO ACTION" del-action="NO ACTION" ref-table="public.account" table="public.api_key">
<columns names="account" ref-type="src-columns"/>
<columns names="id" ref-type="dst-columns"/>
</constraint>
<relationship name="rel_config_account" type="relfk" layer="0"
src-table="public.config"
dst-table="public.account" reference-fk="config_key_account_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_config_application" type="relfk" layer="0"
src-table="public.config"
dst-table="public.application" reference-fk="config_application_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_application_role_application" type="relfk" layer="0"
src-table="public.application_role"
dst-table="public.application" reference-fk="application_role_app_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_account_history_account" type="relfk" layer="0"
src-table="public.account_history"
dst-table="public.account" reference-fk="fk_account_history_username_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_account_role_account" type="relfk" layer="0"
src-table="public.account_role"
dst-table="public.account" reference-fk="fk_account_role_account"
src-required="false" dst-required="false"/>
<relationship name="rel_account_role_application_role" type="relfk" layer="0"
src-table="public.account_role"
dst-table="public.application_role" reference-fk="fk_account_role_role_name"
src-required="false" dst-required="false"/>
<relationship name="rel_application_permission_application" type="relfk" layer="0"
src-table="public.application_permission"
dst-table="public.application" reference-fk="application_permission_app_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_role_permission_application_role" type="relfk" layer="0"
src-table="public.role_permission"
dst-table="public.application_role" reference-fk="fk_role_permission_application_role"
src-required="false" dst-required="false"/>
<relationship name="rel_role_permission_application_permission" type="relfk" layer="0"
src-table="public.role_permission"
dst-table="public.application_permission" reference-fk="fk_role_permission_role_permission"
src-required="false" dst-required="false"/>
<relationship name="rel_account_login_account" type="relfk" layer="0"
src-table="public.account_login"
dst-table="public.account" reference-fk="account_login_fk"
src-required="false" dst-required="false"/>
<relationship name="rel_api_key_account" type="relfk" layer="0"
src-table="public.api_key"
dst-table="public.account" reference-fk="api_key_account_fk"
src-required="false" dst-required="false"/>
</dbmodel>

View File

@ -18,10 +18,10 @@ package de.muehlencord.shared.account.business.account.boundary;
import de.muehlencord.shared.account.business.account.control.AccountControl; import de.muehlencord.shared.account.business.account.control.AccountControl;
import de.muehlencord.shared.account.business.account.entity.AccountEntity; import de.muehlencord.shared.account.business.account.entity.AccountEntity;
import de.muehlencord.shared.account.business.account.entity.ApiKeyEntity; import de.muehlencord.shared.account.business.account.entity.ApiKeyEntity;
import de.muehlencord.shared.account.business.account.entity.ApiKeyObject;
import de.muehlencord.shared.account.business.account.entity.JWTObject; import de.muehlencord.shared.account.business.account.entity.JWTObject;
import de.muehlencord.shared.account.business.config.boundary.ConfigService; import de.muehlencord.shared.account.business.config.boundary.ConfigService;
import de.muehlencord.shared.account.business.config.entity.ConfigException; import de.muehlencord.shared.account.business.config.entity.ConfigException;
import de.muehlencord.shared.account.dao.ApiKeyObject;
import de.muehlencord.shared.account.util.AccountPU; import de.muehlencord.shared.account.util.AccountPU;
import de.muehlencord.shared.jeeutil.jwt.JWTDecoder; import de.muehlencord.shared.jeeutil.jwt.JWTDecoder;
import de.muehlencord.shared.jeeutil.jwt.JWTEncoder; import de.muehlencord.shared.jeeutil.jwt.JWTEncoder;
@ -45,6 +45,11 @@ import javax.ejb.TransactionAttributeType;
import javax.inject.Inject; import javax.inject.Inject;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.Query; import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -112,20 +117,37 @@ public class ApiKeyService implements Serializable {
return apiKeys.get(0); return apiKeys.get(0);
} }
public List<ApiKeyEntity> getUsersApiKeys(AccountEntity account) { public List<ApiKeyEntity> getUsersApiKeys(AccountEntity account, boolean onlyValid) {
Query query = em.createNamedQuery("ApiKeyEntity.findByAccount");
query.setParameter("account", account); Date now = DateUtil.getCurrentTimeInUTC();
List<ApiKeyEntity> keys = query.getResultList(); CriteriaBuilder cb = em.getCriteriaBuilder();
if (keys == null) { CriteriaQuery<ApiKeyEntity> cq = cb.createQuery(ApiKeyEntity.class);
Root<ApiKeyEntity> root = cq.from(ApiKeyEntity.class);
Predicate accountPredicate = cb.equal(root.get("account"), account);
Predicate searchPredicate;
if (onlyValid) {
Predicate expiresOnPredicate = cb.greaterThanOrEqualTo(root.get("expiresOn"), now);
searchPredicate = cb.and(accountPredicate, expiresOnPredicate);
} else {
searchPredicate = accountPredicate;
}
cq.where(searchPredicate);
cq.orderBy(cb.desc(root.get("expiresOn")));
TypedQuery<ApiKeyEntity> query = em.createQuery(cq);
List<ApiKeyEntity> resultList = query.getResultList();
if (resultList == null) {
return new ArrayList<>(); return new ArrayList<>();
} else { } else {
return keys; return resultList;
} }
} }
public List<ApiKeyEntity> getUsersApiKeys(String userName) { public List<ApiKeyEntity> getUsersApiKeys(String userName) {
return getUsersApiKeys(accountControl.getAccountEntity(userName, false)); return getUsersApiKeys(accountControl.getAccountEntity(userName, false), false);
}
public List<ApiKeyEntity> getValidUsersApiKeys(String userName) {
return getUsersApiKeys(accountControl.getAccountEntity(userName, false), true);
} }
@Transactional @Transactional
@ -143,16 +165,25 @@ public class ApiKeyService implements Serializable {
Date now = DateUtil.getCurrentTimeInUTC(); Date now = DateUtil.getCurrentTimeInUTC();
ZonedDateTime issuedOn = ZonedDateTime.ofInstant(now.toInstant(), ZoneId.of("UTC")); ZonedDateTime issuedOn = ZonedDateTime.ofInstant(now.toInstant(), ZoneId.of("UTC"));
ZonedDateTime expiresOn = issuedOn.plusMinutes(expirationInMinutes); ZonedDateTime expiresOn = issuedOn.plusMinutes(expirationInMinutes);
Date expiresOnDate = Date.from(expiresOn.toInstant());
String apiKeyString = RandomStringUtils.randomAscii(50); String apiKeyString = RandomStringUtils.randomAscii(50);
ApiKeyEntity apiKey = new ApiKeyEntity(); ApiKeyEntity apiKey = new ApiKeyEntity();
apiKey.setAccount(accountControl.getAccountEntity(userName, false)); apiKey.setAccount(accountControl.getAccountEntity(userName, false));
apiKey.setApiKey(apiKeyString); apiKey.setApiKey(apiKeyString);
apiKey.setIssuedOn(now); apiKey.setIssuedOn(now);
apiKey.setExpiresOn(expiresOnDate);
apiKey.setExpiration(expirationInMinutes); apiKey.setExpiration(expirationInMinutes);
return getApiKeyObject(apiKey);
}
public ApiKeyObject getApiKeyObject(ApiKeyEntity apiKey) throws ApiKeyException {
ZonedDateTime issuedOn = ZonedDateTime.ofInstant(apiKey.getIssuedOn().toInstant(), ZoneId.of("UTC"));
ZonedDateTime expiresOn = issuedOn.plusMinutes(expirationInMinutes);
String userName = apiKey.getAccount().getUsername();
try { try {
String jwtString = JWTEncoder.encode(password, issuer, issuedOn, apiKey.getAccount().getUsername(), apiKey.getApiKey(), apiKey.getExpiration()); String jwtString = JWTEncoder.encode(password, issuer, issuedOn, userName, apiKey.getApiKey(), apiKey.getExpiration());
em.persist(apiKey); em.persist(apiKey);
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Created API key for {}, valid for {} minutes", userName, expirationInMinutes); LOGGER.debug("Created API key for {}, valid for {} minutes", userName, expirationInMinutes);
@ -189,7 +220,7 @@ public class ApiKeyService implements Serializable {
if (userAccount == null) { if (userAccount == null) {
throw new JWTException("AccountControl exception"); throw new JWTException("AccountControl exception");
} }
List<ApiKeyEntity> apiKeys = getUsersApiKeys(userAccount); List<ApiKeyEntity> apiKeys = getUsersApiKeys(userAccount, true);
if (LOGGER.isTraceEnabled()) { if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Found {} keys for user {}", apiKeys.size(), userName); LOGGER.trace("Found {} keys for user {}", apiKeys.size(), userName);
} }

View File

@ -48,13 +48,15 @@ import org.hibernate.annotations.Type;
@XmlRootElement @XmlRootElement
@NamedQueries({ @NamedQueries({
@NamedQuery(name = "ApiKeyEntity.findAll", query = "SELECT a FROM ApiKeyEntity a"), @NamedQuery(name = "ApiKeyEntity.findAll", query = "SELECT a FROM ApiKeyEntity a"),
@NamedQuery(name = "ApiKeyEntity.findByApiKey", query = "SELECT a FROM ApiKeyEntity a WHERE a.apiKey = :apiKey", hints = { @NamedQuery(name = "ApiKeyEntity.findByApiKey", query = "SELECT a FROM ApiKeyEntity a WHERE a.apiKey = :apiKey",
@QueryHint(name = "org.hibernate.cacheable", value = "true"), hints = {
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}), @QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApiKeyEntity.findByIssuedOn", query = "SELECT a FROM ApiKeyEntity a WHERE a.issuedOn = :issuedOn"), @NamedQuery(name = "ApiKeyEntity.findByIssuedOn", query = "SELECT a FROM ApiKeyEntity a WHERE a.issuedOn = :issuedOn"),
@NamedQuery(name = "ApiKeyEntity.findByAccount", query = "SELECT a FROM ApiKeyEntity a WHERE a.account = :account", hints = { @NamedQuery(name = "ApiKeyEntity.findByAccount", query = "SELECT a FROM ApiKeyEntity a WHERE a.account = :account ORDER BY a.issuedOn DESC",
@QueryHint(name = "org.hibernate.cacheable", value = "true"), hints = {
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}), @QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApiKeyEntity.findByExpiration", query = "SELECT a FROM ApiKeyEntity a WHERE a.expiration = :expiration")}) @NamedQuery(name = "ApiKeyEntity.findByExpiration", query = "SELECT a FROM ApiKeyEntity a WHERE a.expiration = :expiration")})
public class ApiKeyEntity implements Serializable { public class ApiKeyEntity implements Serializable {
@ -80,6 +82,11 @@ public class ApiKeyEntity implements Serializable {
private Date issuedOn; private Date issuedOn;
@Column(name = "expiration") @Column(name = "expiration")
private Short expiration; private Short expiration;
@Basic(optional = false)
@NotNull
@Column(name = "expires_on")
@Temporal(TemporalType.TIMESTAMP)
private Date expiresOn;
@JoinColumn(name = "account", referencedColumnName = "id") @JoinColumn(name = "account", referencedColumnName = "id")
@ManyToOne(optional = false) @ManyToOne(optional = false)
private AccountEntity account; private AccountEntity account;
@ -120,6 +127,14 @@ public class ApiKeyEntity implements Serializable {
this.expiration = expiration; this.expiration = expiration;
} }
public Date getExpiresOn() {
return expiresOn;
}
public void setExpiresOn(Date expiresOn) {
this.expiresOn = expiresOn;
}
public AccountEntity getAccount() { public AccountEntity getAccount() {
return account; return account;
} }

View File

@ -59,17 +59,14 @@ public class ConfigService implements Serializable {
ApplicationEntity application; ApplicationEntity application;
/** /**
* returns global config key which is not assigned to any. If more than one * returns global config key which is not assigned to any. If more than one value is defined for the given key, the
* value is defined for the given key, the key assigned to system is * key assigned to system is returned. If more than one key is defined but system key is not defined, an exception
* returned. If more than one key is defined but system key is not defined, * is thrown.
* an exception is thrown.
* *
* @param configKey the key to return * @param configKey the key to return
* @return the configValue belonging to the given configKey * @return the configValue belonging to the given configKey
* @throws * @throws de.muehlencord.shared.account.business.config.entity.ConfigException if more than one value is defined
* de.muehlencord.shared.account.business.config.entity.ConfigException if * for the given key but none of the values is defined for the system user
* more than one value is defined for the given key but none of the values
* is defined for the system user
*/ */
@Lock(LockType.READ) @Lock(LockType.READ)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@ -98,6 +95,16 @@ public class ConfigService implements Serializable {
} }
} }
// TODO replace with DAO?
@Lock(LockType.READ)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List<ConfigEntity> getApplicationConfigItems() {
Query query = em.createNamedQuery("ConfigEntity.findByApplication");
query.setParameter("application", application);
List<ConfigEntity> configList = query.getResultList();
return configList;
}
@Lock(LockType.READ) @Lock(LockType.READ)
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public String getConfigValue(String configKey, String defaultValue) throws ConfigException { public String getConfigValue(String configKey, String defaultValue) throws ConfigException {

View File

@ -47,6 +47,10 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
hints = { hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"), @QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}), @QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ConfigEntity.findByApplication", query = "SELECT c FROM ConfigEntity c WHERE c.configPK.application = :application",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ConfigEntity.findByConfigKey", query = "SELECT c FROM ConfigEntity c WHERE c.configPK.application = :application AND c.configPK.configKey = :configKey", @NamedQuery(name = "ConfigEntity.findByConfigKey", query = "SELECT c FROM ConfigEntity c WHERE c.configPK.application = :application AND c.configPK.configKey = :configKey",
hints = { hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"), @QueryHint(name = "org.hibernate.cacheable", value = "true"),

View File

@ -42,20 +42,25 @@ public class StartupBean {
ApplicationEntity application; ApplicationEntity application;
public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
try { if (application == null) {
LOGGER.info("Starting application {}", application.getApplicationName()); LOGGER.error("Application not initialized");
String instanceName = configService.getConfigValue("base.instance", "Development System", true); throw new RuntimeException ("Application not initilized, validate applicationUID mapping");
LOGGER.info("instanceName={}", instanceName); } else {
try {
LOGGER.info("Starting application {}", application.getApplicationName());
String instanceName = configService.getConfigValue("base.instance", "Development System", true);
LOGGER.info("instanceName={}", instanceName);
// ensure maxFailedLogins is available // ensure maxFailedLogins is available
configService.getConfigValue("account.maxFailedLogins", "5", true); configService.getConfigValue("account.maxFailedLogins", "5", true);
LOGGER.info("Application startup complete"); LOGGER.info("Application startup complete");
} catch (ConfigException ex) { } catch (ConfigException ex) {
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug(ex.toString(), ex); LOGGER.debug(ex.toString(), ex);
} else { } else {
LOGGER.error(ex.toString()); LOGGER.error(ex.toString());
}
} }
} }
} }

View File

@ -26,6 +26,7 @@ import javax.ejb.EJB;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces; import javax.enterprise.inject.Produces;
import javax.inject.Named; import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -65,7 +66,12 @@ public class ApplicationController {
version = props.getProperty("build.version"); version = props.getProperty("build.version");
buildDate = props.getProperty("build.timestamp"); buildDate = props.getProperty("build.timestamp");
uuid = UUID.fromString(props.getProperty("application.uuid")); String uuidString = props.getProperty("application.uuid");
if (StringUtils.isEmpty(uuidString)) {
throw new RuntimeException("ApplicationId not defined, please check database setup");
} else {
uuid = UUID.fromString(uuidString);
}
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("buildInfo.properties parsed successfully"); LOGGER.debug("buildInfo.properties parsed successfully");
@ -82,7 +88,7 @@ public class ApplicationController {
if (uuid != null) { if (uuid != null) {
this.application = applicationService.findById(uuid); this.application = applicationService.findById(uuid);
if (application == null) { if (application == null) {
throw new RuntimeException("ApplicationId "+uuid.toString()+" not readable, application will not be able to run. You need to setup application in account database first."); throw new RuntimeException("ApplicationId " + uuid.toString() + " not readable, application will not be able to run. You need to setup application in account database first.");
} else { } else {
LOGGER.info("Found application {} with id {}", application.getApplicationName(), uuid.toString()); LOGGER.info("Found application {} with id {}", application.getApplicationName(), uuid.toString());
} }
@ -90,8 +96,9 @@ public class ApplicationController {
} }
/** /**
* needs to return link to "Account UI" and not to current selected application * needs to return link to "Account UI" and not to current selected application TODO: ensure only Account UI can
* TODO: ensure only Account UI can call functions where application can be handed in - all other applications need to call the function which use the injected application * call functions where application can be handed in - all other applications need to call the function which use
* the injected application
*/ */
@Produces @Produces
public ApplicationEntity getApplication() { public ApplicationEntity getApplication() {

View File

@ -0,0 +1,28 @@
package de.muehlencord.shared.account.business.account.boundary;
import org.junit.Test;
import org.slf4j.LoggerFactory;
/**
*
* @author Joern Muehlencord <joern at muehlencord.de>
*/
public class ApiKeyServiceTest {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ApiKeyServiceTest.class);
@Test
public void testCreateApiKey() {
try {
ApiKeyService apiKeyService = new ApiKeyService();
apiKeyService.createNewApiKey("web", (short) 120);
} catch (ApiKeyException ex) {
LOGGER.error(ex.getMessage());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
}
}
}
}

12
pom.xml
View File

@ -18,6 +18,7 @@
<module>shiro-faces</module> <module>shiro-faces</module>
<module>poi-util</module> <module>poi-util</module>
<module>account-ui</module> <module>account-ui</module>
<module>account-dao</module>
</modules> </modules>
<scm> <scm>
@ -34,6 +35,11 @@
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-account-dao</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>de.muehlencord.shared</groupId> <groupId>de.muehlencord.shared</groupId>
<artifactId>shared-shiro-faces</artifactId> <artifactId>shared-shiro-faces</artifactId>
@ -122,6 +128,12 @@
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.8.5</version> <version>2.8.5</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
<type>jar</type>
</dependency>
<dependency> <dependency>
<groupId>org.apache.shiro</groupId> <groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId> <artifactId>shiro-core</artifactId>