Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf433524cd | |||
| a64051dc28 | |||
| 91f8c2b2f1 | |||
| 7161c32a61 | |||
| be34fa9e8d | |||
| 4ec319fc45 | |||
| bb3d08360e | |||
| 336e76f536 | |||
| 70a45b1919 | |||
| 59969ccef4 | |||
| 4f3c3d4673 | |||
| 70829f9204 | |||
| 91b967f008 | |||
| eb332461aa | |||
| 4fd1710503 | |||
| 3d1f2a93cf | |||
| f8fe805ba6 | |||
| cc7e1b5e73 | |||
| 4aafdb1222 | |||
| cdc5a2ae8c | |||
| b49fb0e19d | |||
| e75523e9dd | |||
| 0d0984bb53 | |||
| 827b110e7c | |||
| 701f0dd3a0 | |||
| a7e845d514 | |||
| 7b315f6fd0 | |||
| 70bebd4ef8 | |||
| d76154e279 | |||
| b30b40769c | |||
| be31b12d0a | |||
| 03281643ad | |||
| 6117cc6c10 | |||
| 479145af6f | |||
| b32b756da1 | |||
| 1c6bb1769c | |||
| 6933900635 | |||
| 763d6f57f2 | |||
| 2540d2a454 | |||
| 76795365c0 | |||
| 5c7abf4c47 | |||
| a3e7f6c527 | |||
| 73bb6a18ca | |||
| 15dc1f9e50 | |||
| 825aa6cbf5 | |||
| 9f301136f0 | |||
| 4daa43ad04 | |||
| 5ab4d99dd3 | |||
| 333508632c | |||
| 38592887c5 | |||
| 49e0c9651e | |||
| ac2cf1b784 | |||
| 584bdfce4f | |||
| 48a6ca9231 | |||
| 37290a2ed7 | |||
| 3ba593a15c | |||
| c1410554f5 | |||
| dc2ec093d2 | |||
| 1aab667a8e | |||
| 2659ba1ff5 | |||
| 2965c6be4b | |||
| 7790a6fe50 | |||
| 12da8c2d8c | |||
| 698b4477a2 | |||
| 8291d3e3aa | |||
| ecedc1872b | |||
| b552e0b8bc | |||
| f4145ca2fc | |||
| e114dcb9eb | |||
| 257def9140 | |||
| d8e47d1eb0 | |||
| df86b707a6 | |||
| c3fafa4331 | |||
| 0b123bec0f | |||
| 452ffa4f9d | |||
| aaa67135a8 | |||
| 7acc23892b | |||
| 6bad0e75a6 | |||
| 7ad25dc734 | |||
| 5e42012907 | |||
| c09a27583b | |||
| 1fed967100 | |||
| 87f389fc2d | |||
| 057dfd9c05 | |||
| f712e269c5 | |||
| 350d045eb0 | |||
| 9b8284a2cf | |||
| ac39be3848 | |||
| 0b044bac78 | |||
| c05ba11044 | |||
| 79c9ab623c | |||
| 8c11d3424e | |||
| c822b30ca0 | |||
| ed0892b1dc | |||
| 06d625013b | |||
| 4f6f851e2b | |||
| 798a178f42 | |||
| 1e3d2986c5 | |||
| 4d69e8e70a | |||
| 46f2827338 | |||
| b2c2619dc4 | |||
| 349310ccf9 | |||
| 1bb9b24bcf | |||
| 2dc317b84f | |||
| 7b1d4f24ab | |||
| 389e3a6a73 | |||
| 6f5baaaa69 | |||
| 6533451d06 | |||
| 765589afdf | |||
| 939f043b01 | |||
| ed63692c0c | |||
| 13da4a3e04 | |||
| 7fceccc109 | |||
| 00925aa389 | |||
| 76114f6cf2 | |||
| d1f72db6ac | |||
| ea3ebdddf5 | |||
| c5a70b9d11 | |||
| 634b51b051 | |||
| 17c080faa2 | |||
| 9ebb649458 | |||
| c30af64604 | |||
| c254d27e84 | |||
| b95ffdb417 | |||
| 14e4c2cc6e | |||
| f07467fd3e | |||
| 4eb6bb77e2 | |||
| 8c4f304d18 | |||
| d3e0e09718 | |||
| 95d93ec222 | |||
| 4d79cbc35b | |||
| 530b6f500a | |||
| 8b2da09418 | |||
| 64c5d3b370 | |||
| c3961f9a28 | |||
| 65cdf950c9 | |||
| ecf8558e92 | |||
| 81cd34817b | |||
| a0cda5f498 | |||
| 4b2e4e9055 | |||
| a0d77ca744 | |||
| 9da7004c6b | |||
| 0a47471b49 | |||
| 4559f20170 | |||
| 077ab22438 | |||
| da46ca4c16 | |||
| a68f19b868 | |||
| 6a05de2b30 | |||
| 1feb529c9e |
18
.gitignore
vendored
18
.gitignore
vendored
@ -1,10 +1,8 @@
|
||||
/configuration/target/
|
||||
/network/target/
|
||||
/util/target/
|
||||
/sharepoint/api/target/
|
||||
/security/target/
|
||||
/jeeutil/target/
|
||||
/account/target/
|
||||
/shiro-faces/target/
|
||||
/pdf/target/
|
||||
/shared-poi-util/target/
|
||||
/**/.settings/
|
||||
**/target/
|
||||
.classpath
|
||||
.project
|
||||
**/nbproject/
|
||||
*.dump
|
||||
**/_dump
|
||||
**/nb-configuration.xml
|
||||
|
||||
5
_bin/createRelease.bat
Normal file
5
_bin/createRelease.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
setlocal
|
||||
set BASEPATH=%~dp0%
|
||||
cd %BASEPATH%\..
|
||||
mvn release:clean release:prepare -Dmaven.test.skip=true -Darguments="-Dmaven.test.skip=true -DskipTests"
|
||||
55
account-dao/pom.xml
Normal file
55
account-dao/pom.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<?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</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>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>5.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.8</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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 com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public class ApiKeyObject implements Serializable {
|
||||
|
||||
@Expose
|
||||
private String userName;
|
||||
@Expose
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, locale = "en_US", timezone = "UTC", pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
private Date issuedOn;
|
||||
@Expose
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, locale = "en_US", timezone = "UTC", pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
private Date expiresOn;
|
||||
@Expose
|
||||
private String authToken;
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public Date getIssuedOn() {
|
||||
return issuedOn;
|
||||
}
|
||||
|
||||
public void setIssuedOn(Date issuedOn) {
|
||||
this.issuedOn = issuedOn;
|
||||
}
|
||||
|
||||
public Date getExpiresOn() {
|
||||
return expiresOn;
|
||||
}
|
||||
|
||||
public void setExpiresOn(Date expiresOn) {
|
||||
this.expiresOn = expiresOn;
|
||||
}
|
||||
|
||||
public String getAuthToken() {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public void setAuthToken(String authToken) {
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 53 * hash + Objects.hashCode(this.userName);
|
||||
hash = 53 * hash + Objects.hashCode(this.issuedOn);
|
||||
hash = 53 * hash + Objects.hashCode(this.expiresOn);
|
||||
hash = 53 * hash + Objects.hashCode(this.authToken);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final ApiKeyObject other = (ApiKeyObject) obj;
|
||||
if (!Objects.equals(this.userName, other.userName)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.authToken, other.authToken)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package de.muehlencord.shared.account.dao;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
public abstract class JacksonConfig {
|
||||
|
||||
private static ObjectMapper objectMapper = null;
|
||||
|
||||
private JacksonConfig() {
|
||||
// hide public constructor for static only class
|
||||
}
|
||||
|
||||
public static ObjectMapper getInstance() {
|
||||
if (objectMapper == null) {
|
||||
objectMapper = new ObjectMapper();
|
||||
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"));
|
||||
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
|
||||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||
}
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package de.muehlencord.shared.account.dao;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public class ApiKeyObjectTest {
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testJackson() throws JsonProcessingException, IOException {
|
||||
ApiKeyObject apiKeyObject = new ApiKeyObject();
|
||||
apiKeyObject.setUserName("web");
|
||||
apiKeyObject.setIssuedOn(new Date());
|
||||
apiKeyObject.setExpiresOn(new Date());
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String json = mapper.writeValueAsString(apiKeyObject);
|
||||
|
||||
System.out.println(json);
|
||||
|
||||
|
||||
ApiKeyObject apiKeyObject2 = mapper.readValue (json, ApiKeyObject.class);
|
||||
assertTrue (apiKeyObject.equals(apiKeyObject2));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
account-ui/faces-config.NavData
Normal file
6
account-ui/faces-config.NavData
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scene Scope="Project" version="2">
|
||||
<Scope Scope="Faces Configuration Only"/>
|
||||
<Scope Scope="Project"/>
|
||||
<Scope Scope="All Faces Configurations"/>
|
||||
</Scene>
|
||||
@ -6,6 +6,7 @@ The configuration is intended to be shared among all the users of project and
|
||||
therefore it is assumed to be part of version control checkout.
|
||||
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
|
||||
-->
|
||||
<libraries xmlns="http://www.netbeans.org/ns/cdnjs-libraries/1"/>
|
||||
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||
<!--
|
||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
||||
@ -13,6 +14,9 @@ You can copy and paste the single properties, into the pom.xml file and the IDE
|
||||
That way multiple projects can share the same settings (useful for formatting rules for example).
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<netbeans.checkstyle.format>true</netbeans.checkstyle.format>
|
||||
<org-netbeans-modules-projectapi.jsf_2e_language>Facelets</org-netbeans-modules-projectapi.jsf_2e_language>
|
||||
<org-netbeans-modules-maven-j2ee.netbeans_2e_hint_2e_deploy_2e_server>WildFly</org-netbeans-modules-maven-j2ee.netbeans_2e_hint_2e_deploy_2e_server>
|
||||
<netbeans.hint.license>apache20</netbeans.hint.license>
|
||||
<org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder>js/libs</org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
||||
150
account-ui/pom.xml
Normal file
150
account-ui/pom.xml
Normal file
@ -0,0 +1,150 @@
|
||||
<?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>
|
||||
<artifactId>shared</artifactId>
|
||||
<groupId>de.muehlencord</groupId>
|
||||
<version>1.1</version>
|
||||
</parent>
|
||||
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-account-ui</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>shared-account-ui</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<!-- default filter if not selected via profile -->
|
||||
<applicationUuid>143a2bd3-7e0b-4162-a76e-3031331c7dfe</applicationUuid>
|
||||
<filter.name>development</filter.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.primefaces</groupId>
|
||||
<artifactId>primefaces</artifactId>
|
||||
</dependency>
|
||||
<!-- Admin faces template -->
|
||||
<dependency>
|
||||
<groupId>com.github.adminfaces</groupId>
|
||||
<artifactId>admin-template</artifactId>
|
||||
</dependency>
|
||||
<!-- Omnifaces, faces utils -->
|
||||
<dependency>
|
||||
<groupId>org.omnifaces</groupId>
|
||||
<artifactId>omnifaces</artifactId>
|
||||
</dependency>
|
||||
<!-- Apache Shiro, Security API -->
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-shiro-faces</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-account</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-jeeutil</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.sf</groupId>
|
||||
<artifactId>filter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax</groupId>
|
||||
<artifactId>javaee-web-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<filters>
|
||||
<filter>${basedir}/src/main/filters/${filter.name}.properties</filter>
|
||||
</filters>
|
||||
|
||||
<resources>
|
||||
<!-- fill buildinformation file -->
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>**/*.properties</include>
|
||||
<include>**/*.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<finalName>account</finalName>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<webResources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/webapp</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>WEB-INF/web.xml</include>
|
||||
<include>WEB-INF/shiro.ini</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</webResources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>development</id>
|
||||
<properties>
|
||||
<filter.name>development</filter.name>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>production</id>
|
||||
<properties>
|
||||
<filter.name>production</filter.name>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
7
account-ui/src/main/filters/development.properties
Normal file
7
account-ui/src/main/filters/development.properties
Normal file
@ -0,0 +1,7 @@
|
||||
jsf.projectStage=Development
|
||||
|
||||
shiro.contextFactory = # not defined
|
||||
shiro.passwordMatcher= passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher${line.separator}passwordMatcher.passwordService = $passwordService
|
||||
shiro.ldapRealm = # not defined
|
||||
shiro.authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
|
||||
shiro.realms = $jdbcRealm
|
||||
17
account-ui/src/main/filters/production.properties
Normal file
17
account-ui/src/main/filters/production.properties
Normal file
@ -0,0 +1,17 @@
|
||||
jsf.projectStage=Production
|
||||
|
||||
ldap.url = ldaps://host:port
|
||||
## we will use provided username / password from webapplication
|
||||
ldap.user = user
|
||||
ldap.password = secret
|
||||
ldap.suffix = @diebold.com
|
||||
ldap.fallbackSuffix = @dieboldnixdorf.com
|
||||
ldap.searchBase = dc=ad,dc=diebold,dc=com
|
||||
ldap.searchFilter = (&(objectClass=*)(mail={0}))
|
||||
|
||||
## NO CHANGES BEHIND THIS LINE REQUIRED
|
||||
shiro.contextFactory = contextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory${line.separator}contextFactory.url = ${ldap.url}${line.separator}contextFactory.systemUsername = ${ldap.user}${line.separator}contextFactory.systemPassword = ${ldap.password}${line.separator}contextFactory.environment[java.naming.security.protocol] = ssl
|
||||
shiro.passwordMatcher= passwordMatcher=org.apache.shiro.authc.credential.AllowAllCredentialsMatcher
|
||||
shiro.ldapRealm = ldapRealm = de.muehlencord.shared.account.shiro.realm.UserNameActiveDirectoryRealm${line.separator}ldapRealm.principalSuffix = ${ldap.suffix}${line.separator}ldapRealm.fallbackPrincipalSuffix = ${ldap.fallbackSuffix}${line.separator}ldapRealm.ldapContextFactory = $contextFactory${line.separator}ldapRealm.searchBase = ${ldap.searchBase}${line.separator}ldapRealm.searchFilter = ${ldap.searchFilter}${line.separator}ldapRealm.permissionsLookupEnabled=false
|
||||
shiro.authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
|
||||
shiro.realms=$jdbcRealm,$ldapRealm
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2018 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.web;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.boundary.AccountPermissions;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountException;
|
||||
import de.muehlencord.shared.account.business.instance.boundary.ApplicationPermissions;
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl;
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import java.util.Arrays;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.context.Initialized;
|
||||
import javax.enterprise.event.Observes;
|
||||
import javax.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class EnsurePermissionsBean {
|
||||
|
||||
@Inject
|
||||
ApplicationEntity application;
|
||||
|
||||
@Inject
|
||||
ApplicationPermissionControl applicationPermissionControl;
|
||||
|
||||
@Inject
|
||||
ApplicationRoleControl applicationRoleControl;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EnsurePermissionsBean.class);
|
||||
|
||||
public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Ensure all permissions for {} are available", application.getApplicationName());
|
||||
}
|
||||
applicationPermissionControl.setupPermissions(Arrays.asList(ApplicationPermissions.values()));
|
||||
applicationPermissionControl.setupPermissions(Arrays.asList(AccountPermissions.values()));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("All permissions added to application", application.getApplicationName());
|
||||
}
|
||||
|
||||
// all permissions available - ensure permission is assigned to Admin role
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Ensuring Admin role for {} has all permissions", application.getApplicationName());
|
||||
}
|
||||
try {
|
||||
applicationRoleControl.setupRolePermission(Arrays.asList(ApplicationPermissions.values()), "Admin"); // NOI18N
|
||||
applicationRoleControl.setupRolePermission(Arrays.asList(AccountPermissions.values()), "Admin"); // NOI18N
|
||||
} catch (AccountException ex) {
|
||||
LOGGER.error("Error adding permission to Admin role. Reason={}", ex.getMessage());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("All permissions added to Admin role of {}", application.getApplicationName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2017 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.web;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.context.ContextNotActiveException;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class FacesContextProducer {
|
||||
|
||||
@Produces
|
||||
@RequestScoped
|
||||
public FacesContext getFacesContext() {
|
||||
FacesContext ctx = FacesContext.getCurrentInstance();
|
||||
if (ctx == null) {
|
||||
throw new ContextNotActiveException("FacesContext is not active");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2018 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.web;
|
||||
|
||||
import de.muehlencord.shared.account.business.instance.boundary.ApplicationPermissions;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* TODO replace with omnifaces:importConstants currently problems with Netbeans
|
||||
* to import omnifaces taglib
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Named(value = "permissionConstants")
|
||||
@ApplicationScoped
|
||||
public class PermissionConstants {
|
||||
|
||||
public String getApplicationListAll() {
|
||||
return ApplicationPermissions.APP_LIST.getName();
|
||||
}
|
||||
|
||||
public String getPermissionsCombined() {
|
||||
return ApplicationPermissions.PERMISSION_ADD.getName() + ","
|
||||
+ ApplicationPermissions.PERMISSION_EDIT.getName() + ","
|
||||
+ ApplicationPermissions.PERMISSION_DELETE.getName();
|
||||
}
|
||||
|
||||
public String getRolesCombined() {
|
||||
return ApplicationPermissions.ROLE_ADD.getName() + ","
|
||||
+ ApplicationPermissions.ROLE_EDIT.getName() + ","
|
||||
+ ApplicationPermissions.ROLE_DELETE.getName();
|
||||
}
|
||||
|
||||
public String getAccountsCombined() {
|
||||
return ApplicationPermissions.ACCOUNT_ADD.getName() + ","
|
||||
+ ApplicationPermissions.ACCOUNT_DELETE.getName() + ","
|
||||
+ ApplicationPermissions.ACCOUNT_EDIT.getName() + ","
|
||||
+ ApplicationPermissions.ACCOUNT_LIST.getName() + ","
|
||||
+ ApplicationPermissions.ACCOUNT_LOGIN_ADD.getName() + ","
|
||||
+ ApplicationPermissions.ACCOUNT_LOGIN_DELETE.getName() + ","
|
||||
+ ApplicationPermissions.ACCOUNT_LOGIN_EDIT.getName();
|
||||
}
|
||||
|
||||
public String getAccountAdd() {
|
||||
return ApplicationPermissions.ACCOUNT_ADD.getName();
|
||||
}
|
||||
|
||||
public String getAccountDelete() {
|
||||
return ApplicationPermissions.ACCOUNT_DELETE.getName();
|
||||
}
|
||||
|
||||
public String getAccountEdit() {
|
||||
return ApplicationPermissions.ACCOUNT_EDIT.getName();
|
||||
}
|
||||
|
||||
public String getAccountList() {
|
||||
return ApplicationPermissions.ACCOUNT_LIST.getName();
|
||||
}
|
||||
|
||||
public String getAccountLoginAdd() {
|
||||
return ApplicationPermissions.ACCOUNT_LOGIN_ADD.getName();
|
||||
}
|
||||
|
||||
public String getAccountLoginDelete() {
|
||||
return ApplicationPermissions.ACCOUNT_LOGIN_DELETE.getName();
|
||||
}
|
||||
|
||||
public String getAccountLoginEdit() {
|
||||
return ApplicationPermissions.ACCOUNT_LOGIN_EDIT.getName();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package de.muehlencord.shared.account.web;
|
||||
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import de.muehlencord.shared.account.util.ApplicationPU;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Disposes;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceUnit;
|
||||
import javax.persistence.SynchronizationType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PersistenceContextFactory {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PersistenceContextFactory.class);
|
||||
|
||||
// need to define 2nd database as TransactionJoinInterceptor requires it
|
||||
// account UI is the only application where application and account is the same database
|
||||
// account UI does not call this as it references all database access via accountPu
|
||||
@PersistenceUnit (unitName = "accountPu")
|
||||
EntityManagerFactory entityManagerFactory;
|
||||
|
||||
|
||||
@Produces
|
||||
@RequestScoped
|
||||
@ApplicationPU
|
||||
public EntityManager getPcdEntityManager() {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("getting entityManager for application");
|
||||
}
|
||||
EntityManager em = entityManagerFactory.createEntityManager(SynchronizationType.UNSYNCHRONIZED);
|
||||
return em;
|
||||
}
|
||||
|
||||
public void closePcdEntityManager (@Disposes @ApplicationPU EntityManager em) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("closing entityManager application database");
|
||||
}
|
||||
em.close();
|
||||
}
|
||||
|
||||
|
||||
@Produces
|
||||
@RequestScoped
|
||||
@AccountPU
|
||||
public EntityManager getAccountEntityManager() {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("getting entityManager for account database");
|
||||
}
|
||||
EntityManager em = entityManagerFactory.createEntityManager(SynchronizationType.UNSYNCHRONIZED);
|
||||
return em;
|
||||
}
|
||||
|
||||
public void closeAccountEntityManager (@Disposes @AccountPU EntityManager em) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("closing entityManager for account database");
|
||||
}
|
||||
em.close();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2017 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.web;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@RequestScoped
|
||||
public class ResourceBundleProducer implements Serializable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceBundleProducer.class);
|
||||
|
||||
private static final long serialVersionUID = 3764096270387408239L;
|
||||
|
||||
@Inject
|
||||
private Locale locale;
|
||||
|
||||
@Inject
|
||||
private FacesContext facesContext;
|
||||
|
||||
@Produces
|
||||
public ResourceBundle getResourceBundle() {
|
||||
ResourceBundle rb = ResourceBundle.getBundle("de.muehlencord.shared.account.web.presentation.messages", facesContext.getViewRoot().getLocale());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("ResourceBundle = "+rb);
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
package de.muehlencord.shared.account.web.presentation;
|
||||
|
||||
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.AccountException;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountStatus;
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.jeeutil.FacesUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.ejb.EJB;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.view.ViewScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.primefaces.event.SelectEvent;
|
||||
import org.primefaces.event.UnselectEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jomu
|
||||
*/
|
||||
@ViewScoped
|
||||
@Named("accountView")
|
||||
public class AccountView implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8050582392249849438L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountView.class);
|
||||
|
||||
@Inject
|
||||
private ApplicationView applicationView;
|
||||
@EJB
|
||||
private AccountControl accountService;
|
||||
@EJB
|
||||
private ApplicationRoleControl appliationRoleService;
|
||||
|
||||
/**
|
||||
* boolean flag to determine wether disabled accounts should be shown
|
||||
* accounts are not deleted but disabled and can be activated in case
|
||||
*/
|
||||
private boolean showDisabledAccounts = false;
|
||||
|
||||
// cached accounts
|
||||
private List<AccountEntity> accountList = null;
|
||||
// cached application roles
|
||||
private List<ApplicationRoleEntity> applicationRoles = null;
|
||||
|
||||
// account currently on edit
|
||||
private AccountEntity currentAccount;
|
||||
private List<ApplicationRoleEntity> currentAccountRoles = null;
|
||||
private AccountLoginEntity currentAccountLogin;
|
||||
private String password = null;
|
||||
private String repeatPassword = null;
|
||||
|
||||
public List<AccountEntity> getAccounts() {
|
||||
if (accountList == null) {
|
||||
accountList = accountService.getAllAccounts(showDisabledAccounts);
|
||||
}
|
||||
return accountList;
|
||||
}
|
||||
|
||||
public List<ApplicationRoleEntity> getAllApplicationRoles() {
|
||||
if (applicationRoles == null) {
|
||||
ApplicationEntity application = applicationView.getCurrentApplication();
|
||||
applicationRoles = appliationRoleService.getAllRoles(application);
|
||||
}
|
||||
return applicationRoles;
|
||||
}
|
||||
|
||||
public void selectAccount(SelectEvent event) {
|
||||
// nothing to do, currentAccountRoles are loaded before dialog is shown
|
||||
}
|
||||
|
||||
public void unselectAccount(UnselectEvent event) {
|
||||
applicationRoles = null;
|
||||
currentAccountRoles = null;
|
||||
}
|
||||
|
||||
public boolean getAccountSelected() {
|
||||
return currentAccount != null;
|
||||
}
|
||||
|
||||
public void newAccount() {
|
||||
currentAccount = new AccountEntity();
|
||||
currentAccount.setStatus("NEW"); // TODO add status enum
|
||||
currentAccountRoles = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void editAccount() {
|
||||
// function called by webpage
|
||||
if (currentAccount == null) {
|
||||
currentAccountRoles = null;
|
||||
} else {
|
||||
currentAccount = accountService.getAccountEntity(currentAccount.getUsername(), true);
|
||||
this.currentAccountRoles = new ArrayList<>();
|
||||
if (currentAccount.getApplicationRoleList() != null) {
|
||||
currentAccountRoles.addAll(currentAccount.getApplicationRoleList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelEditAccount() {
|
||||
currentAccount = null;
|
||||
currentAccountRoles = null;
|
||||
}
|
||||
|
||||
public void saveEditAccount() {
|
||||
String username = currentAccount.getUsername();
|
||||
AccountEntity existingEntity = accountService.getAccountEntity(username, true);
|
||||
// check if it is a new user (createdBy == null) but a user with same name already exists
|
||||
if ((currentAccount.getCreatedBy() == null) && (existingEntity != null)) {
|
||||
FacesUtil.addErrorMessage("editDialogMessaegs", "Create new account failed", "Account with username " + username + " already exists");
|
||||
} else {
|
||||
accountService.saveAccount(currentAccount, applicationView.getCurrentApplication(), currentAccountRoles);
|
||||
// force accounts to be loaded from database again
|
||||
accountList = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAccount() {
|
||||
try {
|
||||
accountService.deleteAccount(currentAccount);
|
||||
accountList.remove(currentAccount);
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Account " + currentAccount.getUsername() + " deleted");
|
||||
currentAccount = null;
|
||||
currentAccountRoles = null;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
FacesUtil.addGlobalErrorMessage("Error deleting account", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void showDisabledAccountsChange() {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("show diabled accounts changed to {}", showDisabledAccounts);
|
||||
}
|
||||
this.accountList = null;
|
||||
}
|
||||
|
||||
public List<String> getStatusList() {
|
||||
return AccountStatus.getAllStatusNames();
|
||||
}
|
||||
|
||||
/* **** account login methods **** */
|
||||
public boolean validatePasswords(FacesContext context, List<UIInput> components, List<Object> values) {
|
||||
String currentPassword = components.get(0).getSubmittedValue().toString();
|
||||
String currentPasswordRepeat = components.get(1).getSubmittedValue().toString();
|
||||
|
||||
if ((currentPassword == null) || (currentPasswordRepeat == null)) {
|
||||
return false;
|
||||
}
|
||||
boolean returnValue = currentPassword.equals(currentPasswordRepeat);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public void addAccountLogin() {
|
||||
if (currentAccount == null) {
|
||||
// TODO add error handling
|
||||
} else {
|
||||
this.currentAccountLogin = accountService.createLoginWithRandomPassword();
|
||||
}
|
||||
}
|
||||
|
||||
public void editAccountLogin() {
|
||||
if (currentAccount == null) {
|
||||
// TODO add error handling
|
||||
} else {
|
||||
this.currentAccountLogin = currentAccount.getAccountLogin();
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAccountLogin() {
|
||||
if (currentAccount == null) {
|
||||
// TODO add error handling
|
||||
} else {
|
||||
accountService.deleteLogin(currentAccount);
|
||||
currentAccount.setAccountLogin(null);
|
||||
currentAccountLogin = null;
|
||||
accountList = null; // force reload
|
||||
FacesUtil.addGlobalInfoMessage("Account saved", "Login removed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void saveEditAccountLogin() {
|
||||
// TODO move to account control - to much logic for the view
|
||||
if ((currentAccountLogin == null) || (currentAccount == null)) {
|
||||
// TODO add error handling
|
||||
} else {
|
||||
|
||||
// overwrite password if provided
|
||||
if ((password != null) && (!password.trim().equals(""))) {
|
||||
// password has been specified
|
||||
if (password.equals(repeatPassword)) {
|
||||
currentAccountLogin.setAccountPassword(accountService.getHashedPassword(password));
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Password updated");
|
||||
} else {
|
||||
// TODO connect to IPRS
|
||||
// frontend does validate passwords do match
|
||||
// someone is trying to cheat
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAccountLogin.getId() == null) {
|
||||
accountService.addLogin(currentAccount, currentAccountLogin);
|
||||
currentAccount.setAccountLogin(currentAccountLogin);
|
||||
accountList = null; // force reload of accounts
|
||||
} else {
|
||||
accountService.updateLogin(currentAccountLogin);
|
||||
}
|
||||
currentAccountLogin = null;
|
||||
FacesUtil.addGlobalInfoMessage("Account saved", "Login data updated");
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelEditAccountLogin() {
|
||||
this.currentAccountLogin = null;
|
||||
}
|
||||
|
||||
public boolean getCurrentLoggedInUser() {
|
||||
if (currentAccount == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
if (currentUser == null) {
|
||||
// TODO - connect to IPRS - how can this method be called if no user is logged in
|
||||
return false;
|
||||
}
|
||||
String currentUserName = currentUser.getPrincipal().toString();
|
||||
return currentUserName.equals(currentAccount.getUsername());
|
||||
}
|
||||
|
||||
/* **** getter / setter **** */
|
||||
/**
|
||||
* setter for managed property applicationView
|
||||
*
|
||||
* @param applicationView the applicaton view to inject
|
||||
*/
|
||||
public void setApplicationView(ApplicationView applicationView) {
|
||||
this.applicationView = applicationView;
|
||||
}
|
||||
|
||||
public AccountEntity getCurrentAccount() {
|
||||
return currentAccount;
|
||||
}
|
||||
|
||||
public void setCurrentAccount(AccountEntity currentAccount) {
|
||||
this.currentAccount = currentAccount;
|
||||
}
|
||||
|
||||
public boolean isShowDisabledAccounts() {
|
||||
return showDisabledAccounts;
|
||||
}
|
||||
|
||||
public void setShowDisabledAccounts(boolean showDisabledAccounts) {
|
||||
this.showDisabledAccounts = showDisabledAccounts;
|
||||
}
|
||||
|
||||
public List<ApplicationRoleEntity> getCurrentAccountRoles() {
|
||||
return currentAccountRoles;
|
||||
}
|
||||
|
||||
public void setCurrentAccountRoles(List<ApplicationRoleEntity> currentAccountRoles) {
|
||||
this.currentAccountRoles = currentAccountRoles;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getRepeatPassword() {
|
||||
return repeatPassword;
|
||||
}
|
||||
|
||||
public void setRepeatPassword(String repeatPassword) {
|
||||
this.repeatPassword = repeatPassword;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
package de.muehlencord.shared.account.web.presentation;
|
||||
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.util.AccountSecurityException;
|
||||
import de.muehlencord.shared.jeeutil.FacesUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.enterprise.context.SessionScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@SessionScoped
|
||||
@Named("applicationView")
|
||||
public class ApplicationView implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5515249316880163539L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationView.class);
|
||||
|
||||
@Inject
|
||||
ApplicationControl applicationService;
|
||||
|
||||
@Inject
|
||||
InstanceView instanceView;
|
||||
|
||||
@Inject
|
||||
Locale locale;
|
||||
|
||||
private ApplicationEntity currentApplication = null;
|
||||
private ApplicationEntity editApplication = null;
|
||||
private List<ApplicationEntity> applicationList = null;
|
||||
|
||||
@PostConstruct
|
||||
public void selectDefaultCurrentApplication() {
|
||||
// force applications to be loaded from database
|
||||
getAllApplications();
|
||||
if ((applicationList != null) && (!applicationList.isEmpty())) {
|
||||
currentApplication = applicationList.get(0);
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("post construct executed");
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void predestroy() {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Predestroy executed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<ApplicationEntity> getAllApplications() {
|
||||
if (applicationList == null) {
|
||||
try {
|
||||
applicationList = applicationService.getAllApplications();
|
||||
|
||||
// if no role is assigned to user, ensure that at least current application is added
|
||||
if ((applicationList == null) || (applicationList.isEmpty())) {
|
||||
applicationList = new ArrayList<>();
|
||||
applicationList.add(instanceView.getInstanceApplication());
|
||||
}
|
||||
|
||||
return applicationList;
|
||||
} catch (AccountSecurityException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
FacesUtil.addGlobalErrorMessage("Error " + ex.getErrorCode(), ex.getLocalizedMessage(locale));
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
return applicationList;
|
||||
}
|
||||
|
||||
public void selectApplication() {
|
||||
if (currentApplication != null) {
|
||||
LOGGER.info("selected application: {}", currentApplication.getApplicationName());
|
||||
FacesUtil.addGlobalInfoMessage("Success", "Selected application " + currentApplication.getApplicationName());
|
||||
}
|
||||
}
|
||||
|
||||
public void newApplication() {
|
||||
this.editApplication = new ApplicationEntity();
|
||||
}
|
||||
|
||||
public void cancelEditApplication() {
|
||||
this.editApplication = null;
|
||||
}
|
||||
|
||||
public void saveEditApplication() {
|
||||
if (editApplication == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Need to provide data");
|
||||
} else if ((editApplication.getApplicationName() == null) || (editApplication.getApplicationName().trim().equals(""))) {
|
||||
String hint;
|
||||
if (editApplication.getId() == null) {
|
||||
hint = "Cannot create application";
|
||||
} else {
|
||||
hint = "Cannot save application";
|
||||
}
|
||||
FacesUtil.addGlobalErrorMessage(hint, "Application name must not be empty");
|
||||
} else {
|
||||
currentApplication = applicationService.createOrUpdate(editApplication);
|
||||
// force reload of to update view
|
||||
applicationList = null;
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Application saved");
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteApplication() {
|
||||
if (currentApplication == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Need to provide data");
|
||||
} else if (currentApplication.getId() == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Cannot delete non persistent data");
|
||||
} else {
|
||||
String applicationName = currentApplication.getApplicationName();
|
||||
applicationService.delete(currentApplication);
|
||||
applicationList = null; // force reload to update view
|
||||
currentApplication = null;
|
||||
selectDefaultCurrentApplication();
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Application " + applicationName + " deleted");
|
||||
}
|
||||
}
|
||||
|
||||
/* *** getter / setter *** */
|
||||
public ApplicationEntity getCurrentApplication() {
|
||||
return currentApplication;
|
||||
}
|
||||
|
||||
public void setCurrentApplication(ApplicationEntity currentApplication) {
|
||||
this.currentApplication = currentApplication;
|
||||
}
|
||||
|
||||
public ApplicationEntity getEditApplication() {
|
||||
return editApplication;
|
||||
}
|
||||
|
||||
public void setEditApplication(ApplicationEntity editApplication) {
|
||||
this.editApplication = editApplication;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package de.muehlencord.shared.account.web.presentation;
|
||||
|
||||
import de.muehlencord.shared.account.business.instance.control.ApplicationController;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.config.boundary.ConfigService;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigException;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* TODO - move to shared-account and remove from all applications and archetype
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Named(value = "instanceView")
|
||||
@ApplicationScoped
|
||||
public class InstanceView {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(InstanceView.class);
|
||||
|
||||
@Inject
|
||||
ConfigService configService;
|
||||
|
||||
@Inject
|
||||
ApplicationController applicationController;
|
||||
|
||||
public boolean isDevelopmentVersion() {
|
||||
String instanceName = getInstanceName();
|
||||
return !instanceName.equals("Production");
|
||||
}
|
||||
|
||||
public String getInstanceName() {
|
||||
String instanceName;
|
||||
try {
|
||||
instanceName = configService.getConfigValue("base.instance");
|
||||
} catch (ConfigException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
instanceName = "unknown (" + ex.toString() + ")";
|
||||
}
|
||||
if (instanceName == null) {
|
||||
return "unknown";
|
||||
} else {
|
||||
return instanceName;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationEntity getInstanceApplication() {
|
||||
return applicationController.getApplication();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2017 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.web.presentation;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountException;
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
|
||||
import de.muehlencord.shared.jeeutil.FacesUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import javax.ejb.EJB;
|
||||
import javax.faces.view.ViewScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ViewScoped
|
||||
@Named("permissionView")
|
||||
public class PermissionView implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1469453490360990772L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(PermissionView.class);
|
||||
|
||||
@Inject
|
||||
private ApplicationView applicationView;
|
||||
|
||||
@EJB
|
||||
ApplicationPermissionControl applicationPermissionService;
|
||||
private ApplicationPermissionEntity currentPermission;
|
||||
|
||||
public List<ApplicationPermissionEntity> getAppPermissions() {
|
||||
return applicationPermissionService.getApplicationPermissions(applicationView.getCurrentApplication());
|
||||
}
|
||||
|
||||
public void saveEditPermission() throws AccountException {
|
||||
if (currentPermission != null) {
|
||||
String newPermissionName = currentPermission.getPermissionName();
|
||||
String newPermissionDescription = currentPermission.getPermissionDescription();
|
||||
if ((newPermissionName == null) || (newPermissionName.trim().length() == 0)) {
|
||||
FacesUtil.addErrorMessage("editDialogMessages", "Error", "Permission name must not be null");
|
||||
} else if ((newPermissionDescription == null) || (newPermissionDescription.trim().length() == 0)) {
|
||||
FacesUtil.addErrorMessage("editDialogMessages", "Error", "Permission name must not be null");
|
||||
} else {
|
||||
if (currentPermission.getId() == null) {
|
||||
applicationPermissionService.create(applicationView.getCurrentApplication(), newPermissionName, newPermissionDescription);
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Permission " + newPermissionName + " created");
|
||||
} else {
|
||||
applicationPermissionService.update(currentPermission);
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Permission " + newPermissionName + " updated");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationEntity getCurrentApplication() {
|
||||
if (applicationView.getCurrentApplication() == null) {
|
||||
return null;
|
||||
} else {
|
||||
return applicationView.getCurrentApplication();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelEditPermission() {
|
||||
this.currentPermission = null;
|
||||
}
|
||||
|
||||
public void newPermission() {
|
||||
this.currentPermission = new ApplicationPermissionEntity();
|
||||
}
|
||||
|
||||
public void editPermission() {
|
||||
if (currentPermission == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit");
|
||||
}
|
||||
}
|
||||
|
||||
public void deletePermission() {
|
||||
if (currentPermission == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit");
|
||||
} else {
|
||||
try {
|
||||
applicationPermissionService.delete(currentPermission);
|
||||
currentPermission = null;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
FacesUtil.addGlobalErrorMessage("Error while deleting permission.", ex.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getCanEdit() {
|
||||
return isPermissionSelected();
|
||||
}
|
||||
|
||||
public boolean getCanDelete() {
|
||||
return isPermissionSelected();
|
||||
}
|
||||
|
||||
/* *** getter / setter *** */
|
||||
/**
|
||||
* required setter for managedProperty
|
||||
*
|
||||
*
|
||||
* @param applicationView the injected applicationView
|
||||
*/
|
||||
public void setApplicationView(ApplicationView applicationView) {
|
||||
this.applicationView = applicationView;
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity getCurrentPermission() {
|
||||
return currentPermission;
|
||||
}
|
||||
|
||||
public void setCurrentPermission(ApplicationPermissionEntity newCurrentPermission) {
|
||||
this.currentPermission = newCurrentPermission;
|
||||
|
||||
}
|
||||
|
||||
public boolean isPermissionSelected() {
|
||||
return currentPermission != null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2017 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.web.presentation;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountException;
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.jeeutil.FacesUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.ejb.EJB;
|
||||
import javax.faces.view.ViewScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.primefaces.event.SelectEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ViewScoped
|
||||
@Named("roleView")
|
||||
public class RoleView implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1669321020398119007L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RoleView.class);
|
||||
|
||||
@Inject
|
||||
private ApplicationView applicationView;
|
||||
|
||||
@EJB
|
||||
ApplicationRoleControl applicationRoleControl;
|
||||
|
||||
private List<ApplicationRoleEntity> allRoles = null;
|
||||
private List<ApplicationPermissionEntity> currentRolePermissions = null;
|
||||
private List<ApplicationPermissionEntity> missingApplicationsPermissions = null;
|
||||
|
||||
private ApplicationRoleEntity currentRole;
|
||||
private ApplicationPermissionEntity currentPermission;
|
||||
private ApplicationPermissionEntity newPermission;
|
||||
|
||||
public ApplicationEntity getCurrentApplication() {
|
||||
return applicationView.getCurrentApplication();
|
||||
}
|
||||
|
||||
public List<ApplicationRoleEntity> getAllRoles() {
|
||||
if (allRoles == null) {
|
||||
allRoles = applicationRoleControl.getAllRoles(applicationView.getCurrentApplication());
|
||||
}
|
||||
return allRoles;
|
||||
}
|
||||
|
||||
public void startNewRole() {
|
||||
this.currentRole = new ApplicationRoleEntity(applicationView.getCurrentApplication());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Created new current role: {}", currentRole.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void cancelEditRole() {
|
||||
this.currentRole = null;
|
||||
}
|
||||
|
||||
public void saveEditRole() {
|
||||
if ((currentRole == null) || (currentRole.getRoleName() == null) || (currentRole.getRoleName().trim().length() == 0)) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Permission name must not be null");
|
||||
} else if (currentRole.getId() == null) {
|
||||
applicationRoleControl.create(currentRole);
|
||||
allRoles = null; // force reload
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Role " + currentRole.getRoleName() + " created");
|
||||
} else {
|
||||
applicationRoleControl.update(currentRole);
|
||||
allRoles = null; // force reload
|
||||
FacesUtil.addGlobalInfoMessage("Info", "Role " + currentRole.getRoleName() + " updated");
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRole() {
|
||||
if (currentRole == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Please select a permission to edit");
|
||||
} else {
|
||||
try {
|
||||
applicationRoleControl.delete(currentRole);
|
||||
allRoles = null; // force reload
|
||||
currentRole = null;
|
||||
currentRolePermissions = null;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
|
||||
FacesUtil.addGlobalErrorMessage("Error while deleting permission.", ex.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getRoleSelected() {
|
||||
return currentRole != null;
|
||||
}
|
||||
|
||||
public boolean getPermissionSelected() {
|
||||
return currentPermission != null;
|
||||
}
|
||||
|
||||
public boolean getMissingPermissionAvailable() {
|
||||
return ((missingApplicationsPermissions != null) && (!missingApplicationsPermissions.isEmpty()));
|
||||
}
|
||||
|
||||
public void onRoleSelect(SelectEvent event) {
|
||||
currentRolePermissions = null;
|
||||
currentRolePermissions = getRolePermissions();
|
||||
missingApplicationsPermissions = null;
|
||||
missingApplicationsPermissions = getMissingPermissions();
|
||||
}
|
||||
|
||||
public List<ApplicationPermissionEntity> getRolePermissions() {
|
||||
if (currentRole == null) {
|
||||
currentRolePermissions = new ArrayList<>();
|
||||
return currentRolePermissions;
|
||||
} else {
|
||||
if (currentRolePermissions == null) {
|
||||
try {
|
||||
currentRolePermissions = applicationRoleControl.getRolePermissions(currentRole);
|
||||
} catch (AccountException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
|
||||
FacesUtil.addGlobalErrorMessage("Error while fetching role permissions", "see log for details");
|
||||
currentRolePermissions = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
return currentRolePermissions;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ApplicationPermissionEntity> getMissingPermissions() {
|
||||
if (currentRole == null) {
|
||||
missingApplicationsPermissions = new ArrayList<>();
|
||||
return missingApplicationsPermissions;
|
||||
} else {
|
||||
if (missingApplicationsPermissions == null) {
|
||||
missingApplicationsPermissions = applicationRoleControl.getNotAssignedApplicationPermissions(currentRole);
|
||||
|
||||
}
|
||||
return missingApplicationsPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
public void addRolePermission() {
|
||||
if (newPermission == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Please select a new permission first");
|
||||
} else {
|
||||
try {
|
||||
applicationRoleControl.addPermission(currentRole, newPermission);
|
||||
currentRolePermissions = null;
|
||||
missingApplicationsPermissions = null;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
|
||||
FacesUtil.addGlobalErrorMessage("Error while adding permission", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void removeRolePermission() {
|
||||
if (currentPermission == null) {
|
||||
FacesUtil.addGlobalErrorMessage("Error", "Please select a permission first");
|
||||
} else {
|
||||
try {
|
||||
applicationRoleControl.removePermission(currentRole, currentPermission);
|
||||
currentRolePermissions = null;
|
||||
missingApplicationsPermissions = null;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Detailed stacktrace", new Object[]{ex});
|
||||
}
|
||||
FacesUtil.addGlobalErrorMessage("Error while adding permission", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* *** getter / setter *** */
|
||||
public void setApplicationView(ApplicationView applicationView) {
|
||||
this.applicationView = applicationView;
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity getCurrentRole() {
|
||||
return currentRole;
|
||||
}
|
||||
|
||||
public void setCurrentRole(ApplicationRoleEntity currentRole) {
|
||||
this.currentRole = currentRole;
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity getCurrentPermission() {
|
||||
return currentPermission;
|
||||
}
|
||||
|
||||
public void setCurrentPermission(ApplicationPermissionEntity currentPermission) {
|
||||
this.currentPermission = currentPermission;
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity getNewPermission() {
|
||||
return newPermission;
|
||||
}
|
||||
|
||||
public void setNewPermission(ApplicationPermissionEntity newPermission) {
|
||||
this.newPermission = newPermission;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package de.muehlencord.shared.account.web.presentation;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.faces.application.FacesMessage;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.validator.FacesValidator;
|
||||
import javax.faces.validator.Validator;
|
||||
import javax.faces.validator.ValidatorException;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationRoleControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@FacesValidator("uniqueApplicationRoleNameValidator")
|
||||
public class UniqueApplicationRoleNameValidator implements Validator, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8165013107453616719L;
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
@Inject
|
||||
ApplicationRoleControl applicationRoleControl;
|
||||
|
||||
@Override
|
||||
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
|
||||
|
||||
Object oldRoleNameObj = ((UIInput) component).getValue();
|
||||
String oldRoleName = "";
|
||||
if (oldRoleNameObj != null) {
|
||||
oldRoleName = oldRoleNameObj.toString();
|
||||
}
|
||||
|
||||
Object applicationObj = component.getAttributes().get("application");
|
||||
if ((applicationObj != null) && (applicationObj instanceof ApplicationEntity)) {
|
||||
ApplicationEntity application = (ApplicationEntity) applicationObj;
|
||||
if (value == null) {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role name must not be empty"));
|
||||
}
|
||||
if (value instanceof String) {
|
||||
String roleName = (String) value;
|
||||
ApplicationRoleEntity existingRole = applicationRoleControl.findByName(application, roleName);
|
||||
if (existingRole != null) {
|
||||
if (!oldRoleName.equals(roleName)) {
|
||||
// name of role changed and there is another role with the new name already --> this must not happen
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role already exists"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Role name invalid", "Role name must be a string value"));
|
||||
// TODO add IPRS logger - someone is trying to cheat
|
||||
}
|
||||
} else {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application not set", "Permission name cannot be set if application is unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package de.muehlencord.shared.account.web.presentation;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.faces.application.FacesMessage;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.validator.FacesValidator;
|
||||
import javax.faces.validator.Validator;
|
||||
import javax.faces.validator.ValidatorException;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@FacesValidator("uniqueApplicationValidator")
|
||||
public class UniqueApplicationValidator implements Validator, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2526409681909574670L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(UniqueApplicationValidator.class);
|
||||
|
||||
@Inject
|
||||
ApplicationControl applicationService;
|
||||
|
||||
@Override
|
||||
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
|
||||
|
||||
Object oldAppNameObj = ((UIInput) component).getValue();
|
||||
String oldAppName = "";
|
||||
if (oldAppNameObj != null) {
|
||||
oldAppName = oldAppNameObj.toString();
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application name invalid", "Application name must not be empty"));
|
||||
}
|
||||
if (value instanceof String) {
|
||||
String applicationname = (String) value;
|
||||
ApplicationEntity existingApplication = applicationService.findByApplicationName(applicationname);
|
||||
if (existingApplication != null) {
|
||||
if (!oldAppName.equals(applicationname)) {
|
||||
// name of application changed and there is another application with the new
|
||||
// name already --> this must not happen
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application name invalid", "Application already exists"));
|
||||
}
|
||||
}
|
||||
LOGGER.info("Name = {}", applicationname);
|
||||
} else {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application name invalid", "Application name must be a string value"));
|
||||
// TODO add IPRS logger - someone is trying to cheat
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package de.muehlencord.shared.account.web.presentation;
|
||||
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
|
||||
import de.muehlencord.shared.account.business.application.control.ApplicationPermissionControl;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import java.io.Serializable;
|
||||
import javax.faces.application.FacesMessage;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.validator.FacesValidator;
|
||||
import javax.faces.validator.Validator;
|
||||
import javax.faces.validator.ValidatorException;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@FacesValidator("uniquePermissionNameValidator")
|
||||
public class UniquePermissionNameValidator implements Validator, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2526409681909574670L;
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
@Inject
|
||||
ApplicationPermissionControl applicationPermissionControl;
|
||||
|
||||
@Override
|
||||
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
|
||||
|
||||
Object oldPermissionNameObj = ((UIInput) component).getValue();
|
||||
String oldPermissionName = "";
|
||||
if (oldPermissionNameObj != null) {
|
||||
oldPermissionName = oldPermissionNameObj.toString();
|
||||
}
|
||||
|
||||
Object applicationObj = component.getAttributes().get("application");
|
||||
if ((applicationObj != null) && (applicationObj instanceof ApplicationEntity)) {
|
||||
ApplicationEntity application = (ApplicationEntity) applicationObj;
|
||||
if (value == null) {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission name must not be empty"));
|
||||
}
|
||||
if (value instanceof String) {
|
||||
String permissionName = (String) value;
|
||||
ApplicationPermissionEntity existingPermission = applicationPermissionControl.findPermissionByName(application, permissionName);
|
||||
if (existingPermission != null) {
|
||||
if ((!oldPermissionName.equals (permissionName))) {
|
||||
// name of permission changed and there is another permission with the new
|
||||
// name already --> this must not happen
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission already exists"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Permission name invalid", "Permission name must be a string value"));
|
||||
// TODO add IPRS logger - someone is trying to cheat
|
||||
}
|
||||
} else {
|
||||
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Application not set", "Permission name cannot be set if application is unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
25
account-ui/src/main/resources/META-INF/persistence.xml
Normal file
25
account-ui/src/main/resources/META-INF/persistence.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
|
||||
<persistence-unit name="accountPu" transaction-type="JTA">
|
||||
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||
<jta-data-source>java:/jboss/accountDs</jta-data-source>
|
||||
<class>de.muehlencord.shared.account.business.account.entity.AccountEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.account.entity.AccountHistoryEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.account.entity.AccountLoginEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.account.entity.ApiKeyEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.application.entity.ApplicationEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.config.entity.ConfigEntity</class>
|
||||
<class>de.muehlencord.shared.account.business.mail.entity.MailTemplateEntity</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
|
||||
<validation-mode>NONE</validation-mode>
|
||||
<properties>
|
||||
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL94Dialect"/>
|
||||
<property name="hibernate.show_sql" value="false"/>
|
||||
<property name="hibernate.cache.use_second_level_cache" value="true"/>
|
||||
<property name="hibernate.cache.use_query_cache" value="true"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
32
account-ui/src/main/resources/admin-config.properties
Normal file
32
account-ui/src/main/resources/admin-config.properties
Normal file
@ -0,0 +1,32 @@
|
||||
admin.loginPage=login.xhtml
|
||||
admin.indexPage=web/index.xhtml
|
||||
#admin.dateFormat=
|
||||
#admin.breadcrumbSize=5
|
||||
admin.renderMessages=true
|
||||
#admin.renderAjaxStatus=true
|
||||
## disable filter to redirect to login page - shiro security filter is already doing this
|
||||
admin.disableFilter=true
|
||||
#admin.renderBreadCrumb=true
|
||||
#admin.enableSlideMenu=true
|
||||
#admin.enableRipple=true
|
||||
#admin.rippleElements= .ripplelink,button.ui-button,.ui-selectlistbox-item,.ui-multiselectlistbox-item,.ui-selectonemenu-label,.ui-selectcheckboxmenu,\
|
||||
#.ui-autocomplete-dropdown, .ui-autocomplete-item ... (the list goes on)
|
||||
admin.skin=skin-purple-light
|
||||
#admin.autoShowNavbar=true
|
||||
#admin.ignoredResources=
|
||||
#admin.loadingImage=ajaxloadingbar.gif
|
||||
#admin.extensionLessUrls=false
|
||||
admin.renderControlSidebar=false
|
||||
#admin.controlSidebar.showOnMobile=false
|
||||
#admin.controlSidebar.leftMenuTemplate=true
|
||||
#admin.controlSidebar.fixedLayout=false
|
||||
#admin.controlSidebar.boxedLayout=false
|
||||
#admin.controlSidebar.sidebarCollapsed=false
|
||||
#admin.controlSidebar.expandOnHover=false
|
||||
#admin.controlSidebar.fixed=false
|
||||
#admin.controlSidebar.darkSkin=true
|
||||
#admin.rippleMobileOnly=true
|
||||
admin.renderMenuSearch=false
|
||||
## do not autohide
|
||||
admin.autoHideMessages=false
|
||||
#admin.messagesHideTimeout=2500
|
||||
3
account-ui/src/main/resources/buildInfo.properties
Normal file
3
account-ui/src/main/resources/buildInfo.properties
Normal file
@ -0,0 +1,3 @@
|
||||
build.version=${project.version}
|
||||
build.timestamp=${timestamp}
|
||||
application.uuid=${applicationUuid}
|
||||
@ -0,0 +1,133 @@
|
||||
header_login=Login
|
||||
header_reset_password=Reset password
|
||||
message_username_password=Please enter your user name and a new password
|
||||
button_login=Login
|
||||
button_cancel=Cancel
|
||||
button_password_lost=Password lost?
|
||||
label_username=Username
|
||||
label_password=Password
|
||||
label_new_password=New Password
|
||||
button_password_reset=Reset password
|
||||
header_passwort_lost=Lost password
|
||||
message_start_password_reset=Please enter your username to start the password recovery procedure
|
||||
menu_dashboard=Dashboard
|
||||
menu_events=Events
|
||||
menu_administration=Administration
|
||||
menu_overview=Overview
|
||||
menu_emails=Emails
|
||||
menu_account=Account
|
||||
menu_config=Config
|
||||
menu_logout=Logout
|
||||
button_new=New
|
||||
button_delete=Delete
|
||||
button_edit=Edit
|
||||
button_reload=Reload
|
||||
label_name=Name
|
||||
label_description=Description
|
||||
label_event_date=Event Date
|
||||
label_reservation=Reservation
|
||||
label_reservation_from_to=Reservation from/to
|
||||
label_actions=Actions
|
||||
message_confirm=Are you sure?
|
||||
button_setup=Setup
|
||||
button_reservations=Reservations
|
||||
label_event_name=Event Name
|
||||
label_event_item_desc=Event Item Description
|
||||
label_timezone=Timezone
|
||||
label_event_start=Event Start
|
||||
label_event_end=Event End
|
||||
label_reservation_autostart=Reservation start
|
||||
label_reservation_autoend=Reservation end
|
||||
label_reservation_active=Reservation active
|
||||
label_reservation_max_items=Max Items
|
||||
label_booking_deadline=Booking deadline
|
||||
label_template_validation=Email validation mail template
|
||||
label_template_confirmation=Event confirmation mail template
|
||||
label_template_waitlist=Event waitlist mail template
|
||||
message_event_not_found=Event not found
|
||||
label_items=Items
|
||||
label_costs=Costs
|
||||
label_all=All
|
||||
label_yes=Yes
|
||||
label_no=No
|
||||
label_status=Status
|
||||
label_firstname=Firstname
|
||||
label_lastname=Lastname
|
||||
label_emailaddress=Emailaddress
|
||||
label_comment=Comment
|
||||
label_email_confirmed=Email confirmed
|
||||
label_booking_number=Booking Number
|
||||
label_booking_executed=booking executed
|
||||
tt_log_entries=Show log entries
|
||||
tt_cancel_reservation=Cancel the current reservation
|
||||
tt_send_email_again=Send email again
|
||||
tt_move_from_wl=Move from waitlist
|
||||
tt_fix_reservation=Try to fix the current reservation
|
||||
tt_edit_reservation=Edit the reservation
|
||||
button_refresh_free=Refresh free
|
||||
button_manual_reserve=Reserve
|
||||
button_export=Export
|
||||
label_amount=Amount
|
||||
label_select=Select
|
||||
label_created_by=Created by
|
||||
label_ip=IP
|
||||
label_ip_forwarded=IP (forwarded)
|
||||
label_value=Value
|
||||
label_useragent=Useragent
|
||||
button_ok=Ok
|
||||
message_comment=Please add a comment
|
||||
menu_help=Help
|
||||
label_event=Event
|
||||
label_active=Active
|
||||
label_waitlist=Waitlist
|
||||
label_is_waitlist=Is waitlist
|
||||
label_order=Order
|
||||
message_dynamic_numbering=dynamic numbering (put %n as placeholder)
|
||||
label_start_number=Start Number
|
||||
label_end_number=End Number
|
||||
header_item_def=Define items for event
|
||||
message_no_event_items=No event item defined
|
||||
button_overview=Overview
|
||||
label_reservation_auto_start=Automatically switch on/off
|
||||
label_item_public=Item public?
|
||||
label_is_publicitem=Is public
|
||||
label_customer_comment=Customer comment
|
||||
label_change_comment=Change comment
|
||||
label_existing_items=current items
|
||||
label_new_items=new items
|
||||
label_available_items=available items
|
||||
label_no_records=No records found.
|
||||
button_mail=Mail
|
||||
header_email_distribution=Email distribution
|
||||
label_template=Template
|
||||
label_demomode=Demo mode
|
||||
message_invalid_email=Please provide a valid email address
|
||||
menu_permissions=Permissions
|
||||
button_save=Save
|
||||
menu_groups=Groups
|
||||
message_email_sent=email sent
|
||||
message_email_with_error=emails with error
|
||||
message_no_email=no email address defined
|
||||
message_email_not_sent=Error while sending emails
|
||||
label_seating=Seating
|
||||
label_attachments=Attachments
|
||||
label_language=Language
|
||||
label_subject=Subject
|
||||
label_bytes=Bytes
|
||||
label_upload=Upload
|
||||
header_export=Export
|
||||
label_export_type=Export Type
|
||||
label_filtered=Filtered
|
||||
label_include_deleted=Include deleted
|
||||
label_include_log=Include Logs
|
||||
label_template_booking_executed=Booking executed template
|
||||
label_street=Street
|
||||
label_zipCode=ZIP Code
|
||||
label_city=City
|
||||
label_groupName=Groupname
|
||||
label_phoneNumber=Phone Number
|
||||
label_template_waitlist_cancelled=Waitlist cancelled mail template
|
||||
msgs_menu_status=Status
|
||||
menu_status=Status
|
||||
button_add=Add
|
||||
passwords_different=Passwords do not match, please check input
|
||||
@ -0,0 +1,134 @@
|
||||
|
||||
header_login=Anmeldung
|
||||
header_reset_password=Passwort zur\u00fccksetzten
|
||||
message_username_password=Bitte geben Deinen Benutzernamen und dein Passwort ein
|
||||
button_login=Anmelden
|
||||
button_cancel=Abbruch
|
||||
button_password_lost=Passwort vergessen?
|
||||
label_username=Benutzername
|
||||
label_password=Passwort
|
||||
label_new_password=Neues Passwort
|
||||
button_password_reset=Passwort zur\u00fccksetzten
|
||||
header_passwort_lost=Passwort vergessen
|
||||
message_start_password_reset=Bitte gib deinen Benutzernamen ein um das Zur\u00fccksetzten des Passworts zu starten.
|
||||
menu_dashboard=Dashbaord
|
||||
menu_events=Veranstaltungen
|
||||
menu_administration=Administration
|
||||
menu_overview=\u00dcbersicht
|
||||
menu_emails=E-Mails
|
||||
menu_account=Benutzer
|
||||
menu_config=Konfiguration
|
||||
menu_logout=Abmelden
|
||||
button_new=Neu
|
||||
button_delete=L\u00f6schen
|
||||
button_edit=Bearbeiten
|
||||
button_reload=Aktualisieren
|
||||
label_name=Name
|
||||
label_description=Beschreibung
|
||||
label_event_date=Veranstaltungsdatum
|
||||
label_reservation=Reservierung
|
||||
label_reservation_from_to=Reservierung von/bis
|
||||
label_actions=Aktionen
|
||||
message_confirm=Bist du sicher?
|
||||
button_setup=Setup
|
||||
button_reservations=Reservierungen
|
||||
label_event_name=Veranstaltungsname
|
||||
label_event_item_desc=Veranstaltungsobjekte
|
||||
label_timezone=Zeitzone
|
||||
label_event_start=Veranstaltungsbeginn
|
||||
label_event_end=Veranstaltungsende
|
||||
label_reservation_autostart=Reservierung von
|
||||
label_reservation_autoend=Reservierung bis
|
||||
label_reservation_active=Reservierung aktiv
|
||||
label_reservation_max_items=Maximale Objekte
|
||||
label_booking_deadline=Buchungsfrist
|
||||
label_template_validation=Vorlage Emailvalidierung
|
||||
label_template_confirmation=Vorlage Reservierungsbest\u00e4tigung
|
||||
label_template_waitlist=Vorlage Wartelist
|
||||
message_event_not_found=Veranstaltung nicht gefunden
|
||||
label_items=Objekte
|
||||
label_costs=Kosten
|
||||
label_all=Alle
|
||||
label_yes=Ja
|
||||
label_no=Nein
|
||||
label_status=Status
|
||||
label_firstname=Vorname
|
||||
label_lastname=Nachname
|
||||
label_emailaddress=E-Mail-Adresse
|
||||
label_comment=Kommentar
|
||||
label_email_confirmed=E-Mail best\u00e4tigt
|
||||
label_booking_number=Buchungsnummer
|
||||
label_booking_executed=Buchungs ausgef\u00fchrt
|
||||
tt_log_entries=Logbucheintr\u00e4ge
|
||||
tt_cancel_reservation=Storniere die Reservierung
|
||||
tt_send_email_again=Sende E-Mail erneut
|
||||
tt_move_from_wl=Schiebe von Warteliste
|
||||
tt_fix_reservation=Versuche den Fehler zu beheben
|
||||
tt_edit_reservation=Bearbeite die Reservierung
|
||||
button_refresh_free=Frei aktualisieren
|
||||
button_manual_reserve=Reservieren
|
||||
button_export=Exportieren
|
||||
label_amount=Anzahl
|
||||
label_select=W\u00e4hlen
|
||||
label_created_by=Erzeugt durch
|
||||
label_ip=IP
|
||||
label_ip_forwarded=IP (forwarded)
|
||||
label_value=Wert
|
||||
label_useragent=Useragent
|
||||
button_ok=Ok
|
||||
message_comment=Bitte geben Sie einen Kommentar an
|
||||
menu_help=Hilfe
|
||||
label_event=Veranstaltung
|
||||
label_active=Aktiv
|
||||
label_waitlist=Warteliste
|
||||
label_is_waitlist=Ist Warteliste
|
||||
label_order=Reihenfolge
|
||||
message_dynamic_numbering=Dynamische Nummerierung (%n als Platzhalter)
|
||||
label_start_number=Startnummer
|
||||
label_end_number=Endnummer
|
||||
header_item_def=Objekte f\u00fcr Veranstaltung
|
||||
message_no_event_items=Keine Objekte definiert
|
||||
button_overview=\u00dcbersicht
|
||||
label_reservation_auto_start=Automatisch ein/ausschalten
|
||||
label_item_public=\u00d6ffentlich verf\u00fcgar?
|
||||
label_is_publicitem=\u00d6ffentlich
|
||||
label_customer_comment=Kundenkommentar
|
||||
label_change_comment=\u00c4nderungskommentar
|
||||
label_existing_items=aktuelle Objekte
|
||||
label_new_items=neue Objekte
|
||||
label_available_items=verf\u00fcgbare Objekte
|
||||
label_no_records=Keine Daten gefunden.
|
||||
button_mail=E-Mail
|
||||
header_email_distribution=Emailversand
|
||||
label_template=Vorlage
|
||||
label_demomode=Demomodus
|
||||
message_invalid_email=Bitte geben Sie eine g\u00fcltige Emailadresse an
|
||||
menu_permissions=Berechtigungen
|
||||
button_save=Speichern
|
||||
menu_groups=Gruppen
|
||||
message_email_sent=Email gesendet
|
||||
message_email_with_error=Emails mit Fehler
|
||||
message_no_email=keine Emailadresse verf\u00fcgbar
|
||||
message_email_not_sent=Fehler beim Versenden der Email
|
||||
label_seating=Saalplan Platz
|
||||
label_attachments=Attachments
|
||||
label_language=Sprache
|
||||
label_subject=Betreff
|
||||
label_bytes=Bytes
|
||||
label_upload=Upload
|
||||
header_export=Exportieren
|
||||
label_export_type=Export Art
|
||||
label_filtered=Gefiltert
|
||||
label_include_deleted=Einschlie\u00dflich gel\u00f6scht
|
||||
label_include_log=mit Logbuch
|
||||
label_template_booking_executed=Vorlage Buchung durchgef\u00fchrt
|
||||
label_street=Stra\u00dfe
|
||||
label_zipCode=PLZ
|
||||
label_city=Ort
|
||||
label_groupName=Gruppenname
|
||||
label_phoneNumber=Telefonnummer
|
||||
label_template_waitlist_cancelled=Vorlage Warteliste Abbruch
|
||||
msgs_menu_status=Status
|
||||
menu_status=Status
|
||||
button_add=Hinzuf\u00fcgen
|
||||
passwords_different=Passw\u00f6rter stimmen \u00fcberein, bitte \u00fcberpr\u00fcfen Sie ihre Eingabe
|
||||
@ -0,0 +1,134 @@
|
||||
|
||||
header_login=Login
|
||||
header_reset_password=Reset password
|
||||
message_username_password=Please enter your user name and a new password
|
||||
button_login=Login
|
||||
button_cancel=Cancel
|
||||
button_password_lost=Password lost?
|
||||
label_username=Username
|
||||
label_password=Password
|
||||
label_new_password=New Password
|
||||
button_password_reset=Reset password
|
||||
header_passwort_lost=Lost password
|
||||
message_start_password_reset=Please enter your username to start the password recovery procedure
|
||||
menu_dashboard=Dashboard
|
||||
menu_events=Events
|
||||
menu_administration=Administration
|
||||
menu_overview=Overview
|
||||
menu_emails=Emails
|
||||
menu_account=Account
|
||||
menu_config=Config
|
||||
menu_logout=Logout
|
||||
button_new=New
|
||||
button_delete=Delete
|
||||
button_edit=Edit
|
||||
button_reload=Reload
|
||||
label_name=Name
|
||||
label_description=Description
|
||||
label_event_date=Event Date
|
||||
label_reservation=Reservation
|
||||
label_reservation_from_to=Reservation from/to
|
||||
label_actions=Actions
|
||||
message_confirm=Are you sure?
|
||||
button_setup=Setup
|
||||
button_reservations=Reservations
|
||||
label_event_name=Event Name
|
||||
label_event_item_desc=Event Item Description
|
||||
label_timezone=Timezone
|
||||
label_event_start=Event Start
|
||||
label_event_end=Event End
|
||||
label_reservation_autostart=Reservation Start
|
||||
label_reservation_autoend=Reservation End
|
||||
label_reservation_active=Reservation active
|
||||
label_reservation_max_items=Max Items
|
||||
label_booking_deadline=Booking deadline
|
||||
label_template_validation=Email validation mail template
|
||||
label_template_confirmation=Event confirmation mail template
|
||||
label_template_waitlist=Event waitlist mail template
|
||||
message_event_not_found=Event not found
|
||||
label_items=Items
|
||||
label_costs=Costs
|
||||
label_all=All
|
||||
label_yes=Yes
|
||||
label_no=No
|
||||
label_status=Status
|
||||
label_firstname=Firstname
|
||||
label_lastname=Lastname
|
||||
label_emailaddress=Emailaddress
|
||||
label_comment=Comment
|
||||
label_email_confirmed=Email confirmed
|
||||
label_booking_number=Booking Number
|
||||
label_booking_executed=booking executed
|
||||
tt_log_entries=Show log entries
|
||||
tt_cancel_reservation=Cancel the current reservation
|
||||
tt_send_email_again=Send email again
|
||||
tt_move_from_wl=Move from waitlist
|
||||
tt_fix_reservation=Try to fix the current reservation
|
||||
tt_edit_reservation=Edit the reservation
|
||||
button_refresh_free=Refresh free
|
||||
button_manual_reserve=Reserve
|
||||
button_export=Export
|
||||
label_amount=Amount
|
||||
label_select=Select
|
||||
label_created_by=Created by
|
||||
label_ip=IP
|
||||
label_ip_forwarded=IP (forwarded)
|
||||
label_value=Value
|
||||
label_useragent=Useragent
|
||||
button_ok=Ok
|
||||
message_comment=Please add a comment
|
||||
menu_help=Help
|
||||
label_event=Event
|
||||
label_active=Active
|
||||
label_waitlist=Waitlist
|
||||
label_is_waitlist=Is waitlist
|
||||
label_order=Reservation Order
|
||||
message_dynamic_numbering=dynamic numbering (put %n as placeholder)
|
||||
label_start_number=Start Number
|
||||
label_end_number=End Number
|
||||
header_item_def=Define items for event
|
||||
message_no_event_items=No event item defined
|
||||
button_overview=Overview
|
||||
label_reservation_auto_start=Automatically switch on/off
|
||||
label_item_public=Item public?
|
||||
label_is_publicitem=Is public
|
||||
label_customer_comment=Customer comment
|
||||
label_change_comment=Change comment
|
||||
label_existing_items=current items
|
||||
label_new_items=new items
|
||||
label_available_items=available items
|
||||
label_no_records=No records found.
|
||||
button_mail=Mail
|
||||
header_email_distribution=Email distribution
|
||||
label_template=Template
|
||||
label_demomode=Demo mode
|
||||
message_invalid_email=Please provide a valid email address
|
||||
menu_permissions=Permissions
|
||||
button_save=Save
|
||||
menu_groups=Groups
|
||||
message_email_sent=email sent
|
||||
message_email_with_error=emails with error
|
||||
message_no_email=no email address defined
|
||||
message_email_not_sent=Error while sending emails
|
||||
label_seating=Seating
|
||||
label_attachments=Anh\u00e4nge
|
||||
label_language=Language
|
||||
label_subject=Subject
|
||||
label_bytes=Bytes
|
||||
label_upload=Hochladen
|
||||
header_export=Export
|
||||
label_export_type=Export Type
|
||||
label_filtered=Filtered
|
||||
label_include_deleted=Include deleted
|
||||
label_include_log=Include Logs
|
||||
label_template_booking_executed=Booking executed template
|
||||
label_street=Street
|
||||
label_zipCode=ZIP Code
|
||||
label_city=City
|
||||
label_groupName=Groupname
|
||||
label_phoneNumber=Phone Number
|
||||
label_template_waitlist_cancelled=Waitlist cancelled mail template
|
||||
msgs_menu_status=Status
|
||||
menu_status=Status
|
||||
button_add=Add
|
||||
passwords_different=Passwords do not match, please check input
|
||||
7
account-ui/src/main/webapp/WEB-INF/beans.xml
Normal file
7
account-ui/src/main/webapp/WEB-INF/beans.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
|
||||
version="1.1"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
||||
22
account-ui/src/main/webapp/WEB-INF/faces-config.xml
Normal file
22
account-ui/src/main/webapp/WEB-INF/faces-config.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<faces-config version="2.2"
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
|
||||
|
||||
<application>
|
||||
<el-resolver>org.primefaces.application.exceptionhandler.PrimeExceptionHandlerELResolver</el-resolver>
|
||||
|
||||
<locale-config>
|
||||
<default-locale>en</default-locale>
|
||||
<supported-locale>en</supported-locale>
|
||||
<supported-locale>de</supported-locale>
|
||||
</locale-config>
|
||||
<!-- register own messages for general i18n support -->
|
||||
<resource-bundle>
|
||||
<base-name>de.muehlencord.shared.account.web.presentation.messages</base-name>
|
||||
<var>msgs</var>
|
||||
</resource-bundle>
|
||||
</application>
|
||||
|
||||
</faces-config>
|
||||
59
account-ui/src/main/webapp/WEB-INF/shiro.ini
Normal file
59
account-ui/src/main/webapp/WEB-INF/shiro.ini
Normal file
@ -0,0 +1,59 @@
|
||||
[main]
|
||||
|
||||
# Context factory required for LDAP
|
||||
${shiro.contextFactory}
|
||||
|
||||
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
|
||||
securityManager.cacheManager = $cacheManager
|
||||
|
||||
# DataSource Setup
|
||||
datasource = org.apache.shiro.jndi.JndiObjectFactory
|
||||
datasource.resourceName = java:/jboss/accountDs
|
||||
datasource.resourceRef = true
|
||||
|
||||
# HashService
|
||||
hashService = org.apache.shiro.crypto.hash.DefaultHashService
|
||||
hashService.hashIterations = 500000
|
||||
hashService.hashAlgorithmName = SHA-512
|
||||
hashService.generatePublicSalt = true
|
||||
|
||||
# Password service
|
||||
passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
|
||||
passwordService.hashService = $hashService
|
||||
|
||||
# Required password matcher
|
||||
${shiro.passwordMatcher}
|
||||
|
||||
# LDAP Realm setup
|
||||
${shiro.ldapRealm}
|
||||
|
||||
# JDBC Realm setup
|
||||
jdbcRealm = de.muehlencord.shared.account.shiro.realm.AccountRealm
|
||||
jdbcRealm.applicationId = ${applicationUuid}
|
||||
jdbcRealm.credentialsMatcher = $passwordMatcher
|
||||
jdbcRealm.dataSource = $datasource
|
||||
|
||||
# Activate realms
|
||||
authcStrategy = ${shiro.authcStrategy}
|
||||
securityManager.realms = ${shiro.realms}
|
||||
securityManager.authenticator.authenticationStrategy = $authcStrategy
|
||||
|
||||
# Setup authentication filter
|
||||
authc = de.muehlencord.shirofaces.filter.FacesAjaxAwarePassThruAuthenticationFilter
|
||||
authc.loginUrl = /login.xhtml
|
||||
authc.successUrl = /web/index.xhtml
|
||||
|
||||
roles.unauthorizedUrl = /error/accessDenied.xhtml
|
||||
|
||||
#
|
||||
# filter setup
|
||||
#
|
||||
[urls]
|
||||
/public/**=anon
|
||||
/resources/**=anon
|
||||
/fonts/**=anon
|
||||
/javax.faces.resource/**=anon
|
||||
/login.xhtml=authc
|
||||
/logout.xhtml=logout
|
||||
/**=authc
|
||||
# /web/**=authc
|
||||
123
account-ui/src/main/webapp/WEB-INF/web.xml
Normal file
123
account-ui/src/main/webapp/WEB-INF/web.xml
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
|
||||
<display-name>reservation-system-backed</display-name>
|
||||
<context-param>
|
||||
<param-name>javax.faces.PROJECT_STAGE</param-name>
|
||||
<param-value>${jsf.projectStage}</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name>
|
||||
<param-value>1048576</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>primefaces.THEME</param-name>
|
||||
<param-value>admin</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>primefaces.FONT_AWESOME</param-name>
|
||||
<param-value>true</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>primefaces.MOVE_SCRIPTS_TO_BOTTOM</param-name>
|
||||
<param-value>true</param-value>
|
||||
</context-param>
|
||||
<servlet>
|
||||
<servlet-name>FacesServlet</servlet-name>
|
||||
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>FacesServlet</servlet-name>
|
||||
<url-pattern>*.xhtml</url-pattern>
|
||||
</servlet-mapping>
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>web/index.xhtml</welcome-file>
|
||||
</welcome-file-list>
|
||||
<!-- Shiro Web Environment -->
|
||||
<listener>
|
||||
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
|
||||
</listener>
|
||||
<filter>
|
||||
<display-name>ShiroFilter</display-name>
|
||||
<filter-name>shiroFilter</filter-name>
|
||||
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>shiroFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
<dispatcher>REQUEST</dispatcher>
|
||||
<dispatcher>FORWARD</dispatcher>
|
||||
<dispatcher>INCLUDE</dispatcher>
|
||||
<dispatcher>ERROR</dispatcher>
|
||||
</filter-mapping>
|
||||
<!-- Faces Exception Filter -->
|
||||
<filter>
|
||||
<display-name>FacesExceptionFilter</display-name>
|
||||
<filter-name>facesExceptionFilter</filter-name>
|
||||
<filter-class>org.omnifaces.filter.FacesExceptionFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>facesExceptionFilter</filter-name>
|
||||
<servlet-name>FacesServlet</servlet-name>
|
||||
</filter-mapping>
|
||||
<!-- Content Security Policy headers -->
|
||||
<filter>
|
||||
<filter-name>contentSecurityFilter</filter-name>
|
||||
<filter-class>de.muehlencord.sf.filter.ContentSecurityPolicyFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>report-only</param-name>
|
||||
<param-value>false</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>default-src</param-name>
|
||||
<param-value>'none'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>img-src</param-name>
|
||||
<param-value>'self'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>script-src</param-name>
|
||||
<param-value>'self' 'unsafe-inline' 'unsafe-eval'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>style-src</param-name>
|
||||
<param-value>'self' 'unsafe-inline'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>connect-src</param-name>
|
||||
<param-value>'self'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>font-src</param-name>
|
||||
<param-value>'self'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>object-src</param-name>
|
||||
<param-value>'none'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>media-src</param-name>
|
||||
<param-value>'none'</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>child-src</param-name>
|
||||
<param-value>'none'</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
<!-- Security related headers -->
|
||||
<filter>
|
||||
<display-name>OwaspStandardFilter</display-name>
|
||||
<filter-name>owaspStandardFilter</filter-name>
|
||||
<filter-class>de.muehlencord.sf.filter.OwaspStandardFilter</filter-class>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>owaspStandardFilter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
</web-app>
|
||||
96
account-ui/src/main/webapp/login.xhtml
Normal file
96
account-ui/src/main/webapp/login.xhtml
Normal file
@ -0,0 +1,96 @@
|
||||
<?xml version='1.0' encoding='UTF-8' ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:f="http://java.sun.com/jsf/core"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
<h:head>
|
||||
<title>Login Page</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<link rel="shortcut icon" href="#{resource['images/favicon/favicon.ico']}" />
|
||||
<h:outputStylesheet name="css/admin.css" />
|
||||
|
||||
<style type="text/css">
|
||||
/* below css hides growls in small screens and makes messages visible */
|
||||
@media (max-width: 768px) {
|
||||
|
||||
body div.ui-growl {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body div.ui-messages {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
/* below css hides messages in medium/big devices and makes growl visible in such devices */
|
||||
@media (min-width: 769px) {
|
||||
body div.ui-growl {
|
||||
display: block;
|
||||
}
|
||||
body div.ui-messages {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
body.login-page {
|
||||
background-color: transparent;
|
||||
}
|
||||
html {
|
||||
background: url(#{resource[ 'images:login-bg.png' ]}) no-repeat center center fixed;
|
||||
-webkit-background-size: cover;
|
||||
-moz-background-size: cover;
|
||||
-o-background-size: cover;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
|
||||
</h:head>
|
||||
<h:body styleClass="hold-transition login-page">
|
||||
<p:growl sticky="true">
|
||||
<p:autoUpdate />
|
||||
</p:growl>
|
||||
<div id="loader" class="load-bar" style="display: none">
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<p:link href="/web/accounts.xhtml"><b>Account </b>Management</p:link>
|
||||
<h:outputLabel rendered="#{instanceView.developmentVersion}" value="#{instanceView.instanceName}" />
|
||||
</div>
|
||||
<!-- /.login-logo -->
|
||||
<div class="box login-box-body">
|
||||
<h:form>
|
||||
<p:focus context="panel" for="username" />
|
||||
|
||||
<p class="login-box-msg">Sign in to start your session</p>
|
||||
<p:messages closable="true" />
|
||||
|
||||
|
||||
<div id="panel" class="form-group has-feedback">
|
||||
<p:inputText id="username" value="#{loginView.username}" styleClass="form-control" placeholder="Username"
|
||||
required="true" autocomplete="off"
|
||||
requiredMessage="Username is required."/>
|
||||
<i class="fa fa-user form-control-feedback"></i>
|
||||
</div>
|
||||
<div class="form-group has-feedback">
|
||||
<p:inputText value="#{loginView.password}" type="password" styleClass="form-control"
|
||||
placeholder="Password" required="true" autocomplete="off" size="40"
|
||||
requiredMessage="Password is required."/>
|
||||
<i class="fa fa-lock form-control-feedback" style="font-size: 18px" ></i>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p:spacer height="10"/>
|
||||
<div class="col-xs-12">
|
||||
<p:commandButton styleClass="btn btn-success btn-block" onclick="showBar()"
|
||||
action="#{loginView.authenticate}" oncomplete="if(args.validationFailed) { hideBar()}"
|
||||
value="Sign In" process="@form" update="@form" icon="fa fa-sign-in" iconPos="left"/>
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</div>
|
||||
<!-- /.login-box-body -->
|
||||
</div>
|
||||
</h:body>
|
||||
</html>
|
||||
15
account-ui/src/main/webapp/logout.xhtml
Normal file
15
account-ui/src/main/webapp/logout.xhtml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/resources/template/template.xhtml">
|
||||
|
||||
<ui:define name="title">
|
||||
Logout
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="body">
|
||||
<h2>You are logged out. </h2>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
To change this license header, choose License Headers in Project Properties.
|
||||
To change this template file, choose Tools | Templates
|
||||
and open the template in the editor.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:composite="http://java.sun.com/jsf/composite"
|
||||
xmlns:p="http://primefaces.org/ui">
|
||||
<composite:interface />
|
||||
|
||||
<composite:implementation>
|
||||
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade" styleClass="box-solid box-danger">
|
||||
<p:commandButton value="Yes" type="button" styleClass="btn-material btn-primary ui-confirmdialog-yes"
|
||||
icon="fa fa-check"/>
|
||||
<p:commandButton value="No" type="button" styleClass="btn-material btn-danger ui-confirmdialog-no"
|
||||
icon="fa fa-close"/>
|
||||
</p:confirmDialog>
|
||||
</composite:implementation>
|
||||
</html>
|
||||
18
account-ui/src/main/webapp/resources/css/admin.css
Normal file
18
account-ui/src/main/webapp/resources/css/admin.css
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
To change this license header, choose License Headers in Project Properties.
|
||||
To change this template file, choose Tools | Templates
|
||||
and open the template in the editor.
|
||||
*/
|
||||
/*
|
||||
Created on : 24.10.2018, 19:31:57
|
||||
Author : Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
|
||||
.watermark {
|
||||
position: absolute;
|
||||
opacity: 0.25;
|
||||
font-size: 3em;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
15
account-ui/src/main/webapp/resources/template/footer.xhtml
Normal file
15
account-ui/src/main/webapp/resources/template/footer.xhtml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
|
||||
<title>Account Management</title>
|
||||
</head>
|
||||
<body>
|
||||
<ui:composition>
|
||||
© Joern Muehlencord - Account Management - version ${applicationController.version} - build date ${applicationController.buildDate}
|
||||
</ui:composition>
|
||||
</body>
|
||||
</html>
|
||||
62
account-ui/src/main/webapp/resources/template/leftmenu.xhtml
Normal file
62
account-ui/src/main/webapp/resources/template/leftmenu.xhtml
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:shiro="http://shiro.apache.org/tags">
|
||||
|
||||
|
||||
<shiro:authenticated>
|
||||
<h:form id="menuform">
|
||||
<ul class="sidebar-menu tree" data-widget="tree">
|
||||
|
||||
<li>
|
||||
<p:link outcome="/web/index.xhtml">
|
||||
<i class="fa fa-home"></i>
|
||||
<span>Home</span>
|
||||
</p:link>
|
||||
</li>
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.applicationListAll}">
|
||||
<li>
|
||||
<p:link outcome="/web/applications.xhtml">
|
||||
<i class="fa fa-tablet"></i>
|
||||
<span>Applications</span>
|
||||
</p:link>
|
||||
</li>
|
||||
</shiro:hasPermission>
|
||||
|
||||
<shiro:hasAnyPermission name="#{permissionConstants.permissionsCombined}">
|
||||
<li>
|
||||
<p:link outcome="/web/permissions.xhtml">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
<span>Permissions</span>
|
||||
</p:link>
|
||||
</li>
|
||||
</shiro:hasAnyPermission>
|
||||
|
||||
<shiro:hasAnyPermission name="#{permissionConstants.rolesCombined}">
|
||||
<li>
|
||||
<p:link outcome="/web/roles.xhtml">
|
||||
<i class="fa fa-circle"></i>
|
||||
<span>Roles</span>
|
||||
</p:link>
|
||||
</li>
|
||||
</shiro:hasAnyPermission>
|
||||
|
||||
<li>
|
||||
<p:link outcome="/web/accounts.xhtml">
|
||||
<i class="fa fa-user"></i>
|
||||
<span>Accounts</span>
|
||||
</p:link>
|
||||
</li>
|
||||
<li>
|
||||
<p:commandLink target="/logout.xhtml" actionListener="#{loginView.logout}">
|
||||
<i class="fa fa-sign-out"></i>
|
||||
<span>Logout</span>
|
||||
</p:commandLink>
|
||||
</li>
|
||||
</ul>
|
||||
</h:form>
|
||||
</shiro:authenticated>
|
||||
</ui:composition>
|
||||
50
account-ui/src/main/webapp/resources/template/template.xhtml
Normal file
50
account-ui/src/main/webapp/resources/template/template.xhtml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2018 joern@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.
|
||||
-->
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/admin.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html">
|
||||
|
||||
<ui:define name="head">
|
||||
<title>Account UI</title>
|
||||
<link rel="shortcut icon" href="#{resource['images/favicon/favicon.ico']}" />
|
||||
<h:outputStylesheet name="css/admin.css" />
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="logo-lg">
|
||||
Account Management
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="logo-mini">
|
||||
AM
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="menu">
|
||||
<ui:include src="/resources/template/leftmenu.xhtml"/>
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="footer">
|
||||
<ui:include src="/resources/template/footer.xhtml"/>
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="content-end">
|
||||
<h:outputLabel styleClass="watermark" rendered="#{instanceView.developmentVersion}" value="#{instanceView.instanceName}" />
|
||||
</ui:define>
|
||||
|
||||
|
||||
</ui:composition>
|
||||
341
account-ui/src/main/webapp/web/accounts.xhtml
Normal file
341
account-ui/src/main/webapp/web/accounts.xhtml
Normal file
@ -0,0 +1,341 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/resources/template/template.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:co="http://java.sun.com/jsf/composite/composite"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||
xmlns:o="http://omnifaces.org/ui"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite/composite"
|
||||
xmlns:shiro="http://shiro.apache.org/tags">
|
||||
|
||||
<ui:define name="title">
|
||||
Account Overview
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="description">
|
||||
for #{applicationView.currentApplication.applicationName}
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="body">
|
||||
<p:panel styleClass="box-solid" rendered="#{! empty applicationView.currentApplication}">
|
||||
<h:form id="accountForm" prependId="false">
|
||||
<p:dataTable id="accountTable" value="#{accountView.accounts}" var="account" rowKey="#{account.username}" selectionMode="single" selection="#{accountView.currentAccount}"
|
||||
styleClass="box-primary">
|
||||
<p:ajax event="rowSelect" update="buttonPanel" listener="#{accountView.selectAccount}" />
|
||||
<p:ajax event="rowUnselect" update="buttonPanel" listener="#{accountView.unselectAccount}" />
|
||||
<p:column headerText="Username">
|
||||
<h:outputText value="#{account.username}" />
|
||||
</p:column>
|
||||
<p:column headerText="Lastname">
|
||||
<h:outputText value="#{account.lastname}" />
|
||||
</p:column>
|
||||
<p:column headerText="Firstname">
|
||||
<h:outputText value="#{account.firstname}" />
|
||||
</p:column>
|
||||
<p:column headerText="Email">
|
||||
<h:outputText value="#{account.emailaddress}" />
|
||||
</p:column>
|
||||
<p:column headerText="Status">
|
||||
<h:outputText value="#{account.status}" />
|
||||
</p:column>
|
||||
<p:column headerText="Can login" >
|
||||
<p:selectBooleanCheckbox id="canLogin" disabled="true" value="#{!empty account.accountLogin}" />
|
||||
</p:column>
|
||||
<p:column headerText="CreatedOn">
|
||||
<h:outputText value="#{account.createdOn}" >
|
||||
<f:convertDateTime type="both" dateStyle="full" timeStyle="short" timeZone="Europe/Berlin"/>
|
||||
</h:outputText>
|
||||
</p:column>
|
||||
<p:column headerText="CreatedBy">
|
||||
<h:outputText value="#{account.createdBy}" />
|
||||
</p:column>
|
||||
<p:column headerText="LastUpdatedOn">
|
||||
<h:outputText value="#{account.lastUpdatedOn}">
|
||||
<f:convertDateTime type="both" dateStyle="full" timeStyle="short" timeZone="Europe/Berlin"/>
|
||||
</h:outputText>
|
||||
</p:column>
|
||||
<p:column headerText="LastUpdatedBy">
|
||||
<h:outputText value="#{account.lastUpdatedBy}" />
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
|
||||
<p:spacer height="10px" />
|
||||
<p:panel id="buttonPanel" styleClass="box-primary" style="margin-bottom:20px">
|
||||
<div class="ui-g ui-fluid">
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.accountDelete}">
|
||||
<div class="col-sm-12 col-md-4" style="margin-top:10px">
|
||||
<div class="ui-inputgroup" >
|
||||
<h:outputLabel for="includeDisabledCheckbox" value="Include disabled accounts?" />
|
||||
<p:inputSwitch id="includeDisabledCheckbox" value="#{accountView.showDisabledAccounts}" styleClass="btn-teal btn-block" >
|
||||
<p:ajax listener="#{accountView.showDisabledAccountsChange}" update="accountTable" />
|
||||
</p:inputSwitch>
|
||||
</div>
|
||||
</div>
|
||||
</shiro:hasPermission>
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.accountAdd}">
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<p:commandButton value="New" id="newButton" icon="fa fa-plus"
|
||||
update="editDialog" oncomplete="PF('editDialogVar').show();"
|
||||
actionListener="#{accountView.newAccount}" styleClass="btn-primary btn-block" />
|
||||
</div>
|
||||
</shiro:hasPermission>
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.accountEdit}">
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<p:commandButton value="Edit" id="editButton" icon="fa fa-pencil"
|
||||
update="editDialog" oncomplete="PF('editDialogVar').show();"
|
||||
actionListener="#{accountView.editAccount}" disabled="#{!accountView.accountSelected}" styleClass="btn-teal btn-block" />
|
||||
</div>
|
||||
</shiro:hasPermission>
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.accountDelete}">
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<p:commandButton value="Delete" id="deleteButton" icon="fa fa-trash-o"
|
||||
update=":accountForm:accountTable" action="#{accountView.deleteAccount}" disabled="#{accountView.accountSelected eq false or accountView.currentLoggedInUser eq true}" styleClass="btn-danger btn-block">
|
||||
<p:confirm header="Confirmation" message="Are you sure?" icon="fa fa-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</shiro:hasPermission>
|
||||
|
||||
<shiro:hasAnyPermission name="#{permissionConstants.accountsCombined}">
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<shiro:hasPermission name="#{permissionConstants.accountLoginAdd}">
|
||||
<c:if test="#{empty accountView.currentAccount.accountLogin}">
|
||||
<p:commandButton value="Add login" id="addLoginButton" icon="fa fa-plus" disabled="#{!accountView.accountSelected}"
|
||||
update="editLoginDialog" oncomplete="PF('editLoginDialogVar').show();"
|
||||
action="#{accountView.addAccountLogin}" styleClass="btn-teal btn-block">
|
||||
</p:commandButton>
|
||||
</c:if>
|
||||
</shiro:hasPermission>
|
||||
|
||||
<c:if test="#{!empty accountView.currentAccount.accountLogin}">
|
||||
<p:splitButton value="Edit login" id="editLoginButton" icon="fa fa-pencil" disabled="#{!accountView.accountSelected}" styleClass="btn-success btn-block">
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.accountLoginEdit}">
|
||||
<p:menuitem value="Edit login" icon="fa fa-pencil" disabled="#{!accountView.accountSelected}"
|
||||
update="editLoginDialog" oncomplete="PF('editLoginDialogVar').show();"
|
||||
action="#{accountView.editAccountLogin}" >
|
||||
</p:menuitem>
|
||||
</shiro:hasPermission>
|
||||
|
||||
|
||||
<shiro:hasPermission name="#{permissionConstants.accountLoginDelete}">
|
||||
<p:menuitem value="Delete login" icon="fa fa-trash-o" disabled="#{accountView.currentLoggedInUser}"
|
||||
update="accountTable,buttonPanel" styleClass="btn-danger btn-block"
|
||||
action="#{accountView.deleteAccountLogin}" >
|
||||
<p:confirm header="Confirmation" message="Are you sure?" icon="fa fa-exclamation-triangle" />
|
||||
</p:menuitem>
|
||||
</shiro:hasPermission>
|
||||
|
||||
</p:splitButton>
|
||||
</c:if>
|
||||
</div>
|
||||
</shiro:hasAnyPermission>
|
||||
</div>
|
||||
</p:panel>
|
||||
|
||||
|
||||
|
||||
<composite:confirmationDialog />
|
||||
</h:form>
|
||||
</p:panel>
|
||||
|
||||
|
||||
<p:dialog id="editDialog" widgetVar="editDialogVar" header="Edit account" width="600"
|
||||
modal="true" appendTo="@(body)" showEffect="fade" hideEffect="fade" styleClass="box-solid box-primary" >
|
||||
<h:form id="editDialogForm">
|
||||
<p:messages id="editDialogMessages" showDetail="true" showIcon="true" showSummary="true">
|
||||
<p:autoUpdate />
|
||||
</p:messages>
|
||||
|
||||
<div class="ui-g ui-fluid">
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="username" value="Username" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<c:if test="#{accountView.currentAccount.createdBy != null}">
|
||||
<h:outputText id="username" value="#{accountView.currentAccount.username}" />
|
||||
</c:if>
|
||||
<c:if test="#{accountView.currentAccount.createdBy == null}">
|
||||
<p:inputText id="username" value="#{accountView.currentAccount.username}" />
|
||||
</c:if>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="username"><p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="lastname" value="Lastname" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:inputText id="lastname" value="#{accountView.currentAccount.lastname}" size="40" maxlength="100"/>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3 ">
|
||||
<p:message for="lastname"> <p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="firstname" value="Firstname" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:inputText id="firstname" value="#{accountView.currentAccount.firstname}" size="40" maxlength="100" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="firstname"> <p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="emailaddress" value="emailaddress" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:inputText id="emailaddress" value="#{accountView.currentAccount.emailaddress}" size="40" maxlength="200">
|
||||
<f:validator validatorId="de.muehlencord.shared.jeeutil.validator.EmailValidator" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="emailaddress"> <p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<c:if test="#{accountView.currentAccount.username != null}">
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="status" value="Status" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:selectOneMenu id="status" value="#{accountView.currentAccount.status}" >
|
||||
<f:selectItems value="#{accountView.statusList}" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="status" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="createdon" value="Created on" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<h:outputText id="createdon" value="#{accountView.currentAccount.createdOn}" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="createdon" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="createdby" value="Created by" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<h:outputText id="createdby" value="#{accountView.currentAccount.createdBy}" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="createdby" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="lastupdatedon" value="Last updated on" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<h:outputText id="lastupdatedon" value="#{accountView.currentAccount.lastUpdatedOn}" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="lastupdatedon" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="lastupdatedby" value="Last updated by" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<h:outputText id="lastupdatedby" value="#{accountView.currentAccount.lastUpdatedBy}" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="lastupdatedby" />
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="roles" value="Roles" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:selectManyMenu id="roles" var="role" label="#{role.roleName}" value="#{accountView.currentAccountRoles}" converter="omnifaces.SelectItemsConverter" required="false" >
|
||||
<f:selectItems value="#{accountView.allApplicationRoles}" var="roleItem" itemValue="#{roleItem}" />
|
||||
<p:column>
|
||||
<h:outputText value="#{role.application.applicationName}-#{role.roleName}"/>
|
||||
</p:column>
|
||||
</p:selectManyMenu>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="roles" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Save" action="#{accountView.saveEditAccount}" styleClass="btn-primary btn-block"
|
||||
oncomplete="if (args && !args.validationFailed) PF('editDialogVar').hide();" update=":accountForm:accountTable" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Cancel" action="#{accountView.cancelEditAccount}" immediate="true" styleClass="btn-teal btn-block"
|
||||
oncomplete="PF('editDialogVar').hide();" />
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
|
||||
<p:dialog id="editLoginDialog" widgetVar="editLoginDialogVar" header="Edit account login" width="600"
|
||||
modal="true" appendTo="@(body)" showEffect="fade" hideEffect="fade" styleClass="box-solid box-primary" >
|
||||
<h:form id="editLoginDialogForm">
|
||||
<p:messages id="editLoginDialogMessages" showDetail="true" showIcon="true" showSummary="true">
|
||||
<p:autoUpdate />
|
||||
</p:messages>
|
||||
|
||||
<div class="ui-g ui-fluid">
|
||||
<o:validateMultiple id="myId" components="password repeatPassword"
|
||||
validator="#{accountView.validatePasswords}" message="#{msgs.passwords_different}" />
|
||||
|
||||
<div class="col-sm-12">
|
||||
<p:outputLabel value="Enter a new password or keep values empty to keep existing / autogenrated value" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="password" value="Password" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:password id="password" value="#{accountView.password}" maxlength="32" size="32" required="false"/>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="password" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="repeatPassword" value="repeat Password" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:password id="repeatPassword" value="#{accountView.repeatPassword}" maxlength="32" size="32" required="false"/>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="repeatPassword" />
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Save" action="#{accountView.saveEditAccountLogin}" styleClass="btn-primary btn-block"
|
||||
oncomplete="if (args && !args.validationFailed) PF('editLoginDialogVar').hide();" update=":accountForm:accountTable,:accountForm:buttonPanel" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Cancel" action="#{accountView.cancelEditAccountLogin}" immediate="true" styleClass="btn-teal btn-block"
|
||||
oncomplete="PF('editLoginDialogVar').hide();" />
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
84
account-ui/src/main/webapp/web/applications.xhtml
Normal file
84
account-ui/src/main/webapp/web/applications.xhtml
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/resources/template/template.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:co="http://java.sun.com/jsf/composite/composite"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite/composite">
|
||||
|
||||
<ui:define name="title">
|
||||
Applications
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="body" >
|
||||
|
||||
<p:panel styleClass="box-solid" rendered="#{! empty applicationView.currentApplication}">
|
||||
<h:form id="applicationForm" prependId="false">
|
||||
|
||||
<div class="ui-g ui-fluid">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:selectOneMenu id="applicationSelect" value="#{applicationView.currentApplication}" converter="omnifaces.SelectItemsConverter" required="true">
|
||||
<f:selectItems value="#{applicationView.allApplications}" var="app" itemLabel="#{app.applicationName}" itemValue="#{app}" />
|
||||
</p:selectOneMenu>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<p:commandButton value="Select" styleClass="btn-primary btn-solid}" actionListener="#{applicationView.selectApplication}" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<p:commandButton value="New" id="newButton" icon="fa fa-plus"
|
||||
update="editDialog" oncomplete="PF('editDialogVar').show();"
|
||||
actionListener="#{applicationView.newApplication}" styleClass="btn-teal btn-block" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<p:commandButton id="deletePermissionButton" icon="fa fa-trash-o" value="#{msgs.button_delete}" actionListener="#{applicationView.deleteApplication}"
|
||||
update="applicationSelect" styleClass="btn-danger btn-block" >
|
||||
<p:confirm header="Confirmation" message="Are you sure?" icon="fa fa-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<composite:confirmationDialog />
|
||||
</h:form>
|
||||
</p:panel>
|
||||
|
||||
<p:dialog id="editDialog" widgetVar="editDialogVar" header="Edit account" width="600"
|
||||
modal="true" appendTo="@(body)" showEffect="fade" hideEffect="fade" styleClass="box-solid box-primary" >
|
||||
<h:form id="editDialogForm">
|
||||
<p:messages id="editDialogMessages" showDetail="true" showIcon="true" showSummary="true">
|
||||
<p:autoUpdate />
|
||||
</p:messages>
|
||||
|
||||
<div class="ui-g ui-fluid">
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="applicationName" value="Application name" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:inputText id="applicationName" value="#{applicationView.editApplication.applicationName}">
|
||||
<f:validator validatorId="uniqueApplicationValidator"/>
|
||||
</p:inputText>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="applicationName"><p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Save" action="#{applicationView.saveEditApplication}" styleClass="btn-primary btn-block"
|
||||
oncomplete="if (args && !args.validationFailed) PF('editDialogVar').hide();" update=":applicationForm" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Cancel" action="#{applicationView.cancelEditApplication}" immediate="true" styleClass="btn-teal btn-block"
|
||||
oncomplete="PF('editDialogVar').hide();" />
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
13
account-ui/src/main/webapp/web/index.xhtml
Normal file
13
account-ui/src/main/webapp/web/index.xhtml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
template="/resources/template/template.xhtml">
|
||||
|
||||
<ui:define name="title">
|
||||
Home
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="body" >
|
||||
Home
|
||||
</ui:define>
|
||||
</ui:composition>
|
||||
108
account-ui/src/main/webapp/web/permissions.xhtml
Normal file
108
account-ui/src/main/webapp/web/permissions.xhtml
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/resources/template/template.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:co="http://java.sun.com/jsf/composite/composite"
|
||||
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite/composite">
|
||||
|
||||
<ui:define name="title">
|
||||
Permissions
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="description">
|
||||
for #{applicationView.currentApplication.applicationName}
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="body">
|
||||
<p:panel styleClass="box-solid" rendered="#{! empty applicationView.currentApplication}">
|
||||
<h:form id="permissionForm">
|
||||
<p:dataTable id="permissionTable" value="#{permissionView.appPermissions}" var="permission" rowKey="#{permission.id}"
|
||||
selectionMode="single" selection="#{permissionView.currentPermission}" styleClass="box-primary">
|
||||
<p:ajax event="rowSelect" update=":permissionForm:permissionTable:editPermissionButton,:permissionForm:permissionTable:deletePermissionButton" />
|
||||
<p:ajax event="rowUnselect" update=":permissionForm:permissionTable:editPermissionButton,:permissionForm:permissionTable:deletePermissionButton" />
|
||||
|
||||
<p:column headerText="Permission name">
|
||||
<h:outputText value="#{permission.permissionName}" />
|
||||
</p:column>
|
||||
<p:column headerText="Permission description">
|
||||
<h:outputText value="#{permission.permissionDescription}" />
|
||||
</p:column>
|
||||
|
||||
<f:facet name="footer">
|
||||
<div class="ui-g-12 ui-md-2">
|
||||
<p:commandButton id="newPermissionButton" icon="fa fa-plus" value="#{msgs.button_new}" action="#{permissionView.newPermission}"
|
||||
oncomplete="PF('editDialogVar').show();" update="editDialog" styleClass="btn-primary btn-block" />
|
||||
</div>
|
||||
<div class="ui-g-12 ui-md-2">
|
||||
<p:commandButton id="editPermissionButton" icon="fa fa-pencil" value="#{msgs.button_edit}" action="#{permissionView.editPermission}"
|
||||
oncomplete="PF('editDialogVar').show();" update="editDialog" styleClass="btn-teal btn-block" disabled="#{!permissionView.canEdit}" />
|
||||
</div>
|
||||
<div class="ui-g-12 ui-md-2">
|
||||
<p:commandButton id="deletePermissionButton" icon="fa fa-trash-o" value="#{msgs.button_delete}" actionListener="#{permissionView.deletePermission}"
|
||||
update="permissionForm" styleClass="btn-danger btn-block" disabled="#{!permissionView.canDelete}">
|
||||
<p:confirm header="Confirmation" message="Are you sure?" icon="fa fa-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</f:facet>
|
||||
</p:dataTable>
|
||||
|
||||
<composite:confirmationDialog />
|
||||
</h:form>
|
||||
</p:panel>
|
||||
|
||||
|
||||
<p:dialog id="editDialog" widgetVar="editDialogVar" header="Edit permission" width="600"
|
||||
modal="true" appendTo="@(body)" showEffect="fade" hideEffect="fade" styleClass="box-solid box-primary" >
|
||||
<h:form id="editDialogForm">
|
||||
<p:messages id="editDialogMessages" showDetail="true" showIcon="true" showSummary="true">
|
||||
<p:autoUpdate />
|
||||
</p:messages>
|
||||
|
||||
<div class="ui-g ui-fluid">
|
||||
<div class="ui-g-12 ui-md-3">
|
||||
<div class="ui-inputgroup">
|
||||
<span class="ui-inputgroup-addon"><i style="font-size: 20px" class="fa fa-edit"></i></span>
|
||||
<p:inputText id="newName" value="#{permissionView.currentPermission.permissionName}" maxlength="80" size="30" placeholder="#{msgs.label_name}" >
|
||||
<f:validator validatorId="uniquePermissionNameValidator"/>
|
||||
<f:attribute name="application" value="#{permissionView.currentApplication}" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-9">
|
||||
<p:message for="newName"><p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="ui-g-12 ui-md-3">
|
||||
<div class="ui-inputgroup">
|
||||
<span class="ui-inputgroup-addon"><i style="font-size: 20px" class="fa fa-edit"></i></span>
|
||||
<p:inputText id="newDescription" value="#{permissionView.currentPermission.permissionDescription}" maxlength="200" size="30" placeholder="#{msgs.label_description}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-9">
|
||||
<p:message for="newDescription"><p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Save" action="#{permissionView.saveEditPermission}" styleClass="btn-primary btn-block"
|
||||
oncomplete="if (args && !args.validationFailed) PF('editDialogVar').hide();" update=":permissionForm:permissionTable" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Cancel" action="#{permissionView.cancelEditPermission}" immediate="true" styleClass="btn-teal btn-block"
|
||||
oncomplete="PF('editDialogVar').hide();" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
|
||||
|
||||
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
135
account-ui/src/main/webapp/web/roles.xhtml
Normal file
135
account-ui/src/main/webapp/web/roles.xhtml
Normal file
@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
template="/resources/template/template.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:co="http://java.sun.com/jsf/composite/composite"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:composite="http://xmlns.jcp.org/jsf/composite/composite">
|
||||
|
||||
<ui:define name="title">
|
||||
Roles Overview
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="description">
|
||||
for #{applicationView.currentApplication.applicationName}
|
||||
</ui:define>
|
||||
|
||||
<ui:define name="body">
|
||||
<p:panel styleClass="box-solid" rendered="#{! empty applicationView.currentApplication}">
|
||||
<h:form id="roleForm">
|
||||
<p:dataTable id="roleTable" value="#{roleView.allRoles}" var="role" rowKey="#{role.id}" styleClass="box-primary"
|
||||
selectionMode="single" selection="#{roleView.currentRole}">
|
||||
<p:ajax event="rowSelect" update=":roleForm:permissionTable, editRoleButton, deleteRoleButton" listener="#{roleView.onRoleSelect}"/>
|
||||
<p:ajax event="rowUnselect" update=":roleForm:permissionTable, editRoleButton, deleteRoleButton" />
|
||||
|
||||
<p:column headerText="Role name">
|
||||
<h:outputText value="#{role.roleName}" />
|
||||
</p:column>
|
||||
<p:column headerText="Role description">
|
||||
<h:outputText value="#{role.roleDescription}" />
|
||||
</p:column>
|
||||
|
||||
|
||||
<f:facet name="footer">
|
||||
<div class="ui-g ui-fluid">
|
||||
<div class="col-sm-12 col-md-2" style="margin-top:10px">
|
||||
<p:commandButton id="newRoleButton" icon="fa fa-plus" value="#{msgs.button_new}" actionListener="#{roleView.startNewRole}"
|
||||
update="editDialog" oncomplete="PF('editDialogVar').show();" styleClass="btn-primary btn-block"/>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2" style="margin-top:10px">
|
||||
<p:commandButton id="editRoleButton" icon="fa fa-pencil" value="#{msgs.button_edit}" disabled="#{!roleView.roleSelected}"
|
||||
update="editDialog" oncomplete="PF('editDialogVar').show();" styleClass="btn-teal btn-block"/>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2" style="margin-top:10px">
|
||||
<p:commandButton id="deleteRoleButton" icon="fa fa-trash-o" value="#{msgs.button_delete}" disabled="#{!roleView.roleSelected}"
|
||||
action="#{roleView.deleteRole}" update=":roleForm:roleTable" styleClass="btn-danger btn-block">
|
||||
<p:confirm header="Confirmation" message="Are you sure?" icon="fa fa-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</div>
|
||||
</f:facet>
|
||||
</p:dataTable>
|
||||
|
||||
<p:spacer height="15px;" />
|
||||
|
||||
<p:dataTable id="permissionTable" value="#{roleView.rolePermissions}" var="permission" rowKey="#{permission.id}" styleClass="box-teal"
|
||||
selectionMode="single" selection="#{roleView.currentPermission}">
|
||||
<p:ajax event="rowSelect" update="addPermissionButton, deletePermissionButton" />
|
||||
<p:ajax event="rowUnselect" update="addPermissionButton, deletePermissionButton" />
|
||||
|
||||
<p:column headerText="Permission name">
|
||||
<h:outputText value="#{permission.permissionName}" />
|
||||
</p:column>
|
||||
<p:column headerText="Permission description">
|
||||
<h:outputText value="#{permission.permissionDescription}" />
|
||||
</p:column>
|
||||
<f:facet name="footer" >
|
||||
<p:selectOneMenu value="#{roleView.newPermission}" converter="omnifaces.SelectItemsConverter" >
|
||||
<f:selectItems id="permissionListItems" value="#{roleView.missingPermissions}" var="missingPermission" itemLabel="#{missingPermission.permissionName}" itemValue="#{missingPermission}" />
|
||||
</p:selectOneMenu>
|
||||
<div class="ui-g-12 ui-md-2">
|
||||
<p:commandButton id="addPermissionButton" icon="fa fa-plus" value="#{msgs.button_add}" action="#{roleView.addRolePermission}"
|
||||
update="permissionTable" styleClass="btn-primary btn-block" disabled="#{!roleView.missingPermissionAvailable}" />
|
||||
</div>
|
||||
<div class="ui-g-12 ui-md-2">
|
||||
<p:commandButton id="deletePermissionButton" icon="fa fa-trash-o" value="#{msgs.button_delete}" update=":roleForm:permissionTable"
|
||||
action="#{roleView.removeRolePermission}" styleClass="btn-danger btn-block"
|
||||
disabled="#{!roleView.permissionSelected}" >
|
||||
<p:confirm header="Confirmation" message="Are you sure?" icon="fa fa-exclamation-triangle" />
|
||||
</p:commandButton>
|
||||
</div>
|
||||
</f:facet>
|
||||
</p:dataTable>
|
||||
|
||||
<composite:confirmationDialog />
|
||||
</h:form>
|
||||
</p:panel>
|
||||
|
||||
<p:dialog id="editDialog" widgetVar="editDialogVar" header="Edit account" width="600"
|
||||
modal="true" appendTo="@(body)" showEffect="fade" hideEffect="fade" styleClass="box-solid box-primary" >
|
||||
<h:form id="editDialogForm">
|
||||
<p:messages id="editDialogMessages" showDetail="true" showIcon="true" showSummary="true">
|
||||
<p:autoUpdate />
|
||||
</p:messages>
|
||||
|
||||
<div class="ui-g ui-fluid">
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="newName" value="Role name" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:inputText id="newName" value="#{roleView.currentRole.roleName}" placeholder="#{msgs.label_name}" maxlength="80" size="40">
|
||||
<f:validator validatorId="uniqueApplicationRoleNameValidator"/>
|
||||
<f:attribute name="application" value="#{roleView.currentApplication}" />
|
||||
</p:inputText>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="newName"><p:autoUpdate /></p:message>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:outputLabel for="newDescription" value="Description" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:inputText id="newDescription" value="#{roleView.currentRole.roleDescription}" placeholder="#{msgs.label_description}" maxlength="200" size="40" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<p:message for="newDescription"><p:autoUpdate /></p:message>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Save" action="#{roleView.saveEditRole}" styleClass="btn-primary btn-block"
|
||||
oncomplete="if (args && !args.validationFailed) PF('editDialogVar').hide();" update=":roleForm:roleTable" />
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<p:spacer height="10px" />
|
||||
<p:commandButton value="Cancel" action="#{roleView.cancelEditRole}" immediate="true" styleClass="btn-teal btn-block"
|
||||
oncomplete="PF('editDialogVar').hide();" />
|
||||
</div>
|
||||
</div>
|
||||
</h:form>
|
||||
</p:dialog>
|
||||
</ui:define>
|
||||
|
||||
</ui:composition>
|
||||
@ -4,21 +4,20 @@
|
||||
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-account</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>ejb</packaging>
|
||||
|
||||
<parent>
|
||||
<artifactId>shared</artifactId>
|
||||
<groupId>de.muehlencord</groupId>
|
||||
<version>1.0</version>
|
||||
<version>1.1</version>
|
||||
</parent>
|
||||
|
||||
<name>shared-account</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>10</maven.compiler.source>
|
||||
<maven.compiler.target>10</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -41,10 +40,13 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>shared-jeeutil</artifactId>
|
||||
<type>jar</type>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>shared-account-dao</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@ -66,9 +68,23 @@
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.muehlencord.shared</groupId>
|
||||
<artifactId>shared-util</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax</groupId>
|
||||
<artifactId>javaee-api</artifactId>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
DROP TABLE config;
|
||||
|
||||
CREATE TABLE config (
|
||||
config_key varchar(100),
|
||||
config_value varchar(200),
|
||||
CONSTRAINT config_pk PRIMARY KEY (config_key)
|
||||
);
|
||||
INSERT INTO config (config_key, config_value) VALUES ('account.maxFailedLogins', '5');
|
||||
@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Author: joern.muehlencord
|
||||
* Created: 06.09.2015
|
||||
*/
|
||||
|
||||
DROP TABLE account_role;
|
||||
DROP TABLE account_history;
|
||||
DROP TABLE account;
|
||||
DROP TABLE role_permission;
|
||||
DROP TABLE application_role;
|
||||
DROP TABLE application_permission;
|
||||
|
||||
|
||||
CREATE TABLE application_role (
|
||||
id UUID NOT NULL,
|
||||
role_name varchar(80) NOT NULL,
|
||||
role_description varchar(200) NOT NULL,
|
||||
CONSTRAINT pk_application_role_pk PRIMARY KEY (id),
|
||||
CONSTRAINT uidx_application_id UNIQUE (id)
|
||||
);
|
||||
|
||||
CREATE TABLE account (
|
||||
id UUID NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
emailaddress varchar(200) NOT NULL,
|
||||
firstname varchar(100) NOT NULL,
|
||||
lastname varchar(100) NOT NULL,
|
||||
account_password char(200) NOT NULL,
|
||||
last_login timestamp with time zone,
|
||||
last_failed_login timestamp with time zone,
|
||||
failure_count int NOT NULL DEFAULT 0,
|
||||
status varchar(10) NOT NULL DEFAULT 'NEW', -- NEW, INIT, OK, BLOCKED,
|
||||
password_reset_ongoing boolean NOT NULL DEFAULT false,
|
||||
password_reset_valid_to timestamp with time zone,
|
||||
password_reset_hash char(200),
|
||||
created_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'),
|
||||
created_by varchar(32) NOT NULL,
|
||||
last_updated_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'),
|
||||
last_updated_by varchar(32) NOT NULL,
|
||||
CONSTRAINT pk_account PRIMARY KEY (id),
|
||||
CONSTRAINT uidx_username UNIQUE (username)
|
||||
);
|
||||
|
||||
CREATE TABLE account_history (
|
||||
id UUID NOT NULL,
|
||||
account_id UUID NOT NULL,
|
||||
message varchar(200),
|
||||
failure_count int NOT NULL DEFAULT 0,
|
||||
status varchar(20) NOT NULL, -- constants needed, after action - new, init, active, blocked, inactive, marked for deletion
|
||||
last_updated_on timestamp with time zone NOT NULL DEFAULT (now() at time zone 'utc'),
|
||||
last_updated_by varchar(32) NOT NULL,
|
||||
CONSTRAINT pk_account_history PRIMARY KEY (id),
|
||||
CONSTRAINT fk_account_history_username_fk FOREIGN KEY (account_id) REFERENCES account (id)
|
||||
);
|
||||
|
||||
CREATE TABLE account_role (
|
||||
account UUID NOT NULL,
|
||||
account_role UUID NOT NULL,
|
||||
CONSTRAINT pk_account_role PRIMARY KEY (account, account_role),
|
||||
CONSTRAINT fk_account_role_account FOREIGN KEY (account) REFERENCES account(id),
|
||||
CONSTRAINT fk_account_role_role_name FOREIGN KEY (account_role) REFERENCES application_role(id)
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE application_permission (
|
||||
id UUID NOT NULL,
|
||||
permission_name varchar(80) NOT NULL,
|
||||
permission_description varchar(200) NOT NULL,
|
||||
CONSTRAINT pk_application_permission PRIMARY KEY (id),
|
||||
CONSTRAINT uidx_application_permission_name UNIQUE (permission_name)
|
||||
);
|
||||
|
||||
CREATE TABLE role_permission (
|
||||
application_role UUID NOT NULL,
|
||||
role_permission UUID NOT NULL,
|
||||
CONSTRAINT pk_role_permission_role_permission_name PRIMARY KEY (application_role, role_permission),
|
||||
CONSTRAINT fk_role_permission_application_role FOREIGN KEY (application_role) REFERENCES application_role(id),
|
||||
CONSTRAINT fk_role_permission_role_permission FOREIGN KEY (role_permission) REFERENCES application_permission(id)
|
||||
);
|
||||
|
||||
INSERT INTO application_permission (id, permission_name, permission_description) values ('dfd0f8f1-4a51-4fdc-9a1c-a942bee9b649', 'test:view', 'Display test view');
|
||||
|
||||
INSERT INTO application_role (id, role_name, role_description) values ('5cd0aca0-5466-483d-8f3e-c369f8061131','Admin', 'Admin role');
|
||||
INSERT INTO application_role (id, role_name, role_description) values ('da30060e-fd23-4016-a506-4e12e9322148', 'User', 'Standard user role');
|
||||
|
||||
-- INSERT INTO role_permission (role_name, permission_name) values ('Admin','test:view');
|
||||
|
||||
INSERT INTO account (id, username, emailaddress, firstname, lastname, account_password, created_by, last_updated_by) values('ab5c8337-6872-4aea-a9b9-78ea63706b8f','admin', 'joern@muehlencord.de', 'Joern', 'Muehlencord','$shiro1$SHA-256$500000$4bHPNH9k539UjdFLgm/HOA==$T/n8skgoGSOtNw/c9ScDlXCiGrx2cZF0Esrvf6WPq6g=', 'admin','admin'); --admin/secret
|
||||
INSERT INTO account_role (account, account_role) values ('ab5c8337-6872-4aea-a9b9-78ea63706b8f', '5cd0aca0-5466-483d-8f3e-c369f8061131');
|
||||
|
||||
--select uuid_generate_v4();
|
||||
@ -1,21 +0,0 @@
|
||||
DROP TABLE mail_template;
|
||||
|
||||
CREATE TABLE mail_template (
|
||||
template_name varchar(40) NOT NULL,
|
||||
template_value text NOT NULL,
|
||||
CONSTRAINT mail_template_pk PRIMARY KEY (template_name)
|
||||
);
|
||||
|
||||
|
||||
INSERT INTO mail_template (template_name, template_value) VALUES('password_reset_html',
|
||||
'<#ftl strip_whitespace = true>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
Dear ${account.firstname},<br>
|
||||
<br>
|
||||
you requested to reset your password at ${parameter.url}. Please open the following URL to proceed.<br>
|
||||
<a href="${parameter.resetUrl}">${parameter.resetUrl}</a><br>
|
||||
<br>
|
||||
</body>
|
||||
</html>');
|
||||
410
account/sql/account.dbm
Normal file
410
account/sql/account.dbm
Normal file
@ -0,0 +1,410 @@
|
||||
<?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="0.9" 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="1" max-obj-count="6">
|
||||
<schema name="public"/>
|
||||
<position x="1480" y="220"/>
|
||||
<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="1" max-obj-count="4">
|
||||
<schema name="public"/>
|
||||
<position x="100" y="260"/>
|
||||
<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="1" max-obj-count="12">
|
||||
<schema name="public"/>
|
||||
<position x="1080" y="460"/>
|
||||
<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="1" max-obj-count="8">
|
||||
<schema name="public"/>
|
||||
<position x="180" y="640"/>
|
||||
<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="1" max-obj-count="3">
|
||||
<schema name="public"/>
|
||||
<position x="480" y="440"/>
|
||||
<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="1" max-obj-count="4">
|
||||
<schema name="public"/>
|
||||
<position x="1080" y="100"/>
|
||||
<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="1" max-obj-count="3">
|
||||
<schema name="public"/>
|
||||
<position x="560" y="80"/>
|
||||
<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="1" max-obj-count="2">
|
||||
<schema name="public"/>
|
||||
<position x="120" y="940"/>
|
||||
<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="1" max-obj-count="2">
|
||||
<schema name="public"/>
|
||||
<position x="720" y="320"/>
|
||||
<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="1" max-obj-count="15">
|
||||
<schema name="public"/>
|
||||
<position x="1520" y="680"/>
|
||||
<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="1" max-obj-count="7">
|
||||
<schema name="public"/>
|
||||
<position x="680" y="760"/>
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
251
account/sql/account.sql
Normal file
251
account/sql/account.sql
Normal file
@ -0,0 +1,251 @@
|
||||
-- Database generated with pgModeler (PostgreSQL Database Modeler).
|
||||
-- pgModeler version: 0.9.2-beta
|
||||
-- PostgreSQL version: 9.6
|
||||
-- Project Site: pgmodeler.io
|
||||
-- Model Author: ---
|
||||
|
||||
|
||||
-- Database creation must be done outside a multicommand file.
|
||||
-- These commands were put in this file only as a convenience.
|
||||
-- -- object: account_test | type: DATABASE --
|
||||
-- -- DROP DATABASE IF EXISTS account_test;
|
||||
-- CREATE DATABASE account_test
|
||||
-- ENCODING = 'UTF8'
|
||||
-- LC_COLLATE = 'C'
|
||||
-- LC_CTYPE = 'C';
|
||||
-- -- ddl-end --
|
||||
--
|
||||
|
||||
-- object: public.config | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.config CASCADE;
|
||||
CREATE TABLE public.config (
|
||||
application uuid NOT NULL,
|
||||
config_key varchar(100) NOT NULL,
|
||||
config_key_account uuid NOT NULL,
|
||||
config_key_group varchar(200),
|
||||
config_value varchar(200),
|
||||
CONSTRAINT config_pk PRIMARY KEY (application,config_key,config_key_account)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.application_role | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.application_role CASCADE;
|
||||
CREATE TABLE public.application_role (
|
||||
id uuid NOT NULL,
|
||||
application uuid NOT NULL,
|
||||
role_name character varying(80) NOT NULL,
|
||||
role_description character varying(200) NOT NULL,
|
||||
CONSTRAINT application_role_pk PRIMARY KEY (id),
|
||||
CONSTRAINT application_role_name_uidx UNIQUE (application,role_name)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.account | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.account CASCADE;
|
||||
CREATE TABLE public.account (
|
||||
id uuid NOT NULL,
|
||||
username character varying(32) NOT NULL,
|
||||
emailaddress character varying(200) NOT NULL,
|
||||
firstname character varying(100) NOT NULL,
|
||||
lastname character varying(100) NOT NULL,
|
||||
status character varying(10) NOT NULL DEFAULT 'NEW',
|
||||
created_on timestamp with time zone NOT NULL DEFAULT timezone('utc'::text, now()),
|
||||
created_by character varying(32) NOT NULL,
|
||||
last_updated_on timestamp with time zone NOT NULL DEFAULT timezone('utc'::text, now()),
|
||||
last_updated_by character varying(32) NOT NULL,
|
||||
CONSTRAINT pk_account PRIMARY KEY (id),
|
||||
CONSTRAINT uidx_username UNIQUE (username)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.account_history | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.account_history CASCADE;
|
||||
CREATE TABLE public.account_history (
|
||||
id uuid NOT NULL,
|
||||
account_id uuid NOT NULL,
|
||||
message character varying(200),
|
||||
failure_count integer NOT NULL DEFAULT 0,
|
||||
status character varying(20) NOT NULL,
|
||||
last_updated_on timestamp with time zone NOT NULL DEFAULT timezone('utc'::text, now()),
|
||||
last_updated_by character varying(32) NOT NULL,
|
||||
CONSTRAINT pk_account_history PRIMARY KEY (id)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.account_role | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.account_role CASCADE;
|
||||
CREATE TABLE public.account_role (
|
||||
account uuid NOT NULL,
|
||||
account_role uuid NOT NULL,
|
||||
CONSTRAINT pk_account_role PRIMARY KEY (account,account_role)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.application_permission | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.application_permission CASCADE;
|
||||
CREATE TABLE public.application_permission (
|
||||
id uuid NOT NULL,
|
||||
application uuid NOT NULL,
|
||||
permission_name character varying(80) NOT NULL,
|
||||
permission_description character varying(200) NOT NULL,
|
||||
CONSTRAINT pk_application_permission PRIMARY KEY (id),
|
||||
CONSTRAINT application_permission_name_uidx UNIQUE (application,permission_name)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.role_permission | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.role_permission CASCADE;
|
||||
CREATE TABLE public.role_permission (
|
||||
application_role uuid NOT NULL,
|
||||
role_permission uuid NOT NULL,
|
||||
CONSTRAINT pk_role_permission_role_permission_name PRIMARY KEY (application_role,role_permission)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.mail_template | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.mail_template CASCADE;
|
||||
CREATE TABLE public.mail_template (
|
||||
template_name character varying(40) NOT NULL,
|
||||
template_value text NOT NULL,
|
||||
CONSTRAINT mail_template_pk PRIMARY KEY (template_name)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.application | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.application CASCADE;
|
||||
CREATE TABLE public.application (
|
||||
id uuid NOT NULL,
|
||||
application_name varchar(200) NOT NULL,
|
||||
CONSTRAINT application_pk PRIMARY KEY (id)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- -- object: "uuid-ossp" | type: EXTENSION --
|
||||
-- -- DROP EXTENSION IF EXISTS "uuid-ossp" CASCADE;
|
||||
-- CREATE EXTENSION "uuid-ossp"
|
||||
-- WITH SCHEMA public;
|
||||
-- -- ddl-end --
|
||||
--
|
||||
-- object: public.account_login | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.account_login CASCADE;
|
||||
CREATE TABLE public.account_login (
|
||||
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
account uuid NOT NULL,
|
||||
account_password varchar(200) NOT NULL,
|
||||
last_login timestamptz,
|
||||
last_failed_login timestamptz,
|
||||
failure_count integer NOT NULL DEFAULT 0,
|
||||
password_reset_ongoing boolean NOT NULL DEFAULT false,
|
||||
password_reset_valid_to timestamptz,
|
||||
password_reset_hash varchar(200),
|
||||
created_on timestamptz NOT NULL DEFAULT timezone('utc'::text, now()),
|
||||
created_by varchar(32) NOT NULL,
|
||||
last_updated_on timestamptz NOT NULL DEFAULT timezone('utc'::text, now()),
|
||||
last_updated_by varchar(32) NOT NULL,
|
||||
CONSTRAINT account_login_pk PRIMARY KEY (id),
|
||||
CONSTRAINT account_login_uidx UNIQUE (account)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: public.api_key | type: TABLE --
|
||||
DROP TABLE IF EXISTS public.api_key CASCADE;
|
||||
CREATE TABLE public.api_key (
|
||||
id uuid NOT NULL,
|
||||
account uuid NOT NULL,
|
||||
api_key varchar(200) NOT NULL,
|
||||
issued_on timestamptz NOT NULL DEFAULT timezone('utc'::text, now()),
|
||||
expiration smallint,
|
||||
expires_on timestamptz NOT NULL,
|
||||
CONSTRAINT api_key_pk PRIMARY KEY (id)
|
||||
|
||||
);
|
||||
-- ddl-end --
|
||||
|
||||
-- object: config_key_account_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.config DROP CONSTRAINT IF EXISTS config_key_account_fk CASCADE;
|
||||
ALTER TABLE public.config ADD CONSTRAINT config_key_account_fk FOREIGN KEY (config_key_account)
|
||||
REFERENCES public.account (id) MATCH FULL
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: config_application_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.config DROP CONSTRAINT IF EXISTS config_application_fk CASCADE;
|
||||
ALTER TABLE public.config ADD CONSTRAINT config_application_fk FOREIGN KEY (application)
|
||||
REFERENCES public.application (id) MATCH FULL
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: application_role_app_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.application_role DROP CONSTRAINT IF EXISTS application_role_app_fk CASCADE;
|
||||
ALTER TABLE public.application_role ADD CONSTRAINT application_role_app_fk FOREIGN KEY (application)
|
||||
REFERENCES public.application (id) MATCH FULL
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: fk_account_history_username_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.account_history DROP CONSTRAINT IF EXISTS fk_account_history_username_fk CASCADE;
|
||||
ALTER TABLE public.account_history ADD CONSTRAINT fk_account_history_username_fk FOREIGN KEY (account_id)
|
||||
REFERENCES public.account (id) MATCH SIMPLE
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: fk_account_role_account | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.account_role DROP CONSTRAINT IF EXISTS fk_account_role_account CASCADE;
|
||||
ALTER TABLE public.account_role ADD CONSTRAINT fk_account_role_account FOREIGN KEY (account)
|
||||
REFERENCES public.account (id) MATCH SIMPLE
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: fk_account_role_role_name | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.account_role DROP CONSTRAINT IF EXISTS fk_account_role_role_name CASCADE;
|
||||
ALTER TABLE public.account_role ADD CONSTRAINT fk_account_role_role_name FOREIGN KEY (account_role)
|
||||
REFERENCES public.application_role (id) MATCH SIMPLE
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: application_permission_app_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.application_permission DROP CONSTRAINT IF EXISTS application_permission_app_fk CASCADE;
|
||||
ALTER TABLE public.application_permission ADD CONSTRAINT application_permission_app_fk FOREIGN KEY (application)
|
||||
REFERENCES public.application (id) MATCH FULL
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: fk_role_permission_application_role | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.role_permission DROP CONSTRAINT IF EXISTS fk_role_permission_application_role CASCADE;
|
||||
ALTER TABLE public.role_permission ADD CONSTRAINT fk_role_permission_application_role FOREIGN KEY (application_role)
|
||||
REFERENCES public.application_role (id) MATCH SIMPLE
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: fk_role_permission_role_permission | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.role_permission DROP CONSTRAINT IF EXISTS fk_role_permission_role_permission CASCADE;
|
||||
ALTER TABLE public.role_permission ADD CONSTRAINT fk_role_permission_role_permission FOREIGN KEY (role_permission)
|
||||
REFERENCES public.application_permission (id) MATCH SIMPLE
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: account_login_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.account_login DROP CONSTRAINT IF EXISTS account_login_fk CASCADE;
|
||||
ALTER TABLE public.account_login ADD CONSTRAINT account_login_fk FOREIGN KEY (account)
|
||||
REFERENCES public.account (id) MATCH FULL
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: api_key_account_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.api_key DROP CONSTRAINT IF EXISTS api_key_account_fk CASCADE;
|
||||
ALTER TABLE public.api_key ADD CONSTRAINT api_key_account_fk FOREIGN KEY (account)
|
||||
REFERENCES public.account (id) MATCH FULL
|
||||
ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
29
account/sql/account_prefill.sql
Normal file
29
account/sql/account_prefill.sql
Normal file
@ -0,0 +1,29 @@
|
||||
DELETE FROM config;
|
||||
DELETE FROM account_role;
|
||||
DELETE FROM account_login;
|
||||
DELETE FROM account;
|
||||
DELETE FROM role_permission;
|
||||
DELETE FROM application_role;
|
||||
DELETE FROM application_permission;
|
||||
DELETE FROM application;
|
||||
|
||||
INSERT INTO application (id, application_name) values ('143a2bd3-7e0b-4162-a76e-3031331c7dfe', 'Account UI');
|
||||
|
||||
-- add roles to Account UI application
|
||||
INSERT INTO application_role (id, application, role_name, role_description) values ('5cd0aca0-5466-483d-8f3e-c369f8061131','143a2bd3-7e0b-4162-a76e-3031331c7dfe', 'Admin', 'Admin role');
|
||||
INSERT INTO application_role (id, application, role_name, role_description) values ('da30060e-fd23-4016-a506-4e12e9322148','143a2bd3-7e0b-4162-a76e-3031331c7dfe', 'User', 'Standard user role');
|
||||
|
||||
-- create accounts
|
||||
INSERT INTO account (id, username, firstname, lastname, emailaddress, created_by, last_updated_by) values ('2a712ed4-30f8-47b4-a002-7d87441b7013', 'system', 'system', 'system', 'n/a', 'system', 'system');
|
||||
INSERT INTO account (id, username, emailaddress, firstname, lastname, created_by, last_updated_by) values('ab5c8337-6872-4aea-a9b9-78ea63706b8f','admin', 'joern@muehlencord.de', 'Joern', 'Muehlencord','system','system');
|
||||
-- assign AccountUI.Admin role to admin user
|
||||
INSERT INTO account_role (account, account_role) values ('ab5c8337-6872-4aea-a9b9-78ea63706b8f', '5cd0aca0-5466-483d-8f3e-c369f8061131');
|
||||
|
||||
-- create login for user admin (login admin, password secret)
|
||||
INSERT INTO account_login (account, account_password, created_by, last_updated_by) VALUES ('ab5c8337-6872-4aea-a9b9-78ea63706b8f', '$shiro1$SHA-256$500000$4bHPNH9k539UjdFLgm/HOA==$T/n8skgoGSOtNw/c9ScDlXCiGrx2cZF0Esrvf6WPq6g=', 'system', 'system'); --admin/secret
|
||||
|
||||
-- config
|
||||
INSERT INTO config (application, config_key, config_key_account, config_value) VALUES ('143a2bd3-7e0b-4162-a76e-3031331c7dfe', 'account.maxFailedLogins', '2a712ed4-30f8-47b4-a002-7d87441b7013', '5');
|
||||
|
||||
|
||||
|
||||
4
account/sql/backupAccount.bat
Normal file
4
account/sql/backupAccount.bat
Normal file
@ -0,0 +1,4 @@
|
||||
echo off
|
||||
SET BACKUPFOLDER=%~dp0
|
||||
"C:\Program Files\PostgreSQL\10\bin\pg_dump.exe" -U jomu -n public --column-inserts --attribute-inserts --no-owner --no-privileges --no-acl --clean account > %BACKUPFOLDER%\account.dump
|
||||
pause;
|
||||
4
account/sql/backupAccountTest.bat
Normal file
4
account/sql/backupAccountTest.bat
Normal file
@ -0,0 +1,4 @@
|
||||
echo off
|
||||
SET BACKUPFOLDER=%~dp0
|
||||
"C:\Program Files\PostgreSQL\10\bin\pg_dump.exe" -U jomu -n public --column-inserts --attribute-inserts --no-owner --no-privileges --no-acl --clean account_test > %BACKUPFOLDER%\account_test.dump
|
||||
pause;
|
||||
9
account/sql/createDatabaseFileFromDBM.bat
Normal file
9
account/sql/createDatabaseFileFromDBM.bat
Normal file
@ -0,0 +1,9 @@
|
||||
@ECHO OFF
|
||||
set BASEDIR=%~dp0%
|
||||
set PGMODELER_DIR=c:\app\pgmodeler
|
||||
|
||||
setlocal
|
||||
cd %PGMODELER_DIR%
|
||||
pgmodeler-cli -if %BASEDIR%/account.dbm -ef -do -of %BASEDIR%/account.sql
|
||||
|
||||
pause
|
||||
@ -1,2 +0,0 @@
|
||||
\i 01_accounts.sql
|
||||
\i 02_templates.sql
|
||||
10
account/sql/drop_all_tables.sql
Normal file
10
account/sql/drop_all_tables.sql
Normal file
@ -0,0 +1,10 @@
|
||||
DROP TABLE IF EXISTS config CASCADE;
|
||||
DROP TABLE IF EXISTS account_role CASCADE;
|
||||
DROP TABLE IF EXISTS account_login CASCADE;
|
||||
DROP TABLE IF EXISTS account CASCADE;
|
||||
DROP TABLE IF EXISTS role_permission CASCADE;
|
||||
DROP TABLE IF EXISTS application_role CASCADE;
|
||||
DROP TABLE IF EXISTS application_permission CASCADE;
|
||||
DROP TABLE IF EXISTS application CASCADE;
|
||||
DROP TABLE IF EXISTS account_history CASCADE;
|
||||
DROP TABLE IF EXISTS mail_template CASCADE;
|
||||
5
account/sql/restoreAccount.bat
Normal file
5
account/sql/restoreAccount.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET BACKUPFOLDER=%~dp0
|
||||
"C:\Program Files\PostgreSQL\10\bin\psql.exe" -U jomu --set ON_ERROR_STOP=on account < %BACKUPFOLDER%\drop_all_tables.sql
|
||||
"C:\Program Files\PostgreSQL\10\bin\psql.exe" -U jomu --set ON_ERROR_STOP=on account < %BACKUPFOLDER%\restore.dump
|
||||
pause;
|
||||
5
account/sql/restoreAccountTest.bat
Normal file
5
account/sql/restoreAccountTest.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
SET BACKUPFOLDER=%~dp0
|
||||
"C:\Program Files\PostgreSQL\10\bin\psql.exe" -U jomu --set ON_ERROR_STOP=on account_test < %BACKUPFOLDER%\drop_all_tables.sql
|
||||
"C:\Program Files\PostgreSQL\10\bin\psql.exe" -U jomu --set ON_ERROR_STOP=on account_test < %BACKUPFOLDER%\restore.dump
|
||||
pause;
|
||||
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 2016 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.business;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.Account;
|
||||
import de.muehlencord.shared.account.util.ApplicationPU;
|
||||
import de.muehlencord.shared.account.util.Updateable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.metamodel.IdentifiableType;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.transaction.Transactional;
|
||||
import org.apache.shiro.util.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractController<T> {
|
||||
|
||||
@Inject
|
||||
@ApplicationPU
|
||||
protected EntityManager em;
|
||||
|
||||
@Inject
|
||||
protected Account account;
|
||||
|
||||
private final Class<T> entityClass;
|
||||
|
||||
public AbstractController(Class<T> clazz) {
|
||||
this.entityClass = clazz;
|
||||
}
|
||||
|
||||
protected Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, Map<String, Object> filters) {
|
||||
return getFilterCondition(cb, root, filters, null);
|
||||
}
|
||||
|
||||
protected Predicate getFilterCondition(CriteriaBuilder cb, Root<T> root, Map<String, Object> filters, Map<String, Object> excludeFilters) {
|
||||
Predicate filterCondition = null;
|
||||
filterCondition = getFilterCondition(filterCondition, cb, root, filters, true);
|
||||
filterCondition = getFilterCondition(filterCondition, cb, root, excludeFilters, false);
|
||||
return filterCondition;
|
||||
}
|
||||
|
||||
protected Predicate addFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate addCondition) {
|
||||
if (addCondition == null) {
|
||||
return filterCondition;
|
||||
}
|
||||
if (filterCondition == null) {
|
||||
filterCondition = addCondition;
|
||||
} else {
|
||||
filterCondition = cb.and(filterCondition, addCondition);
|
||||
}
|
||||
return filterCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* extends the given filterCondition by the addtional filters
|
||||
*
|
||||
* @param filterCondition the current filter condition
|
||||
* @param cb the criteria builder to use
|
||||
* @param root the root of the object to search for
|
||||
* @param filters the filters to apply
|
||||
* @param include if set to true, the filter is used as include filter
|
||||
* (equals, in). If set to false, the filter is inverted and used as exclude
|
||||
* filter (not equals, not in etc)
|
||||
* @return
|
||||
*/
|
||||
protected Predicate getFilterCondition(Predicate filterCondition, CriteriaBuilder cb, Root<T> root, Map<String, Object> filters, boolean include) {
|
||||
String wildCard = "%";
|
||||
if (filters != null) {
|
||||
for (Map.Entry<String, Object> filter : filters.entrySet()) {
|
||||
if (!"".equals(filter.getValue())) {
|
||||
Path<String> path = getPathElement(root, filter);
|
||||
|
||||
// check for differnt types
|
||||
// 1st String, either from Enum Status or from other free text string
|
||||
if (String.class.equals(filter.getValue().getClass())) {
|
||||
switch (filter.getKey()) {
|
||||
default:
|
||||
String filterValue = filter.getValue().toString();
|
||||
Predicate predicate;
|
||||
if (filterValue.equals("{NULL}")) {
|
||||
if (include) {
|
||||
predicate = cb.isNull(path);
|
||||
} else {
|
||||
predicate = cb.isNotNull(path);
|
||||
}
|
||||
} else {
|
||||
filterValue = filterValue.trim();
|
||||
filterValue = filterValue.replace("?", "_");
|
||||
filterValue = filterValue.replace("*", "%");
|
||||
|
||||
String[] values = filterValue.split("\\s+"); // split by whitespaces
|
||||
Predicate[] partSearchPredicates = new Predicate[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
String value = wildCard + values[i] + wildCard;
|
||||
if (include) {
|
||||
partSearchPredicates[i] = cb.like(cb.upper(path), value.toUpperCase(Locale.US), '\\');
|
||||
} else {
|
||||
partSearchPredicates[i] = cb.notLike(cb.upper(path), value.toUpperCase(Locale.US), '\\');
|
||||
}
|
||||
}
|
||||
predicate = cb.and(partSearchPredicates); // all parts must be available
|
||||
}
|
||||
filterCondition = addFilterCondition(cb, filterCondition, predicate);
|
||||
}
|
||||
} // 2nd for arrays, received from e.g. project selections
|
||||
else if (filter.getValue().getClass().isArray()) {
|
||||
Predicate condition = null;
|
||||
Object[] values = (Object[]) filter.getValue();
|
||||
if (values.length > 0) {
|
||||
for (Object value : values) {
|
||||
if (include) {
|
||||
Predicate equalPredicate = cb.equal(path, value);
|
||||
if (condition == null) {
|
||||
condition = equalPredicate;
|
||||
} else {
|
||||
condition = cb.or(condition, equalPredicate);
|
||||
}
|
||||
} else {
|
||||
Predicate equalPredicate = cb.notEqual(path, value);
|
||||
if (condition == null) {
|
||||
condition = equalPredicate;
|
||||
} else {
|
||||
condition = cb.and(condition, equalPredicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
filterCondition = addFilterCondition(cb, filterCondition, condition);
|
||||
}
|
||||
} else { // at last object comparison
|
||||
if (include) {
|
||||
filterCondition = addFilterCondition(cb, filterCondition, cb.equal(path, filter.getValue()));
|
||||
} else {
|
||||
filterCondition = addFilterCondition(cb, filterCondition, cb.notEqual(path, filter.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return filterCondition;
|
||||
}
|
||||
|
||||
private Path<String> getPathElement(Root<T> root, Map.Entry<String, Object> filter) {
|
||||
String[] pathElements = StringUtils.split(filter.getKey(), '.');
|
||||
Path<String> path = null;
|
||||
for (String element : pathElements) {
|
||||
if (path == null) {
|
||||
path = root.get(element);
|
||||
} else {
|
||||
path = path.get(element);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public void applyUpdateableChanges(Updateable updateable, boolean onCreate) throws ControllerException {
|
||||
if (onCreate) {
|
||||
updateable.setCreatedBy(account.getUsername());
|
||||
updateable.setCreatedOn(new Date());
|
||||
}
|
||||
updateable.setLastUpdatedBy(account.getUsername());
|
||||
updateable.setLastUpdatedOn(new Date());
|
||||
}
|
||||
|
||||
public T attach(T entity) {
|
||||
return em.merge(entity);
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public T create(T entity) throws ControllerException {
|
||||
if (Updateable.class.isAssignableFrom(entity.getClass())) {
|
||||
Updateable updateable = (Updateable) entity;
|
||||
applyUpdateableChanges(updateable, true);
|
||||
}
|
||||
em.persist(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public T update(T entity) throws ControllerException {
|
||||
if (Updateable.class.isAssignableFrom(entity.getClass())) {
|
||||
Updateable updateable = (Updateable) entity;
|
||||
applyUpdateableChanges(updateable, false);
|
||||
}
|
||||
return em.merge(entity);
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public void delete(T entity) throws ControllerException {
|
||||
em.remove(attach(entity));
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
public T find(Object id) {
|
||||
return em.find(entityClass, id);
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
public List<T> findAll() {
|
||||
return findAll(new ArrayList<>());
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
public List<T> findAll(String... orderFields) {
|
||||
return findAll(Arrays.asList(orderFields));
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
public List<T> findAll(List<String> orderFields) {
|
||||
final CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
final CriteriaQuery<T> criteria = cb.createQuery(entityClass);
|
||||
|
||||
final Root<T> r = criteria.from(entityClass);
|
||||
|
||||
List<Order> orderList = new ArrayList<>();
|
||||
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
|
||||
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
public List<T> find(Map<String, Object> filters, List<String> orderFields) {
|
||||
final CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
final CriteriaQuery<T> criteria = cb.createQuery(entityClass);
|
||||
|
||||
final Root<T> r = criteria.from(entityClass);
|
||||
Predicate filterCondition = getFilterCondition(cb, r, filters);
|
||||
if (filterCondition != null) {
|
||||
criteria.where(filterCondition);
|
||||
}
|
||||
List<Order> orderList = new ArrayList<>();
|
||||
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field))));
|
||||
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns null, if the list is empty or null itself. Returns the one
|
||||
* element if there is exactly one element in the list. Otherwise an
|
||||
* exception is thrown
|
||||
*
|
||||
* @param resultList
|
||||
* @return
|
||||
* @throws ControllerException
|
||||
*/
|
||||
public T ensureSingleElement(List<T> resultList) throws ControllerException {
|
||||
if ((resultList == null) || (resultList.isEmpty())) {
|
||||
return null;
|
||||
}
|
||||
if (resultList.size() > 1) {
|
||||
throw new ControllerException(ControllerException.CAUSE_TOO_MANY_ROWS, "More than one element found in list - expected exactly one");
|
||||
}
|
||||
return resultList.get(0);
|
||||
}
|
||||
|
||||
private <T> SingularAttribute<? super T, ?> getIdAttribute() {
|
||||
Metamodel m = em.getEntityManagerFactory().getMetamodel();
|
||||
IdentifiableType<T> of = (IdentifiableType<T>) m.managedType(entityClass);
|
||||
// of.getDeclaredId(entityClass).getJavaMember().
|
||||
return of.getId(of.getIdType().getJavaType());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.business;
|
||||
|
||||
import de.muehlencord.shared.account.util.EndDateable;
|
||||
import de.muehlencord.shared.account.util.Updateable;
|
||||
import de.muehlencord.shared.util.DateUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
* @param <T> an entity which needs to extend EndDateable
|
||||
*/
|
||||
public abstract class AbstractEnddateableController<T extends EndDateable<T>> extends AbstractController<T> {
|
||||
|
||||
private final Class<T> endDateableClass;
|
||||
|
||||
public AbstractEnddateableController(Class<T> clazz) {
|
||||
super(clazz);
|
||||
this.endDateableClass = clazz;
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@Override
|
||||
public void delete(T entity) throws ControllerException {
|
||||
T entityToUpdate = attach(entity);
|
||||
if (Updateable.class.isAssignableFrom(entityToUpdate.getClass())) {
|
||||
Updateable updateable = (Updateable) entityToUpdate;
|
||||
applyUpdateableChanges(updateable, false);
|
||||
}
|
||||
entityToUpdate.setValidTo(DateUtil.getCurrentTimeInUTC());
|
||||
em.merge(entityToUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public T create(T entity) throws ControllerException {
|
||||
entity.setValidFrom(DateUtil.getCurrentTimeInUTC());
|
||||
return super.create(entity);
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@Override
|
||||
public T update(T entity) throws ControllerException {
|
||||
T newEntity = entity.cloneEndDateable();
|
||||
delete(entity);
|
||||
return create (newEntity);
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
@Override
|
||||
public List<T> findAll(List<String> orderFields) {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
final CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
final CriteriaQuery<T> criteria = cb.createQuery(endDateableClass);
|
||||
final Root<T> root = criteria.from(endDateableClass);
|
||||
|
||||
Predicate alreadyValid = cb.lessThanOrEqualTo(root.get("validFrom"), now);
|
||||
|
||||
Predicate validToNotSet = cb.isNull(root.get("validTo"));
|
||||
Predicate isBeforeValidTo = cb.greaterThanOrEqualTo(root.get("validTo"), now);
|
||||
Predicate stillValid = cb.or (isBeforeValidTo, validToNotSet);
|
||||
Predicate isValid = cb.and(alreadyValid, stillValid);
|
||||
criteria.where(isValid);
|
||||
|
||||
List<Order> orderList = new ArrayList<>();
|
||||
orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(root.get(field))));
|
||||
final TypedQuery<T> query = em.createQuery(criteria.orderBy(orderList));
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
package de.muehlencord.shared.account.business;
|
||||
|
||||
import de.muehlencord.shared.account.entity.ConfigEntity;
|
||||
import java.io.Serializable;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.ejb.Singleton;
|
||||
import javax.ejb.Startup;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
@Singleton
|
||||
@Startup
|
||||
public class ConfigService implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3195224653632853003L;
|
||||
|
||||
@Inject
|
||||
EntityManager em;
|
||||
|
||||
private String storagePath = null;
|
||||
private int maxFailedLogins = 5;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
ConfigEntity configEntity = em.find(ConfigEntity.class, "storage.path");
|
||||
if (configEntity != null) {
|
||||
this.storagePath = configEntity.getConfigValue();
|
||||
}
|
||||
configEntity = em.find(ConfigEntity.class, "account.maxFailedLogins");
|
||||
if (configEntity != null) {
|
||||
this.maxFailedLogins = Integer.parseInt(configEntity.getConfigValue());
|
||||
}
|
||||
}
|
||||
|
||||
public String getConfigValue(String configKey) {
|
||||
ConfigEntity configEntity = em.find(ConfigEntity.class, configKey);
|
||||
return (configEntity == null ? null : configEntity.getConfigValue());
|
||||
}
|
||||
|
||||
/* *** getter *** */
|
||||
// FIXME remove, this is application specific
|
||||
public String getStoragePath() {
|
||||
return storagePath;
|
||||
}
|
||||
|
||||
public int getMaxFailedLogins() {
|
||||
return maxFailedLogins;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2019 joern.muehlencord.
|
||||
*
|
||||
* 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.business;
|
||||
|
||||
import javax.ejb.ApplicationException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
@ApplicationException(rollback=true)
|
||||
public class ControllerException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5190280225284514859L;
|
||||
public static final int CAUSE_ALREADY_EXISTS = 1;
|
||||
public static final int CAUSE_NOT_FOUND = 2;
|
||||
public static final int CAUSE_CANNOT_PERSIST = 3;
|
||||
public static final int CAUSE_TOO_MANY_ROWS = 4;
|
||||
public static final int CAUSE_CANNOT_DELETE = 5;
|
||||
|
||||
private final int causeCode;
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>ControllerException</code> without detail
|
||||
* message.
|
||||
*
|
||||
* @param cause the reason code
|
||||
* @param message an explanation
|
||||
*/
|
||||
public ControllerException(int cause, String message) {
|
||||
super(message);
|
||||
this.causeCode = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param causeCode
|
||||
* @param message
|
||||
* @param cause
|
||||
*/
|
||||
public ControllerException(int causeCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.causeCode = causeCode;
|
||||
}
|
||||
|
||||
public int getCauseCode() {
|
||||
return causeCode;
|
||||
}
|
||||
}
|
||||
@ -1,276 +0,0 @@
|
||||
package de.muehlencord.shared.account.business.account;
|
||||
|
||||
import de.muehlencord.shared.account.business.ConfigService;
|
||||
import de.muehlencord.shared.account.business.mail.MailException;
|
||||
import de.muehlencord.shared.account.business.mail.MailService;
|
||||
import de.muehlencord.shared.account.entity.AccountEntity;
|
||||
import de.muehlencord.shared.account.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.account.util.SecurityUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import javax.ejb.EJB;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
@Stateless
|
||||
public class AccountControl implements Serializable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountControl.class.getName());
|
||||
private static final long serialVersionUID = 3424816272598108101L;
|
||||
|
||||
@EJB
|
||||
private ConfigService configService;
|
||||
|
||||
@EJB
|
||||
private MailService mailService;
|
||||
|
||||
@Inject
|
||||
EntityManager em;
|
||||
|
||||
public List<AccountEntity> getAccounts() {
|
||||
Query query = em.createQuery("SELECT a FROM AccountEntity a WHERE a.status <> :status", AccountEntity.class);
|
||||
query.setParameter("status", AccountStatus.DISABLED.name());
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
public AccountEntity getAccountEntity(String userName, boolean loadRoles) {
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
queryBuilder.append("SELECT a FROM AccountEntity a ");
|
||||
if (loadRoles) {
|
||||
queryBuilder.append("JOIN FETCH a.applicationRoleList ");
|
||||
}
|
||||
queryBuilder.append("WHERE a.username = :username");
|
||||
Query query = em.createQuery(queryBuilder.toString());
|
||||
query.setParameter("username", userName);
|
||||
try {
|
||||
return (AccountEntity) query.getSingleResult();
|
||||
} catch (NoResultException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO add role names from application because only application can know how its roles are named
|
||||
public AccountEntity saveAccount(AccountEntity account, boolean isAdmin) {
|
||||
Date now = new Date(); // Todo now in UTC
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
String currentLoggedInUser = currentUser.getPrincipal().toString();
|
||||
|
||||
account.setLastUpdatedBy(currentLoggedInUser);
|
||||
account.setLastUpdatedOn(now);
|
||||
|
||||
boolean newAccount = (account.getCreatedOn() == null);
|
||||
|
||||
// new account
|
||||
if (newAccount) {
|
||||
account.setCreatedOn(now);
|
||||
account.setCreatedBy(currentLoggedInUser);
|
||||
|
||||
// set default random password, user has to get password via lost passwort option afterwards
|
||||
String randomPassword = RandomStringUtils.random(20, true, true);
|
||||
String hashedPassword = SecurityUtil.createPassword(randomPassword);
|
||||
account.setAccountPassword(hashedPassword);
|
||||
em.persist(account);
|
||||
} else {
|
||||
em.merge(account);
|
||||
|
||||
// reload account from db and join roles
|
||||
account = getAccountEntity(account.getUsername(), true);
|
||||
}
|
||||
|
||||
// load Admin or User role from database
|
||||
String roleName = (isAdmin ? "Admin" : "User");
|
||||
Query roleQuery = em.createNamedQuery("ApplicationRoleEntity.findByRoleName");
|
||||
roleQuery.setParameter("roleName", roleName);
|
||||
ApplicationRoleEntity role = (ApplicationRoleEntity) roleQuery.getSingleResult();
|
||||
|
||||
if (role != null) {
|
||||
// add new user add required role
|
||||
// do not request based on newUser variable; this way existing users with missing role (for whatever reason)
|
||||
// will be fixed automatically
|
||||
if (account.getApplicationRoleList() == null || account.getApplicationRoleList().isEmpty()) {
|
||||
account.setApplicationRoleList(new ArrayList<>());
|
||||
account.getApplicationRoleList().add(role);
|
||||
em.merge(account);
|
||||
LOGGER.info("Added role " + roleName + " to user " + account.getUsername());
|
||||
|
||||
} else if (!account.getApplicationRoleList().get(0).equals(role)) {
|
||||
// change role from User to Admin and vice versa
|
||||
// user already exists, has existing role
|
||||
// check if existing role is different from current role and change it
|
||||
// be carefull: this only works as long as a user has exactly one role!
|
||||
// he is either User or Admin
|
||||
// TODO add "UserRole" to every user, make this default Role configurable
|
||||
// TODO add AdminRole in addtion if needed
|
||||
account.getApplicationRoleList().remove(0);
|
||||
account.getApplicationRoleList().add(role);
|
||||
em.merge(account);
|
||||
LOGGER.info("Switched role of user " + account.getUsername() + " to " + roleName);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
public void deleteAccount(AccountEntity account) throws AccountException {
|
||||
Date now = new Date(); // Todo now in UTC
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
String currentUserName = currentUser.getPrincipal().toString();
|
||||
|
||||
if (account.getUsername().equals(currentUserName)) {
|
||||
throw new AccountException("Cannot delete own account");
|
||||
} else {
|
||||
account.setStatus(AccountStatus.DISABLED.name());
|
||||
account.setLastUpdatedBy(currentUserName);
|
||||
account.setLastUpdatedOn(now);
|
||||
em.merge(account);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean initPasswordReset(String userName) {
|
||||
try {
|
||||
AccountEntity account = getAccountEntity(userName, false);
|
||||
if (account == null) {
|
||||
LOGGER.warn("Account with name " + userName + " not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getStatus().equals(AccountStatus.BLOCKED.name())) {
|
||||
LOGGER.warn("Account " + userName + " is locked, cannot initialize password reset");
|
||||
return false;
|
||||
}
|
||||
|
||||
String randomString = RandomStringUtils.random(40, true, true);
|
||||
|
||||
Date validTo = new Date(); // TODO now in UTC
|
||||
validTo = new Date(validTo.getTime() + 1000 * 600); // 10 minutes to react
|
||||
|
||||
account.setPasswordResetHash(randomString);
|
||||
account.setPasswordResetOngoing(true);
|
||||
account.setPasswordResetValidTo(validTo);
|
||||
|
||||
mailService.sendPasswortResetStartEmail(account, randomString);
|
||||
|
||||
em.merge(account);
|
||||
return true;
|
||||
} catch (MailException ex) {
|
||||
LOGGER.error("Error while sending password reset mail. " + ex.toString());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Error while sending password reset mail.", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean resetPassword(String userName, String newPassword, String resetPasswordToken) {
|
||||
AccountEntity account = getAccountEntity(userName, false);
|
||||
|
||||
if (account == null) {
|
||||
LOGGER.warn("Error while resetting password, no account with username " + userName + " found");
|
||||
// TODO add extra logging for intrusion protection system like fail2ban
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getPasswordResetOngoing() && (account.getPasswordResetHash() != null) && (account.getPasswordResetValidTo() != null)) {
|
||||
Date now = new Date(); // TODO now in UTC
|
||||
String storedHash = account.getPasswordResetHash().trim();
|
||||
if (account.getPasswordResetValidTo().after(now)) {
|
||||
if (storedHash.equals(resetPasswordToken)) {
|
||||
// everything ok, reset password
|
||||
executePasswordReset(account, newPassword);
|
||||
LOGGER.info("Updated password for user " + userName);
|
||||
return true;
|
||||
} else {
|
||||
// token is not valid, refuse to change password
|
||||
LOGGER.warn("Trying to reset password for user " + userName + " but wrong token " + resetPasswordToken + " provided");
|
||||
addLoginError(account);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// password reset token no longer valid
|
||||
LOGGER.warn("Trying to reset password for user " + userName + " but token is no longer valid");
|
||||
addLoginError(account);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// user is not is password reset mode
|
||||
LOGGER.warn("Trying to reset password for user " + userName + " but password reset was not requested");
|
||||
addLoginError(account);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void executePasswordReset(AccountEntity account, String newPassword) {
|
||||
Date now = new Date(); // TODO now in UTC
|
||||
|
||||
String hashedPassword = SecurityUtil.createPassword(newPassword);
|
||||
account.setAccountPassword(hashedPassword);
|
||||
|
||||
account.setPasswordResetOngoing(false);
|
||||
account.setPasswordResetHash(null);
|
||||
account.setPasswordResetValidTo(null);
|
||||
|
||||
account.setLastUpdatedBy(account.getUsername());
|
||||
account.setLastUpdatedOn(now);
|
||||
em.merge(account);
|
||||
|
||||
}
|
||||
|
||||
public void updateLogin(AccountEntity account) {
|
||||
Date now = new Date(); // TODO now in UTC
|
||||
// a scucessful login ends a password reset procedure
|
||||
if (account.getPasswordResetOngoing()) {
|
||||
account.setPasswordResetOngoing(false);
|
||||
account.setPasswordResetHash(null);
|
||||
account.setPasswordResetValidTo(null);
|
||||
account.setLastUpdatedOn(now);
|
||||
account.setLastUpdatedBy(account.getUsername());
|
||||
}
|
||||
|
||||
account.setLastLogin(now);
|
||||
account.setFailureCount(0);
|
||||
account.setStatus(AccountStatus.NORMAL.name());
|
||||
|
||||
em.merge(account);
|
||||
}
|
||||
|
||||
public void addLoginError(AccountEntity account) {
|
||||
Date now = new Date(); // TODO now in UTC
|
||||
account.setLastFailedLogin(now);
|
||||
account.setFailureCount(account.getFailureCount() + 1);
|
||||
|
||||
int maxFailedLogins = configService.getMaxFailedLogins();
|
||||
if ((account.getFailureCount() >= maxFailedLogins) && (!account.getStatus().equals("LOCKED"))) { // TOD add status enum
|
||||
// max failed logins reached, disabling user
|
||||
LOGGER.info("Locking account " + account.getUsername() + " due to " + account.getFailureCount() + " failed logins");
|
||||
account.setStatus(AccountStatus.BLOCKED.name());
|
||||
}
|
||||
|
||||
// on a failed login request, disable password reset
|
||||
account.setPasswordResetOngoing(false);
|
||||
account.setPasswordResetHash(null);
|
||||
account.setPasswordResetValidTo(null);
|
||||
|
||||
account.setLastUpdatedBy("system");
|
||||
account.setLastUpdatedOn(now);
|
||||
em.merge(account);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package de.muehlencord.shared.account.business.account;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Raimund
|
||||
*/
|
||||
public class AccountException extends Exception {
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>AccountException</code> without detail
|
||||
* message.
|
||||
*/
|
||||
public AccountException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>AccountException</code> with the
|
||||
* specified detail message.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public AccountException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
AccountException(String entity_updated__deleted_please_reload, boolean b) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package de.muehlencord.shared.account.business.account;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
public enum AccountStatus {
|
||||
|
||||
NEW, // account is created but never used
|
||||
NORMAL, // normal account, at least on login, neither blocked or disabled
|
||||
BLOCKED, // account is blocked after too many login failures or other security related events
|
||||
DISABLED; // account is disabled and cannot be used anymore
|
||||
|
||||
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
package de.muehlencord.shared.account.business.account;
|
||||
|
||||
import de.muehlencord.shared.account.entity.ApplicationPermissionEntity;
|
||||
import java.io.Serializable;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Stateless
|
||||
public class ApplicationPermissionControl implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3761100587901739481L;
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager em;
|
||||
|
||||
public List getApplicationPermissions() {
|
||||
List<ApplicationPermissionEntity> permissionList = em.createNamedQuery("ApplicationPermissionEntity.findAll").getResultList();
|
||||
if (permissionList == null) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return permissionList;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void createOrUpdate(String name, String description) {
|
||||
ApplicationPermissionEntity permission = findByName(name);
|
||||
if (permission == null) {
|
||||
permission = new ApplicationPermissionEntity(name, description);
|
||||
em.persist(permission);
|
||||
} else {
|
||||
permission.setPermissionDescription(description);
|
||||
em.merge(permission);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationPermissionEntity existingPermission = attach(permission);
|
||||
em.remove(existingPermission);
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity attach(ApplicationPermissionEntity permission) throws AccountException {
|
||||
try {
|
||||
return em.merge(permission);
|
||||
} catch (OptimisticLockException ex) {
|
||||
throw new AccountException("Entity updated / deleted, please reload", true);
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationPermissionEntity findByName(String name) {
|
||||
Query query = em.createNamedQuery("ApplicationPermissionEntity.findByPermissionName");
|
||||
query.setParameter("permissionName", name);
|
||||
List<ApplicationPermissionEntity> permissions = query.getResultList();
|
||||
if ((permissions == null) || (permissions.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return permissions.get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package de.muehlencord.shared.account.business.account;
|
||||
|
||||
import de.muehlencord.shared.account.entity.ApplicationPermissionEntity;
|
||||
import de.muehlencord.shared.account.entity.ApplicationRoleEntity;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.ejb.EJB;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Stateless
|
||||
public class ApplicationRoleControl implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5962478269550134748L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRoleControl.class);
|
||||
|
||||
@EJB
|
||||
ApplicationPermissionControl applicationPermissionControl;
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager em;
|
||||
|
||||
public List<ApplicationRoleEntity> getAllRoles() {
|
||||
Query query = em.createNamedQuery("ApplicationRoleEntity.findAll");
|
||||
|
||||
List<ApplicationRoleEntity> roles = query.getResultList();
|
||||
if (roles == null) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void createOrUpdate(String name, String description) {
|
||||
ApplicationRoleEntity role = findByName(name);
|
||||
if (role == null) {
|
||||
role = new ApplicationRoleEntity(name, description);
|
||||
em.persist(role);
|
||||
} else {
|
||||
role.setRoleDescription(description);
|
||||
em.merge(role);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(ApplicationRoleEntity permission) throws AccountException {
|
||||
ApplicationRoleEntity existingPermission = attach(permission);
|
||||
em.remove(existingPermission);
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity attach(ApplicationRoleEntity permission) throws AccountException {
|
||||
try {
|
||||
return em.merge(permission);
|
||||
} catch (OptimisticLockException ex) {
|
||||
throw new AccountException("Entity updated / deleted, please reload", true);
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationRoleEntity findByName(String name) {
|
||||
Query query = em.createNamedQuery("ApplicationRoleEntity.findByRoleName");
|
||||
query.setParameter("roleName", name);
|
||||
List<ApplicationRoleEntity> permissions = query.getResultList();
|
||||
if ((permissions == null) || (permissions.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return permissions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ApplicationPermissionEntity> getRolePermissions(ApplicationRoleEntity role) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = em.find(ApplicationRoleEntity.class, role.getId());
|
||||
List<ApplicationPermissionEntity> permissions = existingRole.getApplicationPermissionList();
|
||||
permissions.size(); // force list to load
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public List<ApplicationPermissionEntity> getNotAssignedApplicationPermissions(ApplicationRoleEntity role) {
|
||||
try {
|
||||
List<ApplicationPermissionEntity> rolePermissions = getRolePermissions(role);
|
||||
List<ApplicationPermissionEntity> allPermssions = applicationPermissionControl.getApplicationPermissions();
|
||||
|
||||
List<ApplicationPermissionEntity> missingPermissions = new ArrayList<>();
|
||||
allPermssions.stream().filter((perm) -> (!rolePermissions.contains(perm))).forEachOrdered((perm) -> {
|
||||
missingPermissions.add(perm);
|
||||
});
|
||||
return missingPermissions;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(ex.toString(), ex);
|
||||
} else {
|
||||
LOGGER.debug(ex.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void addPermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = attach(role);
|
||||
if (existingRole.getApplicationPermissionList() == null) {
|
||||
existingRole.setApplicationPermissionList(new ArrayList<>());
|
||||
}
|
||||
existingRole.getApplicationPermissionList().add(permission);
|
||||
em.merge(role);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removePermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = attach(role);
|
||||
if ((existingRole.getApplicationPermissionList() != null) && (existingRole.getApplicationPermissionList().contains(permission))) {
|
||||
existingRole.getApplicationPermissionList().remove(permission);
|
||||
}
|
||||
em.merge(role);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.util.Permission;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public enum AccountPermissions implements Permission {
|
||||
|
||||
ACCOUNT_ADD("account:add", "Allow to create a new account"),
|
||||
ACCOUNT_EDIT ("account:edit", "Allow to edit an existing account"),
|
||||
ACCOUNT_DELETE("account:delete", "Allow to delete an existing account"),
|
||||
ACCOUNT_LOGIN_ADD ("account:login:add", "Allow to create a login for a user"),
|
||||
ACCOUNT_LOGIN_EDIT ("account:login:edit", "Allow to change a login for a user"),
|
||||
ACCOUNT_LOGIN_DELETE ("account:login:delete", "Allow to delete a login for a user");
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
||||
private AccountPermissions(String permissionName, String permissionDesc) {
|
||||
this.name = permissionName;
|
||||
this.description = permissionDesc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.control.AccountControl;
|
||||
import de.muehlencord.shared.account.business.account.entity.Account;
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.annotation.ManagedBean;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.ejb.EJB;
|
||||
import javax.enterprise.context.SessionScoped;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.faces.context.FacesContext;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ManagedBean
|
||||
@SessionScoped
|
||||
public class AccountProducer implements Serializable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountProducer.class);
|
||||
private static final long serialVersionUID = -3806204732038165311L;
|
||||
private final Map<String, Object> objectMap = new ConcurrentHashMap<>();
|
||||
|
||||
@EJB
|
||||
AccountControl accountController;
|
||||
|
||||
private Account account = null;
|
||||
private Locale locale = null;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
FacesContext currentInstance = FacesContext.getCurrentInstance();
|
||||
if (currentInstance == null) {
|
||||
locale = Locale.ENGLISH;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Using default locale {}", locale);
|
||||
}
|
||||
|
||||
} else {
|
||||
locale = currentInstance.getExternalContext().getRequestLocale();
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Using browser locale {}", locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Produces
|
||||
public Account getAccount() {
|
||||
String accountName;
|
||||
if (account == null) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
if (subject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((subject.isAuthenticated() == false) && (subject.isRemembered() == false)) {
|
||||
return null;
|
||||
} else {
|
||||
accountName = subject.getPrincipal().toString();
|
||||
}
|
||||
account = accountController.getAccountEntity(accountName, true);
|
||||
// TODO introduce locale support to account and switch to pre-defined locale if set
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
public <T> T getValue(String key, Class<T> clazz) {
|
||||
if (objectMap.containsKey(key)) {
|
||||
Object obj = objectMap.get(key);
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return clazz.cast(obj);
|
||||
} catch (ClassCastException ex) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(String key, Object obj) {
|
||||
objectMap.put(key, obj);
|
||||
}
|
||||
|
||||
@Produces
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.boundary;
|
||||
|
||||
import de.muehlencord.shared.jeeutil.restexfw.APIError;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
public enum ApiKeyError implements APIError {
|
||||
|
||||
JWT_NO_TOKEN(Response.Status.BAD_REQUEST, "1000", "jwt_no_token"),
|
||||
JWT_TOKEN_INVALID (Response.Status.FORBIDDEN, "1001", "jwt_token_invalid");
|
||||
|
||||
private final Response.Status status;
|
||||
private final String errorCode;
|
||||
private final String messageKey;
|
||||
|
||||
private ApiKeyError(Response.Status status, String errorCode, String messageKey) {
|
||||
this.status = status;
|
||||
this.errorCode = errorCode;
|
||||
this.messageKey = messageKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorCode() {
|
||||
return this.errorCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageKey() {
|
||||
return this.messageKey;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.boundary;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public class ApiKeyException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -8017749942111814929L;
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>ApiKeyException</code> without detail message.
|
||||
*/
|
||||
public ApiKeyException() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>ApiKeyException</code> with the specified detail message.
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public ApiKeyException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>ApiKeyException</code> with the specified detail message and root cause.
|
||||
* @param msg the detail message.
|
||||
* @param th the root cause
|
||||
*/
|
||||
public ApiKeyException(String msg, Throwable th) {
|
||||
super(msg,th);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.business.ControllerException;
|
||||
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.ApiKeyEntity;
|
||||
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.entity.ConfigException;
|
||||
import de.muehlencord.shared.account.dao.ApiKeyObject;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import de.muehlencord.shared.jeeutil.jwt.JWTDecoder;
|
||||
import de.muehlencord.shared.jeeutil.jwt.JWTEncoder;
|
||||
import de.muehlencord.shared.jeeutil.jwt.JWTException;
|
||||
import de.muehlencord.shared.util.DateUtil;
|
||||
import de.muehlencord.shared.util.StringUtil;
|
||||
import java.io.Serializable;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
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 org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Stateless
|
||||
public class ApiKeyService implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6981864888118320228L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApiKeyService.class);
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
@Inject
|
||||
AccountControl accountControl;
|
||||
|
||||
@Inject
|
||||
ConfigService configService;
|
||||
|
||||
private String password;
|
||||
private String issuer;
|
||||
private Short expirationInMinutes;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (configService == null) {
|
||||
password = null;
|
||||
issuer = null;
|
||||
} else {
|
||||
try {
|
||||
password = configService.getConfigValue("rest.password");
|
||||
issuer = configService.getConfigValue("rest.issuer");
|
||||
expirationInMinutes = Short.parseShort(configService.getConfigValue("rest.expiration_in_minutes", "120", true));
|
||||
} catch (ConfigException | NumberFormatException | ControllerException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(ex.toString(), ex);
|
||||
} else {
|
||||
LOGGER.error(ex.toString());
|
||||
}
|
||||
password = null;
|
||||
issuer = null;
|
||||
expirationInMinutes = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public ApiKeyEntity getApiKeyFromString(String encodedJWT) throws ApiKeyException {
|
||||
if (StringUtil.isEmpty(encodedJWT)) {
|
||||
throw new ApiKeyException("Must provide authorization information");
|
||||
}
|
||||
JWTObject jwt = getJWTObject(encodedJWT);
|
||||
Query query = em.createNamedQuery("ApiKeyEntity.findByApiKey");
|
||||
query.setParameter("apiKey", jwt.getUnqiueId());
|
||||
List<ApiKeyEntity> apiKeys = query.getResultList();
|
||||
if ((apiKeys == null) || (apiKeys.isEmpty())) {
|
||||
throw new ApiKeyException("ApiKey not found in database");
|
||||
}
|
||||
return apiKeys.get(0);
|
||||
}
|
||||
|
||||
public List<ApiKeyEntity> getUsersApiKeys(AccountEntity account, boolean onlyValid) {
|
||||
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
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<>();
|
||||
} else {
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ApiKeyEntity> getUsersApiKeys(String userName) {
|
||||
return getUsersApiKeys(accountControl.getAccountEntity(userName, false), false);
|
||||
}
|
||||
|
||||
public List<ApiKeyEntity> getValidUsersApiKeys(String userName) {
|
||||
return getUsersApiKeys(accountControl.getAccountEntity(userName, false), true);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public ApiKeyObject createNewApiKey(String userName) throws ApiKeyException {
|
||||
if (userName == null) {
|
||||
throw new ApiKeyException("Username must not be null");
|
||||
}
|
||||
return createNewApiKey(userName, expirationInMinutes);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public ApiKeyObject createNewApiKey(String userName, short expirationInMinutes) throws ApiKeyException {
|
||||
if ((password == null || issuer == null) || (userName == null)) {
|
||||
LOGGER.error("password, issuer or username not set in, please validate configuration");
|
||||
}
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
ZonedDateTime issuedOn = ZonedDateTime.ofInstant(now.toInstant(), ZoneId.of("UTC"));
|
||||
ZonedDateTime expiresOn = issuedOn.plusMinutes(expirationInMinutes);
|
||||
Date expiresOnDate = Date.from(expiresOn.toInstant());
|
||||
String apiKeyString = RandomStringUtils.randomAscii(50);
|
||||
|
||||
ApiKeyEntity apiKey = new ApiKeyEntity();
|
||||
apiKey.setAccount(accountControl.getAccountEntity(userName, false));
|
||||
apiKey.setApiKey(apiKeyString);
|
||||
apiKey.setIssuedOn(now);
|
||||
apiKey.setExpiresOn(expiresOnDate);
|
||||
apiKey.setExpiration(expirationInMinutes);
|
||||
|
||||
return getApiKeyObject(apiKey);
|
||||
}
|
||||
|
||||
public ApiKeyObject getApiKeyObject(ApiKeyEntity apiKey) throws ApiKeyException {
|
||||
if (apiKey == null) {
|
||||
throw new ApiKeyException("ApiKey must not be null");
|
||||
}
|
||||
ZonedDateTime issuedOn = ZonedDateTime.ofInstant(apiKey.getIssuedOn().toInstant(), ZoneId.of("UTC"));
|
||||
ZonedDateTime expiresOn = issuedOn.plusMinutes(expirationInMinutes);
|
||||
AccountEntity account = apiKey.getAccount();
|
||||
if (account == null) {
|
||||
throw new ApiKeyException("Account of apiKey must not be null");
|
||||
}
|
||||
String userName = account.getUsername();
|
||||
try {
|
||||
String jwtString = JWTEncoder.encode(password, issuer, issuedOn, userName, apiKey.getApiKey(), apiKey.getExpiration());
|
||||
em.persist(apiKey);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Created API key for {}, valid for {} minutes", userName, expirationInMinutes);
|
||||
}
|
||||
|
||||
ApiKeyObject apiKeyObject = new ApiKeyObject();
|
||||
apiKeyObject.setUserName(userName);
|
||||
apiKeyObject.setIssuedOn(Date.from(apiKey.getIssuedOn().toInstant()));
|
||||
apiKeyObject.setExpiresOn(Date.from(expiresOn.toInstant()));
|
||||
apiKeyObject.setAuthToken(jwtString);
|
||||
|
||||
return apiKeyObject;
|
||||
} catch (JWTException ex) {
|
||||
throw new ApiKeyException("Cannot create apiKey. Reason: " + ex.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean validateJWT(String encodedJWT) {
|
||||
JWTDecoder decoder = new JWTDecoder(password, issuer, encodedJWT);
|
||||
ApiKeyEntity validKey;
|
||||
try {
|
||||
validKey = getValidKey(decoder.getSubject(), decoder.getUniqueId(), encodedJWT);
|
||||
} catch (JWTException ex) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace(ex.toString(), ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return validKey != null;
|
||||
}
|
||||
|
||||
private ApiKeyEntity getValidKey(String userName, String apiKey, String authorizationHeader) throws JWTException {
|
||||
AccountEntity userAccount = accountControl.getAccountEntity(userName, false);
|
||||
if (userAccount == null) {
|
||||
throw new JWTException("AccountControl exception");
|
||||
}
|
||||
List<ApiKeyEntity> apiKeys = getUsersApiKeys(userAccount, true);
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Found {} keys for user {}", apiKeys.size(), userName);
|
||||
}
|
||||
|
||||
Iterator<ApiKeyEntity> it = apiKeys.iterator();
|
||||
ApiKeyEntity keyToLogout = null;
|
||||
while (keyToLogout == null && it.hasNext()) {
|
||||
ApiKeyEntity key = it.next();
|
||||
if (key.getApiKey().equals(apiKey)) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Found API key in database");
|
||||
}
|
||||
|
||||
ZonedDateTime issuedOn = ZonedDateTime.ofInstant(key.getIssuedOn().toInstant(), ZoneOffset.UTC);
|
||||
String testString = JWTEncoder.encode(password, issuer, issuedOn, key.getAccount().getUsername(), key.getApiKey(), key.getExpiration());
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Successfully created validation JWT for user {}", userName);
|
||||
}
|
||||
|
||||
if (authorizationHeader.equals(testString)) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Found valid key for user {}", userName);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("No valid key for user {} found", userName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getJWTFromApiKey(ApiKeyEntity apiKey) throws ApiKeyException {
|
||||
ZonedDateTime issuedAt = ZonedDateTime.ofInstant(apiKey.getIssuedOn().toInstant(), ZoneOffset.UTC);
|
||||
try {
|
||||
return JWTEncoder.encode(password, issuer, issuedAt, apiKey.getAccount().getUsername(), apiKey.getApiKey(), apiKey.getExpiration());
|
||||
} catch (JWTException ex) {
|
||||
throw new ApiKeyException("Cannot retrieve JWT from key. Reason: " + ex.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public JWTObject getJWTObject(String encodedJWT) {
|
||||
JWTDecoder decoder = new JWTDecoder(password, issuer, encodedJWT);
|
||||
JWTObject jwtObject = new JWTObject();
|
||||
jwtObject.setUserName(decoder.getSubject());
|
||||
jwtObject.setUnqiueId(decoder.getUniqueId());
|
||||
jwtObject.setValid(true);
|
||||
return jwtObject;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param apiKey
|
||||
* @deprecated use delete (jwtObject) instead
|
||||
*/
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@Deprecated
|
||||
public void delete(ApiKeyEntity apiKey) {
|
||||
em.remove(em.merge(apiKey));
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public void delete(String authorizationHeader) throws ApiKeyException {
|
||||
|
||||
JWTObject jwtObject = getJWTObject(authorizationHeader);
|
||||
if (jwtObject.isValid()) {
|
||||
String userName = jwtObject.getUserName();
|
||||
|
||||
ApiKeyEntity keyToLogout;
|
||||
try {
|
||||
keyToLogout = getValidKey(userName, jwtObject.getUnqiueId(), authorizationHeader);
|
||||
} catch (JWTException ex) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
keyToLogout = null;
|
||||
}
|
||||
|
||||
if (keyToLogout == null) {
|
||||
// no valid key found - must not happen, JWTVeryfingFIlter should have catched this
|
||||
// FIXME - add logging / handle this problem
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("No valid key found, probably user {} has already logged out", userName);
|
||||
}
|
||||
throw new ApiKeyException("No valid key found, probably user " + userName + " has already logged out");
|
||||
} else {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Found matching apiKey to logout");
|
||||
}
|
||||
em.remove(em.merge(keyToLogout));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Key deleted, user {} logged out from webservice", userName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ApiKeyException("Provided JWT is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.control;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountException;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountLoginEntity;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountStatus;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.account.business.config.boundary.ConfigService;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigException;
|
||||
import de.muehlencord.shared.account.business.instance.boundary.ApplicationPermissions;
|
||||
import de.muehlencord.shared.account.business.mail.boundary.MailService;
|
||||
import de.muehlencord.shared.account.business.mail.entity.MailException;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import de.muehlencord.shared.account.util.SecurityUtil;
|
||||
import de.muehlencord.shared.util.DateUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.ejb.EJB;
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
@Stateless
|
||||
public class AccountControl implements Serializable {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AccountControl.class.getName());
|
||||
private static final long serialVersionUID = 3424816272598108101L;
|
||||
|
||||
@EJB
|
||||
private MailService mailService;
|
||||
|
||||
@Inject
|
||||
private ApplicationEntity application;
|
||||
|
||||
@Inject
|
||||
ConfigService configService;
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
public List<AccountEntity> getAllAccounts(boolean includeDisabled) {
|
||||
List<AccountEntity> resultList;
|
||||
if (includeDisabled) {
|
||||
resultList = getAllAccounts();
|
||||
} else {
|
||||
resultList = getActiveAccounts();
|
||||
}
|
||||
|
||||
if (SecurityUtil.checkPermission(ApplicationPermissions.ACCOUNT_LIST)) {
|
||||
return resultList;
|
||||
} else {
|
||||
String currentUserName = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
return resultList.stream()
|
||||
.filter(account -> account.getAccountLogin() != null)
|
||||
.filter(account -> account.getUsername().equals(currentUserName))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a list of active accounts
|
||||
*
|
||||
* @return a list of active accounts
|
||||
*/
|
||||
private List<AccountEntity> getActiveAccounts() {
|
||||
Query query = em.createNamedQuery("AccountEntity.findActiveAccounts");
|
||||
query.setParameter("status", AccountStatus.DISABLED.name());
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a list of active accounts
|
||||
*
|
||||
* @return a list of active accounts
|
||||
*/
|
||||
private List<AccountEntity> getAllAccounts() {
|
||||
Query query = em.createNamedQuery("AccountEntity.findAll");
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
|
||||
public AccountEntity getAccountEntity(String userName, boolean loadRoles) {
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
queryBuilder.append("SELECT a FROM AccountEntity a ");
|
||||
if (loadRoles) {
|
||||
queryBuilder.append("LEFT JOIN FETCH a.applicationRoleList ");
|
||||
}
|
||||
queryBuilder.append("WHERE a.username = :username");
|
||||
Query query = em.createQuery(queryBuilder.toString());
|
||||
query.setParameter("username", userName);
|
||||
try {
|
||||
return (AccountEntity) query.getSingleResult();
|
||||
} catch (NoResultException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public AccountEntity saveAccount(AccountEntity account, ApplicationEntity app, List<ApplicationRoleEntity> applicationRoles) {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
String currentLoggedInUser = currentUser.getPrincipal().toString();
|
||||
|
||||
account.setLastUpdatedBy(currentLoggedInUser); // FIXME - should be done via updateable
|
||||
account.setLastUpdatedOn(now);
|
||||
|
||||
boolean newAccount = (account.getCreatedOn() == null);
|
||||
|
||||
// new account
|
||||
if (newAccount) {
|
||||
account.setCreatedOn(now);
|
||||
account.setCreatedBy(currentLoggedInUser);
|
||||
em.persist(account);
|
||||
} else {
|
||||
em.merge(account);
|
||||
|
||||
// reload account from db and join roles
|
||||
account = getAccountEntity(account.getUsername(), true);
|
||||
}
|
||||
|
||||
// assign roles to account
|
||||
if (account.getApplicationRoleList() == null) {
|
||||
account.setApplicationRoleList(new ArrayList<>());
|
||||
}
|
||||
|
||||
boolean roleSetupChanged = false;
|
||||
// remove roles which are no longer listed
|
||||
// ensure this is only done for the given application - keep the other applications untouched
|
||||
List<ApplicationRoleEntity> assignedRoles = new ArrayList<>();
|
||||
assignedRoles.addAll(account.getApplicationRoleList());
|
||||
for (ApplicationRoleEntity currentlyAssignedRole : assignedRoles) {
|
||||
if ((currentlyAssignedRole.getApplication().equals(app) && (!applicationRoles.contains(currentlyAssignedRole)))) {
|
||||
account.getApplicationRoleList().remove(currentlyAssignedRole);
|
||||
roleSetupChanged = true;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Removed role {} ({}) from user {}", currentlyAssignedRole.getRoleName(), application.getApplicationName(), account.getUsername());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add newly added roles to role list
|
||||
for (ApplicationRoleEntity applicationRole : applicationRoles) {
|
||||
if (!account.getApplicationRoleList().contains(applicationRole)) {
|
||||
account.addApplicationRole(applicationRole);
|
||||
roleSetupChanged = true;
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Added role {} ({}) to account {}", applicationRole.getRoleName(), application.getApplicationName(), account.getUsername());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update account in database if roles changed
|
||||
if (roleSetupChanged) {
|
||||
em.merge(account);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAccount(AccountEntity account) throws AccountException {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
String currentUserName = currentUser.getPrincipal().toString();
|
||||
|
||||
if (account.getUsername().equals(currentUserName)) {
|
||||
throw new AccountException("Cannot delete own account");
|
||||
} else {
|
||||
account.setStatus(AccountStatus.DISABLED.name());
|
||||
account.setLastUpdatedBy(currentUserName);
|
||||
account.setLastUpdatedOn(now);
|
||||
em.merge(account);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public boolean initPasswordReset(String userName) {
|
||||
try {
|
||||
AccountEntity account = getAccountEntity(userName, false);
|
||||
if (account == null) {
|
||||
LOGGER.warn("Account with name " + userName + " not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getAccountLogin() == null) {
|
||||
LOGGER.error("No login for account {} defined, cannot reset password", userName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getStatus().equals(AccountStatus.BLOCKED.name())) {
|
||||
LOGGER.warn("Account " + userName + " is locked, cannot initialize password reset");
|
||||
return false;
|
||||
}
|
||||
|
||||
String randomString = RandomStringUtils.random(40, true, true);
|
||||
|
||||
Date validTo = DateUtil.getCurrentTimeInUTC();
|
||||
validTo = new Date(validTo.getTime() + 1000 * 600); // 10 minutes to react
|
||||
|
||||
account.getAccountLogin().setPasswordResetHash(randomString);
|
||||
account.getAccountLogin().setPasswordResetOngoing(true);
|
||||
account.getAccountLogin().setPasswordResetValidTo(validTo);
|
||||
mailService.sendPasswortResetStartEmail(account, randomString);
|
||||
|
||||
em.merge(account.getAccountLogin());
|
||||
em.merge(account);
|
||||
return true;
|
||||
} catch (MailException | ConfigException ex) {
|
||||
LOGGER.error("Error while sending password reset mail. " + ex.toString());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Error while sending password reset mail.", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public boolean resetPassword(String userName, String newPassword, String resetPasswordToken) {
|
||||
AccountEntity account = getAccountEntity(userName, false);
|
||||
|
||||
if (account == null) {
|
||||
LOGGER.warn("Error while resetting password, no account with username " + userName + " found");
|
||||
// TODO add extra logging for intrusion protection system like fail2ban
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getAccountLogin() == null) {
|
||||
// user has no defined login, cannot reset password
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.getAccountLogin().getPasswordResetOngoing() && (account.getAccountLogin().getPasswordResetHash() != null) && (account.getAccountLogin().getPasswordResetValidTo() != null)) {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
String storedHash = account.getAccountLogin().getPasswordResetHash().trim();
|
||||
if (account.getAccountLogin().getPasswordResetValidTo().after(now)) {
|
||||
if (storedHash.equals(resetPasswordToken)) {
|
||||
// everything ok, reset password
|
||||
executePasswordReset(account, newPassword);
|
||||
LOGGER.info("Updated password for user " + userName);
|
||||
return true;
|
||||
} else {
|
||||
// token is not valid, refuse to change password
|
||||
LOGGER.warn("Trying to reset password for user " + userName + " but wrong token " + resetPasswordToken + " provided");
|
||||
addLoginError(account);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// password reset token no longer valid
|
||||
LOGGER.warn("Trying to reset password for user " + userName + " but token is no longer valid");
|
||||
addLoginError(account);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// user is not is password reset mode
|
||||
LOGGER.warn("Trying to reset password for user " + userName + " but password reset was not requested");
|
||||
addLoginError(account);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void executePasswordReset(AccountEntity account, String newPassword) {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
|
||||
String hashedPassword = SecurityUtil.createPassword(newPassword);
|
||||
if (account.getAccountLogin() == null) {
|
||||
return;
|
||||
}
|
||||
account.getAccountLogin().setAccountPassword(hashedPassword);
|
||||
account.getAccountLogin().setPasswordResetOngoing(false);
|
||||
account.getAccountLogin().setPasswordResetHash(null);
|
||||
account.getAccountLogin().setPasswordResetValidTo(null);
|
||||
account.setLastUpdatedBy(account.getUsername());
|
||||
account.setLastUpdatedOn(now);
|
||||
em.merge(account);
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public AccountLoginEntity updateSuccessFullLogin(AccountLoginEntity login, String byUser) {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
// a scucessful login ends a password reset procedure
|
||||
if (login.getPasswordResetOngoing()) {
|
||||
login.setPasswordResetOngoing(false);
|
||||
login.setPasswordResetHash(null);
|
||||
login.setPasswordResetValidTo(null);
|
||||
login.setLastUpdatedOn(now);
|
||||
login.setLastUpdatedBy(byUser);
|
||||
}
|
||||
|
||||
login.setLastLogin(now);
|
||||
login.setFailureCount(0);
|
||||
return updateLogin(login);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public AccountLoginEntity updateLogin(AccountLoginEntity login) {
|
||||
return em.merge(login);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateLogin(AccountEntity account) {
|
||||
if (account.getAccountLogin() == null) {
|
||||
// TODO connect to IPRS - how can an account ask for an updated login if the user cannot login
|
||||
} else {
|
||||
updateSuccessFullLogin(account.getAccountLogin(), account.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void addLoginError(AccountEntity account) {
|
||||
if (account.getAccountLogin() == null) {
|
||||
LOGGER.error("No login defined for {}", account.getUsername());
|
||||
} else {
|
||||
try {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
account.getAccountLogin().setLastFailedLogin(now);
|
||||
account.getAccountLogin().setFailureCount(account.getAccountLogin().getFailureCount() + 1);
|
||||
|
||||
int maxFailedLogins = Integer.parseInt(configService.getConfigValue("account.maxFailedLogins"));
|
||||
if ((account.getAccountLogin().getFailureCount() >= maxFailedLogins) && (!account.getStatus().equals("LOCKED"))) { // TOD add status enum
|
||||
// max failed logins reached, disabling user
|
||||
LOGGER.info("Locking account " + account.getUsername() + " due to " + account.getAccountLogin().getFailureCount() + " failed logins");
|
||||
account.setStatus(AccountStatus.BLOCKED.name());
|
||||
}
|
||||
|
||||
// on a failed login request, disable password reset
|
||||
account.getAccountLogin().setPasswordResetOngoing(false);
|
||||
account.getAccountLogin().setPasswordResetHash(null);
|
||||
account.getAccountLogin().setPasswordResetValidTo(null);
|
||||
|
||||
account.setLastUpdatedBy("system");
|
||||
account.setLastUpdatedOn(now);
|
||||
em.merge(account);
|
||||
} catch (ConfigException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(ex.toString(), ex);
|
||||
} else {
|
||||
LOGGER.error(ex.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AccountLoginEntity createLoginWithRandomPassword() {
|
||||
AccountLoginEntity login = new AccountLoginEntity();
|
||||
String randomPassword = RandomStringUtils.random(20, true, true);
|
||||
String hashedPassword = SecurityUtil.createPassword(randomPassword);
|
||||
login.setAccountPassword(hashedPassword);
|
||||
login.setLastLogin(null);
|
||||
login.setLastFailedLogin(null);
|
||||
login.setFailureCount(0);
|
||||
|
||||
return login;
|
||||
}
|
||||
|
||||
public String getHashedPassword(String password) {
|
||||
String hashedPassword = SecurityUtil.createPassword(password);
|
||||
return hashedPassword;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void addLogin(AccountEntity accountToAdd, AccountLoginEntity accountLogin) {
|
||||
Date now = DateUtil.getCurrentTimeInUTC();
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
String currentLoggedInUser = currentUser.getPrincipal().toString();
|
||||
|
||||
AccountEntity account = em.merge(accountToAdd);
|
||||
accountLogin.setAccount(account);
|
||||
accountLogin.setCreatedBy(currentLoggedInUser);
|
||||
accountLogin.setCreatedOn(now);
|
||||
accountLogin.setLastUpdatedBy(currentLoggedInUser);
|
||||
accountLogin.setLastUpdatedOn(now);
|
||||
em.persist(accountLogin);
|
||||
|
||||
account.setAccountLogin(accountLogin);
|
||||
em.merge(account);
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteLogin(AccountEntity accountToDelete) {
|
||||
AccountEntity account = em.merge(accountToDelete);
|
||||
AccountLoginEntity login = account.getAccountLogin();
|
||||
login.setAccount(null);
|
||||
account.setAccountLogin(null);
|
||||
em.remove(login);
|
||||
em.merge(account);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public interface Account {
|
||||
|
||||
String getUsername();
|
||||
String getFirstname();
|
||||
String getLastname();
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public enum AccountConfigurationKey {
|
||||
// the base path of the application
|
||||
BaseUrl,
|
||||
// the full pass to the reset password page
|
||||
PasswordResetUrl,
|
||||
// injection handler
|
||||
Producer;
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import javax.enterprise.util.Nonbinding;
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Qualifier
|
||||
public @interface AccountConfigurationValue {
|
||||
|
||||
@Nonbinding
|
||||
AccountConfigurationKey key();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,24 @@
|
||||
package de.muehlencord.shared.account.entity;
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigEntity;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@ -17,44 +35,36 @@ import javax.persistence.ManyToMany;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
/**
|
||||
*
|
||||
* IMPORANT: DO NOT CACHE - e.g. password changes are not synchronized
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "account")
|
||||
@XmlRootElement
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a"),
|
||||
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a ORDER by a.lastname, a.firstname"),
|
||||
@NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username"),
|
||||
@NamedQuery(name = "AccountEntity.findByEmailaddress", query = "SELECT a FROM AccountEntity a WHERE a.emailaddress = :emailaddress"),
|
||||
@NamedQuery(name = "AccountEntity.findByFirstname", query = "SELECT a FROM AccountEntity a WHERE a.firstname = :firstname"),
|
||||
@NamedQuery(name = "AccountEntity.findByLastname", query = "SELECT a FROM AccountEntity a WHERE a.lastname = :lastname"),
|
||||
@NamedQuery(name = "AccountEntity.findByAccountPassword", query = "SELECT a FROM AccountEntity a WHERE a.accountPassword = :accountPassword"),
|
||||
@NamedQuery(name = "AccountEntity.findByLastLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastLogin = :lastLogin"),
|
||||
@NamedQuery(name = "AccountEntity.findByLastFailedLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastFailedLogin = :lastFailedLogin"),
|
||||
@NamedQuery(name = "AccountEntity.findByFailureCount", query = "SELECT a FROM AccountEntity a WHERE a.failureCount = :failureCount"),
|
||||
@NamedQuery(name = "AccountEntity.findByStatus", query = "SELECT a FROM AccountEntity a WHERE a.status = :status"),
|
||||
@NamedQuery(name = "AccountEntity.findByPasswordResetOngoing", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetOngoing = :passwordResetOngoing"),
|
||||
@NamedQuery(name = "AccountEntity.findByPasswordResetValidTo", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetValidTo = :passwordResetValidTo"),
|
||||
@NamedQuery(name = "AccountEntity.findByPasswordResetHash", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetHash = :passwordResetHash"),
|
||||
@NamedQuery(name = "AccountEntity.findActiveAccounts", query = "SELECT a FROM AccountEntity a WHERE a.status <> :status"),
|
||||
@NamedQuery(name = "AccountEntity.findByCreatedOn", query = "SELECT a FROM AccountEntity a WHERE a.createdOn = :createdOn"),
|
||||
@NamedQuery(name = "AccountEntity.findByCreatedBy", query = "SELECT a FROM AccountEntity a WHERE a.createdBy = :createdBy"),
|
||||
@NamedQuery(name = "AccountEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
|
||||
@NamedQuery(name = "AccountEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
|
||||
@NamedQuery(name = "AccountEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")
|
||||
})
|
||||
public class AccountEntity implements Serializable, Account {
|
||||
|
||||
private static final long serialVersionUID = 6216991757526150935L;
|
||||
private static final long serialVersionUID = 2174163529615355336L;
|
||||
|
||||
@Id
|
||||
@Basic(optional = false)
|
||||
@ -86,36 +96,11 @@ public class AccountEntity implements Serializable, Account {
|
||||
private String lastname;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 200)
|
||||
@Column(name = "account_password", columnDefinition = "bpchar(200)")
|
||||
private String accountPassword;
|
||||
@Column(name = "last_login")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastLogin;
|
||||
@Column(name = "last_failed_login")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastFailedLogin;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "failure_count")
|
||||
private int failureCount;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 10)
|
||||
@Column(name = "status")
|
||||
private String status;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "password_reset_ongoing")
|
||||
private boolean passwordResetOngoing;
|
||||
@Column(name = "password_reset_valid_to")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date passwordResetValidTo;
|
||||
@Size(max = 200)
|
||||
@Column(name = "password_reset_hash", columnDefinition = "bpchar(200)")
|
||||
private String passwordResetHash;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "created_on")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date createdOn;
|
||||
@ -141,30 +126,23 @@ public class AccountEntity implements Serializable, Account {
|
||||
private List<ApplicationRoleEntity> applicationRoleList;
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountId", fetch = FetchType.LAZY)
|
||||
private List<AccountHistoryEntity> accountHistoryList;
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "account")
|
||||
private AccountLoginEntity accountLogin;
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account")
|
||||
private List<ConfigEntity> configItems;
|
||||
|
||||
public AccountEntity() {
|
||||
// empty constructor required for JPA
|
||||
}
|
||||
|
||||
public AccountEntity(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AccountEntity(UUID id, String username, String emailaddress, String firstname, String lastname, String accountPassword, int failureCount, String status, boolean passwordResetOngoing, Date createdOn, String createdBy, Date lastUpdatedOn, String lastUpdatedBy) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.emailaddress = emailaddress;
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.accountPassword = accountPassword;
|
||||
this.failureCount = failureCount;
|
||||
this.status = status;
|
||||
this.passwordResetOngoing = passwordResetOngoing;
|
||||
this.createdOn = createdOn;
|
||||
this.createdBy = createdBy;
|
||||
this.lastUpdatedOn = lastUpdatedOn;
|
||||
this.lastUpdatedBy = lastUpdatedBy;
|
||||
public void addApplicationRole(ApplicationRoleEntity applicationRole) {
|
||||
if (applicationRoleList == null) {
|
||||
applicationRoleList = new ArrayList<>();
|
||||
}
|
||||
applicationRoleList.add(applicationRole);
|
||||
}
|
||||
|
||||
/* **** getter / setter **** */
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
@ -190,6 +168,7 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.emailaddress = emailaddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFirstname() {
|
||||
return firstname;
|
||||
}
|
||||
@ -198,6 +177,7 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.firstname = firstname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastname() {
|
||||
return lastname;
|
||||
}
|
||||
@ -206,38 +186,6 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.lastname = lastname;
|
||||
}
|
||||
|
||||
public String getAccountPassword() {
|
||||
return accountPassword;
|
||||
}
|
||||
|
||||
public void setAccountPassword(String accountPassword) {
|
||||
this.accountPassword = accountPassword;
|
||||
}
|
||||
|
||||
public Date getLastLogin() {
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
public void setLastLogin(Date lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
public Date getLastFailedLogin() {
|
||||
return lastFailedLogin;
|
||||
}
|
||||
|
||||
public void setLastFailedLogin(Date lastFailedLogin) {
|
||||
this.lastFailedLogin = lastFailedLogin;
|
||||
}
|
||||
|
||||
public int getFailureCount() {
|
||||
return failureCount;
|
||||
}
|
||||
|
||||
public void setFailureCount(int failureCount) {
|
||||
this.failureCount = failureCount;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
@ -246,30 +194,6 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean getPasswordResetOngoing() {
|
||||
return passwordResetOngoing;
|
||||
}
|
||||
|
||||
public void setPasswordResetOngoing(boolean passwordResetOngoing) {
|
||||
this.passwordResetOngoing = passwordResetOngoing;
|
||||
}
|
||||
|
||||
public Date getPasswordResetValidTo() {
|
||||
return passwordResetValidTo;
|
||||
}
|
||||
|
||||
public void setPasswordResetValidTo(Date passwordResetValidTo) {
|
||||
this.passwordResetValidTo = passwordResetValidTo;
|
||||
}
|
||||
|
||||
public String getPasswordResetHash() {
|
||||
return passwordResetHash;
|
||||
}
|
||||
|
||||
public void setPasswordResetHash(String passwordResetHash) {
|
||||
this.passwordResetHash = passwordResetHash;
|
||||
}
|
||||
|
||||
public Date getCreatedOn() {
|
||||
return createdOn;
|
||||
}
|
||||
@ -302,7 +226,6 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.lastUpdatedBy = lastUpdatedBy;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public List<ApplicationRoleEntity> getApplicationRoleList() {
|
||||
return applicationRoleList;
|
||||
}
|
||||
@ -311,7 +234,6 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.applicationRoleList = applicationRoleList;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public List<AccountHistoryEntity> getAccountHistoryList() {
|
||||
return accountHistoryList;
|
||||
}
|
||||
@ -320,6 +242,22 @@ public class AccountEntity implements Serializable, Account {
|
||||
this.accountHistoryList = accountHistoryList;
|
||||
}
|
||||
|
||||
public AccountLoginEntity getAccountLogin() {
|
||||
return accountLogin;
|
||||
}
|
||||
|
||||
public void setAccountLogin(AccountLoginEntity accountLogin) {
|
||||
this.accountLogin = accountLogin;
|
||||
}
|
||||
|
||||
public List<ConfigEntity> getConfigItems() {
|
||||
return configItems;
|
||||
}
|
||||
|
||||
public void setConfigItems(List<ConfigEntity> configItems) {
|
||||
this.configItems = configItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
@ -344,5 +282,4 @@ public class AccountEntity implements Serializable, Account {
|
||||
public String toString() {
|
||||
return "de.muehlencord.shared.account.entity.Account[ id=" + id + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Raimund
|
||||
*/
|
||||
public class AccountException extends Exception {
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>AccountException</code> without detail
|
||||
* message.
|
||||
*/
|
||||
public AccountException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>AccountException</code> with the
|
||||
* specified detail message.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public AccountException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public AccountException(String entity_updated__deleted_please_reload, boolean b) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,25 @@
|
||||
package de.muehlencord.shared.account.entity;
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
@ -27,6 +43,7 @@ import org.hibernate.annotations.Type;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "account_history")
|
||||
@Cacheable
|
||||
@XmlRootElement
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "AccountHistoryEntity.findAll", query = "SELECT a FROM AccountHistoryEntity a"),
|
||||
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jomu
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "account_login")
|
||||
@Cacheable
|
||||
@XmlRootElement
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "AccountLoginEntity.findAll", query = "SELECT a FROM AccountLoginEntity a"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByAccountPassword", query = "SELECT a FROM AccountLoginEntity a WHERE a.accountPassword = :accountPassword"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByLastLogin", query = "SELECT a FROM AccountLoginEntity a WHERE a.lastLogin = :lastLogin"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByLastFailedLogin", query = "SELECT a FROM AccountLoginEntity a WHERE a.lastFailedLogin = :lastFailedLogin"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByFailureCount", query = "SELECT a FROM AccountLoginEntity a WHERE a.failureCount = :failureCount"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByPasswordResetOngoing", query = "SELECT a FROM AccountLoginEntity a WHERE a.passwordResetOngoing = :passwordResetOngoing"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByPasswordResetValidTo", query = "SELECT a FROM AccountLoginEntity a WHERE a.passwordResetValidTo = :passwordResetValidTo"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByPasswordResetHash", query = "SELECT a FROM AccountLoginEntity a WHERE a.passwordResetHash = :passwordResetHash"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByCreatedOn", query = "SELECT a FROM AccountLoginEntity a WHERE a.createdOn = :createdOn"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByCreatedBy", query = "SELECT a FROM AccountLoginEntity a WHERE a.createdBy = :createdBy"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountLoginEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
|
||||
@NamedQuery(name = "AccountLoginEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountLoginEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
|
||||
public class AccountLoginEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -799045989045040077L;
|
||||
|
||||
@Id
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(generator = "uuid2")
|
||||
@GenericGenerator(name = "uuid2", strategy = "uuid2")
|
||||
@Type(type = "pg-uuid")
|
||||
private UUID id;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 200)
|
||||
@Column(name = "account_password")
|
||||
private String accountPassword;
|
||||
@Column(name = "last_login")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastLogin;
|
||||
@Column(name = "last_failed_login")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastFailedLogin;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "failure_count")
|
||||
private int failureCount;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "password_reset_ongoing")
|
||||
private boolean passwordResetOngoing;
|
||||
@Column(name = "password_reset_valid_to")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date passwordResetValidTo;
|
||||
@Size(max = 200)
|
||||
@Column(name = "password_reset_hash")
|
||||
private String passwordResetHash;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "created_on")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date createdOn;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 32)
|
||||
@Column(name = "created_by")
|
||||
private String createdBy;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "last_updated_on")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastUpdatedOn;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 32)
|
||||
@Column(name = "last_updated_by")
|
||||
private String lastUpdatedBy;
|
||||
@JoinColumn(name = "account", referencedColumnName = "id")
|
||||
@OneToOne(optional = false)
|
||||
private AccountEntity account;
|
||||
|
||||
public AccountLoginEntity() {
|
||||
}
|
||||
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getAccountPassword() {
|
||||
return accountPassword;
|
||||
}
|
||||
|
||||
public void setAccountPassword(String accountPassword) {
|
||||
this.accountPassword = accountPassword;
|
||||
}
|
||||
|
||||
public Date getLastLogin() {
|
||||
return lastLogin;
|
||||
}
|
||||
|
||||
public void setLastLogin(Date lastLogin) {
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
public Date getLastFailedLogin() {
|
||||
return lastFailedLogin;
|
||||
}
|
||||
|
||||
public void setLastFailedLogin(Date lastFailedLogin) {
|
||||
this.lastFailedLogin = lastFailedLogin;
|
||||
}
|
||||
|
||||
public int getFailureCount() {
|
||||
return failureCount;
|
||||
}
|
||||
|
||||
public void setFailureCount(int failureCount) {
|
||||
this.failureCount = failureCount;
|
||||
}
|
||||
|
||||
public boolean getPasswordResetOngoing() {
|
||||
return passwordResetOngoing;
|
||||
}
|
||||
|
||||
public void setPasswordResetOngoing(boolean passwordResetOngoing) {
|
||||
this.passwordResetOngoing = passwordResetOngoing;
|
||||
}
|
||||
|
||||
public Date getPasswordResetValidTo() {
|
||||
return passwordResetValidTo;
|
||||
}
|
||||
|
||||
public void setPasswordResetValidTo(Date passwordResetValidTo) {
|
||||
this.passwordResetValidTo = passwordResetValidTo;
|
||||
}
|
||||
|
||||
public String getPasswordResetHash() {
|
||||
return passwordResetHash;
|
||||
}
|
||||
|
||||
public void setPasswordResetHash(String passwordResetHash) {
|
||||
this.passwordResetHash = passwordResetHash;
|
||||
}
|
||||
|
||||
public Date getCreatedOn() {
|
||||
return createdOn;
|
||||
}
|
||||
|
||||
public void setCreatedOn(Date createdOn) {
|
||||
this.createdOn = createdOn;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(String createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getLastUpdatedOn() {
|
||||
return lastUpdatedOn;
|
||||
}
|
||||
|
||||
public void setLastUpdatedOn(Date lastUpdatedOn) {
|
||||
this.lastUpdatedOn = lastUpdatedOn;
|
||||
}
|
||||
|
||||
public String getLastUpdatedBy() {
|
||||
return lastUpdatedBy;
|
||||
}
|
||||
|
||||
public void setLastUpdatedBy(String lastUpdatedBy) {
|
||||
this.lastUpdatedBy = lastUpdatedBy;
|
||||
}
|
||||
|
||||
public AccountEntity getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(AccountEntity account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (id != null ? id.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
// TODO: Warning - this method won't work in the case the id fields are not set
|
||||
if (!(object instanceof AccountLoginEntity)) {
|
||||
return false;
|
||||
}
|
||||
AccountLoginEntity other = (AccountLoginEntity) object;
|
||||
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "de.muehlencord.shared.account.business.account.entity.AccountLoginEntity[ id=" + id + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
public enum AccountStatus {
|
||||
|
||||
NEW, // account is created but never used
|
||||
NORMAL, // normal account, at least on login, neither blocked or disabled
|
||||
BLOCKED, // account is blocked after too many login failures or other security related events
|
||||
DISABLED; // account is disabled and cannot be used anymore
|
||||
|
||||
public static List<String> getAllStatusNames() {
|
||||
List<String> statusNames = new ArrayList<>();
|
||||
for (AccountStatus currentStatus : AccountStatus.values()) {
|
||||
statusNames.add (currentStatus.name());
|
||||
}
|
||||
return statusNames;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Entity
|
||||
@Cacheable
|
||||
@Table(name = "api_key")
|
||||
@XmlRootElement
|
||||
@NamedQueries({
|
||||
@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 = {
|
||||
@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.findByAccount", query = "SELECT a FROM ApiKeyEntity a WHERE a.account = :account ORDER BY a.issuedOn DESC",
|
||||
hints = {
|
||||
@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")})
|
||||
public class ApiKeyEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1044658457228215810L;
|
||||
|
||||
@Id
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(generator = "uuid2")
|
||||
@GenericGenerator(name = "uuid2", strategy = "uuid2")
|
||||
@Type(type = "pg-uuid")
|
||||
private UUID id;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 200)
|
||||
@Column(name = "api_key")
|
||||
private String apiKey;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "issued_on")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date issuedOn;
|
||||
@Column(name = "expiration")
|
||||
private Short expiration;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "expires_on")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date expiresOn;
|
||||
@JoinColumn(name = "account", referencedColumnName = "id")
|
||||
@ManyToOne(optional = false)
|
||||
private AccountEntity account;
|
||||
|
||||
public ApiKeyEntity() {
|
||||
// empty constructor required for JPA
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public Date getIssuedOn() {
|
||||
return issuedOn;
|
||||
}
|
||||
|
||||
public void setIssuedOn(Date issuedOn) {
|
||||
this.issuedOn = issuedOn;
|
||||
}
|
||||
|
||||
public Short getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(Short expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public Date getExpiresOn() {
|
||||
return expiresOn;
|
||||
}
|
||||
|
||||
public void setExpiresOn(Date expiresOn) {
|
||||
this.expiresOn = expiresOn;
|
||||
}
|
||||
|
||||
public AccountEntity getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(AccountEntity account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (id != null ? id.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
// TODO: Warning - this method won't work in the case the id fields are not set
|
||||
if (!(object instanceof ApiKeyEntity)) {
|
||||
return false;
|
||||
}
|
||||
ApiKeyEntity other = (ApiKeyEntity) object;
|
||||
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "com.wincornixdorf.pcd.business.account.ApiKeyEntity[ id=" + id + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.account.entity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public class JWTObject {
|
||||
|
||||
private String userName;
|
||||
private String unqiueId;
|
||||
private boolean valid;
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getUnqiueId() {
|
||||
return unqiueId;
|
||||
}
|
||||
|
||||
public void setUnqiueId(String unqiueId) {
|
||||
this.unqiueId = unqiueId;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void setValid(boolean valid) {
|
||||
this.valid = valid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.util.SecurityError;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public enum ApplicationError implements SecurityError {
|
||||
|
||||
LIST_DENIED("1000", "list_denied");
|
||||
|
||||
private final String errorCode;
|
||||
private final String messageKey;
|
||||
|
||||
private ApplicationError(String errorCode, String messageKey) {
|
||||
this.errorCode = errorCode;
|
||||
this.messageKey = messageKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageKey() {
|
||||
return messageKey;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.control;
|
||||
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.instance.boundary.ApplicationPermissions;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import de.muehlencord.shared.account.util.AccountSecurityException;
|
||||
import de.muehlencord.shared.account.util.SecurityUtil;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Stateless
|
||||
public class ApplicationControl implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4262608935325326191L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationControl.class);
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
public ApplicationEntity findById(UUID id) {
|
||||
return em.find(ApplicationEntity.class, id);
|
||||
}
|
||||
|
||||
public List<ApplicationEntity> getAllApplications() throws AccountSecurityException {
|
||||
List<ApplicationEntity> resultList = new ArrayList<>();
|
||||
Query query = em.createNamedQuery("ApplicationEntity.findAll");
|
||||
List<ApplicationEntity> queryList = query.getResultList();
|
||||
if ((queryList == null) || (queryList.isEmpty())) {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
Subject currentUser = SecurityUtils.getSubject();
|
||||
if (currentUser == null)
|
||||
return resultList;
|
||||
String userName = currentUser.getPrincipal().toString();
|
||||
|
||||
queryList.stream().forEach(app -> {
|
||||
String applicationName = app.getApplicationName(); // TODO add unique short cut to db model
|
||||
applicationName = applicationName.toLowerCase();
|
||||
applicationName = applicationName.replace (" ", "");
|
||||
String permissionName = ApplicationPermissions.APP_LIST.getName()+":"+applicationName;
|
||||
boolean userHasPermissionToListApplication = SecurityUtil.checkPermission (permissionName);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("validating if user {} has permission {} = {}", userName, permissionName, userHasPermissionToListApplication);
|
||||
}
|
||||
|
||||
if (userHasPermissionToListApplication) {
|
||||
resultList.add (app);
|
||||
}
|
||||
});
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ApplicationEntity createOrUpdate(ApplicationEntity app) {
|
||||
if (app == null) {
|
||||
// TODO add error handling
|
||||
return null;
|
||||
} else {
|
||||
if (app.getId() == null) {
|
||||
em.persist(app);
|
||||
ApplicationEntity returnValue = findByApplicationName(app.getApplicationName());
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Application {} created", app.getApplicationName());
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
} else {
|
||||
ApplicationEntity returnValue = em.merge(app);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Applicateion {} updated", app.getApplicationName());
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationEntity findByApplicationName(String applicationName) {
|
||||
Query query = em.createNamedQuery("ApplicationEntity.findByApplicationName");
|
||||
query.setParameter("applicationName", applicationName);
|
||||
List<ApplicationEntity> resultList = query.getResultList();
|
||||
if ((resultList == null) || (resultList.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return resultList.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(ApplicationEntity app) {
|
||||
ApplicationEntity attachedApp = em.find(ApplicationEntity.class, app.getId());
|
||||
em.remove(attachedApp);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Application {} deleted", app.getApplicationName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.control;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountException;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import de.muehlencord.shared.account.util.Permission;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Stateless
|
||||
public class ApplicationPermissionControl implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3761100587901739481L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationPermissionControl.class);
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
@Inject
|
||||
ApplicationEntity application;
|
||||
|
||||
public List<ApplicationPermissionEntity> getApplicationPermissions(ApplicationEntity app) {
|
||||
if (application == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
Query query = em.createNamedQuery("ApplicationPermissionEntity.findAll");
|
||||
query.setParameter("application", app);
|
||||
List<ApplicationPermissionEntity> permissionList = query.getResultList();
|
||||
if (permissionList == null) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return permissionList;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity findPermissionByName(ApplicationEntity application, String permissionName) {
|
||||
Query query = em.createNamedQuery("ApplicationPermissionEntity.findByPermissionName");
|
||||
query.setParameter("application", application);
|
||||
query.setParameter("permissionName", permissionName);
|
||||
List<ApplicationPermissionEntity> resultList = query.getResultList();
|
||||
if ((resultList == null) || (resultList.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return resultList.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void create(ApplicationEntity application, String name, String description) {
|
||||
ApplicationPermissionEntity permission = new ApplicationPermissionEntity(application, name, description);
|
||||
em.persist(permission);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void update(ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationPermissionEntity existing = attach(permission);
|
||||
em.merge(existing);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void createOrUpdate(ApplicationEntity application, String name, String description) {
|
||||
ApplicationPermissionEntity permission = findByName(application, name);
|
||||
if (permission == null) {
|
||||
permission = new ApplicationPermissionEntity(name, description);
|
||||
em.persist(permission);
|
||||
} else {
|
||||
permission.setPermissionDescription(description);
|
||||
em.merge(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationPermissionEntity existingPermission = attach(permission);
|
||||
em.remove(existingPermission);
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity attach(ApplicationPermissionEntity permission) throws AccountException {
|
||||
try {
|
||||
return em.merge(permission);
|
||||
} catch (OptimisticLockException ex) {
|
||||
throw new AccountException("Entity updated / deleted, please reload", true);
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationPermissionEntity findByName(ApplicationEntity application, String name) {
|
||||
Query query = em.createNamedQuery("ApplicationPermissionEntity.findByPermissionName");
|
||||
query.setParameter("application", application);
|
||||
query.setParameter("permissionName", name);
|
||||
List<ApplicationPermissionEntity> permissions = query.getResultList();
|
||||
if ((permissions == null) || (permissions.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return permissions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void setupPermissions(List<Permission> permissions) {
|
||||
if (application == null) {
|
||||
LOGGER.error("Application not initialized, cannot setup permissions");
|
||||
} else {
|
||||
for (Permission permission : permissions) {
|
||||
ApplicationPermissionEntity existingPermission = findByName(application, permission.getName());
|
||||
if (existingPermission == null) {
|
||||
// permission not available, create it
|
||||
LOGGER.info("missing permission {} of {}", permission.getName(), application.getApplicationName());
|
||||
existingPermission = new ApplicationPermissionEntity(permission.getName(), permission.getDescription());
|
||||
existingPermission.setApplication(application);
|
||||
em.persist(existingPermission);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("missing permission {} added to {}", permission.getName(), application.getApplicationName());
|
||||
}
|
||||
} else {
|
||||
if (existingPermission.getPermissionDescription().equals(permission.getDescription())) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Permission {} for {} already exists, skipping", permission.getName(), application.getApplicationName());
|
||||
}
|
||||
} else {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("description of permssion {} for {} differs, resetting to orignal value {}", permission.getName(), application.getApplicationName(), permission.getDescription());
|
||||
}
|
||||
|
||||
existingPermission.setPermissionDescription(permission.getDescription());
|
||||
em.merge(existingPermission);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("updated permission description {} for {}", permission.getName(), application.getApplicationName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.control;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountException;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationPermissionEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import de.muehlencord.shared.account.util.Permission;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.ejb.EJB;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Stateless
|
||||
public class ApplicationRoleControl implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5962478269550134748L;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRoleControl.class);
|
||||
|
||||
@EJB
|
||||
ApplicationPermissionControl applicationPermissionControl;
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
@Inject
|
||||
ApplicationEntity application;
|
||||
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public List<ApplicationRoleEntity> getAllRoles(ApplicationEntity app) {
|
||||
Query query = em.createNamedQuery("ApplicationRoleEntity.findAll");
|
||||
query.setParameter("application", app);
|
||||
|
||||
List<ApplicationRoleEntity> roles = query.getResultList();
|
||||
if (roles == null) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public void createOrUpdate(ApplicationEntity app, String name, String description) {
|
||||
ApplicationRoleEntity role = findByName(app, name);
|
||||
if (role == null) {
|
||||
role = new ApplicationRoleEntity(app, name, description);
|
||||
em.persist(role);
|
||||
} else {
|
||||
role.setRoleDescription(description);
|
||||
em.merge(role);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public void create(ApplicationRoleEntity role) {
|
||||
em.persist(role);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public void update(ApplicationRoleEntity role) {
|
||||
em.merge(role);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public void delete(ApplicationRoleEntity role) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = attach(role);
|
||||
em.remove(existingRole);
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity attach(ApplicationRoleEntity role) throws AccountException {
|
||||
try {
|
||||
return em.merge(role);
|
||||
} catch (OptimisticLockException ex) {
|
||||
throw new AccountException("Entity updated / deleted, please reload", true);
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity findByName(String name) {
|
||||
return findByName(application, name);
|
||||
}
|
||||
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public ApplicationRoleEntity findByName(ApplicationEntity app, String name) {
|
||||
Query query = em.createNamedQuery("ApplicationRoleEntity.findByRoleName");
|
||||
query.setParameter("application", app);
|
||||
query.setParameter("roleName", name);
|
||||
List<ApplicationRoleEntity> permissions = query.getResultList();
|
||||
if ((permissions == null) || (permissions.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return permissions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ApplicationPermissionEntity> getRolePermissions(ApplicationRoleEntity role) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = em.find(ApplicationRoleEntity.class, role.getId());
|
||||
List<ApplicationPermissionEntity> permissions = existingRole.getApplicationPermissionList();
|
||||
if ((permissions != null) && (permissions.isEmpty())) {
|
||||
permissions.size(); // force list to load
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public List<ApplicationPermissionEntity> getNotAssignedApplicationPermissions(ApplicationRoleEntity role) {
|
||||
try {
|
||||
List<ApplicationPermissionEntity> rolePermissions = getRolePermissions(role);
|
||||
List<ApplicationPermissionEntity> allPermssions = applicationPermissionControl.getApplicationPermissions(role.getApplication());
|
||||
|
||||
List<ApplicationPermissionEntity> missingPermissions = new ArrayList<>();
|
||||
allPermssions.stream().filter((perm) -> (!rolePermissions.contains(perm))).forEachOrdered((perm) -> {
|
||||
missingPermissions.add(perm);
|
||||
});
|
||||
return missingPermissions;
|
||||
} catch (AccountException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(ex.toString(), ex);
|
||||
} else {
|
||||
LOGGER.debug(ex.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public void addPermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = attach(role);
|
||||
if (existingRole.getApplicationPermissionList() == null) {
|
||||
existingRole.setApplicationPermissionList(new ArrayList<>());
|
||||
}
|
||||
existingRole.getApplicationPermissionList().add(permission);
|
||||
em.merge(role);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
// TODO requires special role to maintain role for other allication
|
||||
public void removePermission(ApplicationRoleEntity role, ApplicationPermissionEntity permission) throws AccountException {
|
||||
ApplicationRoleEntity existingRole = attach(role);
|
||||
if ((existingRole.getApplicationPermissionList() != null) && (existingRole.getApplicationPermissionList().contains(permission))) {
|
||||
existingRole.getApplicationPermissionList().remove(permission);
|
||||
}
|
||||
em.merge(role);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void setupRolePermission(List<Permission> permissions, String roleName) throws AccountException {
|
||||
ApplicationRoleEntity role = findByName(application, roleName);
|
||||
if (role == null) {
|
||||
LOGGER.error("A role with name " + roleName + " is not defined for application " + application.getApplicationName());
|
||||
} else {
|
||||
for (Permission permission : permissions) {
|
||||
ApplicationPermissionEntity existingPermission = applicationPermissionControl.findPermissionByName(application, permission.getName());
|
||||
if (existingPermission == null) {
|
||||
LOGGER.error("Required permission " + permission.getName() + " of application " + application.getApplicationName() + " does not exist. Ensure to call setupPermissions first");
|
||||
} else {
|
||||
if (role.getApplicationPermissionList().contains(existingPermission)) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Permission {} already assigned to role {} of {}, skipping", permission.getName(), roleName, application.getApplicationName());
|
||||
}
|
||||
} else {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Permission {} not assigned to role {} of {}", permission.getName(), roleName, application.getApplicationName());
|
||||
}
|
||||
addPermission(role, existingPermission);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Added permission {} to role {} of {}", permission.getName(), roleName, application.getApplicationName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.entity;
|
||||
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigEntity;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "application")
|
||||
@XmlRootElement
|
||||
@Cacheable
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "ApplicationEntity.findAll", query = "SELECT a FROM ApplicationEntity a",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ApplicationEntity.findByApplicationName", query = "SELECT a FROM ApplicationEntity a WHERE a.applicationName = :applicationName",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
|
||||
})
|
||||
|
||||
public class ApplicationEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -6407525020014743727L;
|
||||
|
||||
@Id
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(generator = "uuid2")
|
||||
@GenericGenerator(name = "uuid2", strategy = "uuid2")
|
||||
@Type(type = "pg-uuid")
|
||||
private UUID id;
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 200)
|
||||
@Column(name = "application_name", unique = true)
|
||||
private String applicationName;
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "application")
|
||||
private List<ApplicationRoleEntity> applicationRoleEntityList;
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "application")
|
||||
private List<ApplicationPermissionEntity> applicationPermissions;
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "application")
|
||||
private List<ConfigEntity> configEntityList;
|
||||
|
||||
public ApplicationEntity() {
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getApplicationName() {
|
||||
return applicationName;
|
||||
}
|
||||
|
||||
public void setApplicationName(String applicationName) {
|
||||
this.applicationName = applicationName;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public List<ApplicationRoleEntity> getApplicationRoleEntityList() {
|
||||
return applicationRoleEntityList;
|
||||
}
|
||||
|
||||
public void setApplicationRoleEntityList(List<ApplicationRoleEntity> applicationRoleEntityList) {
|
||||
this.applicationRoleEntityList = applicationRoleEntityList;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public List<ApplicationPermissionEntity> getApplicationPermissions() {
|
||||
return applicationPermissions;
|
||||
}
|
||||
|
||||
public void setApplicationPermissions(List<ApplicationPermissionEntity> applicationPermissions) {
|
||||
this.applicationPermissions = applicationPermissions;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public List<ConfigEntity> getConfigEntityList() {
|
||||
return configEntityList;
|
||||
}
|
||||
|
||||
public void setConfigEntityList(List<ConfigEntity> configEntityList) {
|
||||
this.configEntityList = configEntityList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (id != null ? id.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
// TODO: Warning - this method won't work in the case the id fields are not set
|
||||
if (!(object instanceof ApplicationEntity)) {
|
||||
return false;
|
||||
}
|
||||
ApplicationEntity other = (ApplicationEntity) object;
|
||||
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "de.muehlencord.shared.account.business.application.entity.ApplicationEntity[ id=" + id + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,16 +1,35 @@
|
||||
package de.muehlencord.shared.account.entity;
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
@ -26,11 +45,25 @@ import org.hibernate.annotations.Type;
|
||||
@Entity
|
||||
@Table(name = "application_permission")
|
||||
@XmlRootElement
|
||||
@Cacheable
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a order by a.permissionName"),
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findNotAssigned", query = "SELECT a FROM ApplicationPermissionEntity a LEFT OUTER JOIN a.applicationRoleList r WHERE r NOT IN :permissions"),
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionName", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionName = :permissionName"),
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionDescription", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionDescription = :permissionDescription")})
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application order by a.permissionName",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findNotAssigned", query = "SELECT a FROM ApplicationPermissionEntity a LEFT OUTER JOIN a.applicationRoles r WHERE a.application=:application AND r NOT IN :permissions",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionName", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application AND a.permissionName = :permissionName",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionDescription", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application AND a.permissionDescription = :permissionDescription",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
|
||||
})
|
||||
public class ApplicationPermissionEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8985982754544829534L;
|
||||
@ -53,8 +86,11 @@ public class ApplicationPermissionEntity implements Serializable {
|
||||
@Size(min = 1, max = 200)
|
||||
@Column(name = "permission_description")
|
||||
private String permissionDescription;
|
||||
@JoinColumn(name = "application", referencedColumnName = "id")
|
||||
@ManyToOne(optional = false)
|
||||
private ApplicationEntity application;
|
||||
@ManyToMany(mappedBy = "applicationPermissionList")
|
||||
private List<ApplicationRoleEntity> applicationRoleList;
|
||||
private List<ApplicationRoleEntity> applicationRoles;
|
||||
|
||||
public ApplicationPermissionEntity() {
|
||||
}
|
||||
@ -69,8 +105,16 @@ public class ApplicationPermissionEntity implements Serializable {
|
||||
this.permissionDescription = permissionDescription;
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity(UUID id, String permissionName, String permissionDescription) {
|
||||
public ApplicationPermissionEntity(ApplicationEntity application, String permissionName, String permissionDescription) {
|
||||
this.id = null;
|
||||
this.application = application;
|
||||
this.permissionName = permissionName;
|
||||
this.permissionDescription = permissionDescription;
|
||||
}
|
||||
|
||||
public ApplicationPermissionEntity(UUID id, ApplicationEntity application, String permissionName, String permissionDescription) {
|
||||
this.id = id;
|
||||
this.application = application;
|
||||
this.permissionName = permissionName;
|
||||
this.permissionDescription = permissionDescription;
|
||||
}
|
||||
@ -100,12 +144,12 @@ public class ApplicationPermissionEntity implements Serializable {
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public List<ApplicationRoleEntity> getApplicationRoleList() {
|
||||
return applicationRoleList;
|
||||
public List<ApplicationRoleEntity> getApplicationRoles() {
|
||||
return applicationRoles;
|
||||
}
|
||||
|
||||
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
|
||||
this.applicationRoleList = applicationRoleList;
|
||||
public void setApplicationRoles(List<ApplicationRoleEntity> applicationRoles) {
|
||||
this.applicationRoles = applicationRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -133,4 +177,12 @@ public class ApplicationPermissionEntity implements Serializable {
|
||||
return "de.muehlencord.shared.account.entity.ApplicationPermission[ id=" + id + " ]";
|
||||
}
|
||||
|
||||
public ApplicationEntity getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(ApplicationEntity application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +1,26 @@
|
||||
package de.muehlencord.shared.account.entity;
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.application.entity;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
@ -11,8 +28,10 @@ import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
@ -27,11 +46,22 @@ import org.hibernate.annotations.Type;
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "application_role")
|
||||
@Cacheable
|
||||
@XmlRootElement
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a ORDER BY a.roleName")
|
||||
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleName", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleName = :roleName")
|
||||
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleDescription", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleDescription = :roleDescription")})
|
||||
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application ORDER BY a.roleName",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ApplicationRoleEntity.findByRoleName", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application AND a.roleName = :roleName",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ApplicationRoleEntity.findByRoleDescription", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application AND a.roleDescription = :roleDescription",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
|
||||
})
|
||||
|
||||
public class ApplicationRoleEntity implements Serializable {
|
||||
|
||||
@ -62,22 +92,30 @@ public class ApplicationRoleEntity implements Serializable {
|
||||
@JoinColumn(name = "role_permission", referencedColumnName = "id")})
|
||||
@ManyToMany
|
||||
private List<ApplicationPermissionEntity> applicationPermissionList;
|
||||
@JoinColumn(name = "application", referencedColumnName = "id")
|
||||
@ManyToOne(optional = false)
|
||||
private ApplicationEntity application;
|
||||
|
||||
public ApplicationRoleEntity() {
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity(UUID id) {
|
||||
this.id = id;
|
||||
public ApplicationRoleEntity(ApplicationEntity application) {
|
||||
this.id = null;
|
||||
this.application = application;
|
||||
this.roleName = "";
|
||||
this.roleDescription = "";
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity(String roleName, String roleDescription) {
|
||||
public ApplicationRoleEntity(ApplicationEntity application, String roleName, String roleDescription) {
|
||||
this.id = null;
|
||||
this.application = application;
|
||||
this.roleName = roleName;
|
||||
this.roleDescription = roleDescription;
|
||||
}
|
||||
|
||||
public ApplicationRoleEntity(UUID id, String roleName, String roleDescription) {
|
||||
public ApplicationRoleEntity(UUID id, ApplicationEntity application, String roleName, String roleDescription) {
|
||||
this.id = id;
|
||||
this.application = application;
|
||||
this.roleName = roleName;
|
||||
this.roleDescription = roleDescription;
|
||||
}
|
||||
@ -124,6 +162,14 @@ public class ApplicationRoleEntity implements Serializable {
|
||||
this.applicationPermissionList = applicationPermissionList;
|
||||
}
|
||||
|
||||
public ApplicationEntity getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(ApplicationEntity application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.config.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.business.ControllerException;
|
||||
import de.muehlencord.shared.account.business.account.entity.Account;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigEntity;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigEntityPK;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigException;
|
||||
import de.muehlencord.shared.account.util.AccountPU;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.ejb.Lock;
|
||||
import javax.ejb.LockType;
|
||||
import javax.ejb.Singleton;
|
||||
import javax.ejb.Startup;
|
||||
import javax.ejb.TransactionAttribute;
|
||||
import javax.ejb.TransactionAttributeType;
|
||||
import static javax.ejb.TransactionAttributeType.REQUIRES_NEW;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.transaction.Transactional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author joern.muehlencord
|
||||
*/
|
||||
@Singleton
|
||||
@Startup
|
||||
public class ConfigService implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3195224653632853003L;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigService.class);
|
||||
|
||||
@Inject
|
||||
@AccountPU
|
||||
EntityManager em;
|
||||
|
||||
@Inject
|
||||
ApplicationEntity application;
|
||||
|
||||
/**
|
||||
* returns global config key which is not assigned to any. If more than one value is defined for the given key, the
|
||||
* key assigned to system is returned. If more than one key is defined but system key is not defined, an exception
|
||||
* is thrown.
|
||||
*
|
||||
* @param configKey the key to return
|
||||
* @return the configValue belonging to the given configKey
|
||||
* @throws de.muehlencord.shared.account.business.config.entity.ConfigException if more than one value is defined
|
||||
* for the given key but none of the values is defined for the system user
|
||||
*/
|
||||
@Lock(LockType.READ)
|
||||
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
|
||||
public String getConfigValue(String configKey) throws ConfigException {
|
||||
Query query = em.createNamedQuery("ConfigEntity.findByConfigKey");
|
||||
query.setParameter("application", application);
|
||||
query.setParameter("configKey", configKey);
|
||||
List<ConfigEntity> configList = query.getResultList();
|
||||
if ((configList == null) || (configList.isEmpty())) {
|
||||
// key is not found in the database at all
|
||||
return null;
|
||||
} else if (configList.size() == 1) {
|
||||
// exact one element found, return this one
|
||||
return configList.get(0).getConfigValue();
|
||||
} else {
|
||||
// if more than one result found, return the one which is assigned to system if present
|
||||
// if not present, throw exception
|
||||
Optional<ConfigEntity> firstItem = configList.stream()
|
||||
.filter(config -> config.getConfigPK().getConfigKeyAccount().getUsername().equals("system"))
|
||||
.findFirst();
|
||||
if (firstItem.isPresent()) {
|
||||
return firstItem.get().getConfigValue();
|
||||
} else {
|
||||
throw new ConfigException("ConfigKey " + configKey + " not unique and system value not defined");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
|
||||
public String getConfigValue(String configKey, String defaultValue) throws ConfigException, ControllerException {
|
||||
return getConfigValue(configKey, defaultValue, false);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@TransactionAttribute(REQUIRES_NEW)
|
||||
public String getConfigValue(String configKey, String defaultValue, boolean storeDefaultValue) throws ConfigException, ControllerException {
|
||||
// get configValue as usual
|
||||
String configValue = getConfigValue(configKey);
|
||||
|
||||
// if config value is not found null has been returned
|
||||
// in this case the value to return is the defaultValue
|
||||
if (configValue == null) {
|
||||
configValue = defaultValue;
|
||||
}
|
||||
|
||||
// check if the default value should be stored in the database
|
||||
if (storeDefaultValue) {
|
||||
AccountEntity account = getAccount("system");
|
||||
updateConfigValue(configKey, account, configValue);
|
||||
}
|
||||
|
||||
return configValue;
|
||||
}
|
||||
|
||||
@Lock(LockType.READ)
|
||||
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
|
||||
public String getConfigValue(String configKey, Account account, boolean fallbackToSystem) throws ConfigException {
|
||||
Query query = em.createNamedQuery("ConfigEntity.findByConfigKeyAndAccount");
|
||||
query.setParameter("application", application);
|
||||
query.setParameter("configKey", configKey);
|
||||
query.setParameter("account", account);
|
||||
List<ConfigEntity> configList = query.getResultList();
|
||||
if ((configList == null) || (configList.isEmpty())) {
|
||||
// fallback to default / system value
|
||||
if (fallbackToSystem) {
|
||||
return getConfigValue(configKey);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (configList.size() == 1) {
|
||||
// exact one element found, return this one
|
||||
return configList.get(0).getConfigValue();
|
||||
} else {
|
||||
// more than one value must not happen - this is not possible per the defintion of the datamodel
|
||||
throw new ConfigException("Cannot have more than one value for the the given key " + configKey + " and the given account " + account.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@TransactionAttribute(REQUIRES_NEW)
|
||||
public String getConfigValue(String configKey, String defaultValue, boolean storeDefaultValue, Account account, boolean fallbackToSystem) throws ConfigException, ControllerException {
|
||||
String configValue = getConfigValue(configKey, account, fallbackToSystem);
|
||||
|
||||
if (configValue == null) {
|
||||
// value not found for given account and if allowed also not found for system user
|
||||
configValue = defaultValue;
|
||||
}
|
||||
|
||||
// check if the default value should be stored in the database
|
||||
if (storeDefaultValue) {
|
||||
updateConfigValue(configKey, account, configValue);
|
||||
}
|
||||
|
||||
return configValue;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@TransactionAttribute(REQUIRES_NEW)
|
||||
public boolean updateConfigValue(String configKey, String configValue) throws ConfigException, ControllerException {
|
||||
Account account = getAccount("system");
|
||||
return updateConfigValue(configKey, account, configValue);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@TransactionAttribute(REQUIRES_NEW)
|
||||
public boolean updateConfigValue(String configKey, String accountName, String configValue) throws ControllerException {
|
||||
Account account = getAccount(accountName);
|
||||
if (accountName == null) {
|
||||
return false;
|
||||
}
|
||||
if (account == null) {
|
||||
LOGGER.error("Account for userName {} not found", accountName);
|
||||
return false;
|
||||
}
|
||||
return updateConfigValue(configKey, account, configValue);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
@TransactionAttribute(REQUIRES_NEW)
|
||||
public boolean updateConfigValue(String configKey, Account account, String configValue) throws ControllerException {
|
||||
if ((configKey == null) || (configKey.equals(""))) {
|
||||
// null or empty key
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account == null) {
|
||||
throw new ControllerException(ControllerException.CAUSE_CANNOT_PERSIST, "Account must not be null, not updating");
|
||||
}
|
||||
|
||||
AccountEntity accountEntity = getAccount(account.getUsername());
|
||||
ConfigEntityPK pk = new ConfigEntityPK(application, configKey, accountEntity);
|
||||
ConfigEntity currentEntity = em.find(ConfigEntity.class, pk);
|
||||
if (currentEntity == null) {
|
||||
currentEntity = new ConfigEntity(pk);
|
||||
currentEntity.setConfigValue(configValue);
|
||||
em.persist(currentEntity);
|
||||
return true; // config item created - udpate performed
|
||||
} else {
|
||||
if ((currentEntity.getConfigValue() != null) && (currentEntity.getConfigValue().equals(configValue))) {
|
||||
// value is the same - no update
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("configValue {} not changed, keeping {}", configKey, currentEntity.getConfigValue());
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
String oldValue = currentEntity.getConfigValue();
|
||||
currentEntity.setConfigValue(configValue);
|
||||
em.merge(currentEntity);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("configValue for {} updated from {} to {}", configKey, oldValue, configValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AccountEntity getAccount(String accountName) {
|
||||
Query query = em.createNamedQuery("AccountEntity.findByUsername");
|
||||
query.setParameter("username", accountName);
|
||||
List<AccountEntity> accountList = query.getResultList();
|
||||
if ((accountList == null) || (accountList.isEmpty())) {
|
||||
return null;
|
||||
} else {
|
||||
return accountList.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@TransactionAttribute(TransactionAttributeType.REQUIRED)
|
||||
@Transactional
|
||||
@Lock(LockType.WRITE)
|
||||
public void delete(ConfigEntity config) throws ControllerException {
|
||||
em.remove(em.merge(config));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.config.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountConfigurationKey;
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountConfigurationValue;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigException;
|
||||
import javax.ejb.EJB;
|
||||
import javax.enterprise.context.Dependent;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.enterprise.inject.spi.Annotated;
|
||||
import javax.enterprise.inject.spi.InjectionPoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Dependent
|
||||
public class ConfigurationProducer {
|
||||
|
||||
@EJB
|
||||
ConfigService configService;
|
||||
|
||||
@Produces
|
||||
@AccountConfigurationValue(key = AccountConfigurationKey.Producer)
|
||||
public String produceConfigurationValue(InjectionPoint injectionPoint) {
|
||||
Annotated annotated = injectionPoint.getAnnotated();
|
||||
AccountConfigurationValue annotation = annotated.getAnnotation(AccountConfigurationValue.class);
|
||||
if (annotation != null) {
|
||||
AccountConfigurationKey key = annotation.key();
|
||||
if (key != null) {
|
||||
try {
|
||||
switch (key) {
|
||||
case BaseUrl:
|
||||
return configService.getConfigValue("base.url");
|
||||
case PasswordResetUrl:
|
||||
return configService.getConfigValue("base.url") + "/login.xhtml";
|
||||
default:
|
||||
throw new IllegalStateException("Invalid key " + key + " for injection point: " + injectionPoint);
|
||||
}
|
||||
} catch (ConfigException ex) {
|
||||
throw new IllegalStateException("Invalid key " + key + " for injection point: " + injectionPoint + ". Exception: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No key for injection point: " + injectionPoint);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.config.entity;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.QueryHint;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.Size;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "config")
|
||||
@XmlRootElement
|
||||
@Cacheable(true)
|
||||
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Configuration")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "ConfigEntity.findAll", query = "SELECT c FROM ConfigEntity c ORDER BY c.configPK.configKey",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@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",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ConfigEntity.findByConfigKeyAndAccount", query = "SELECT c FROM ConfigEntity c WHERE c.configPK.application = :application AND c.configPK.configKey = :configKey AND c.configPK.configKeyAccount = :account",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
|
||||
@NamedQuery(name = "ConfigEntity.findByConfigValue", query = "SELECT c FROM ConfigEntity c WHERE c.configPK.application = :application AND c.configValue = :configValue",
|
||||
hints = {
|
||||
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
|
||||
})
|
||||
|
||||
public class ConfigEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2013982316933782223L;
|
||||
|
||||
@EmbeddedId
|
||||
protected ConfigEntityPK configPK;
|
||||
@Size(max = 200)
|
||||
@Column(name = "config_value")
|
||||
private String configValue;
|
||||
@Size(max = 200)
|
||||
@Column(name = "config_key_group")
|
||||
private String configKeyGroup;
|
||||
@JoinColumn(name = "config_key_account", referencedColumnName = "id", insertable = false, updatable = false)
|
||||
@ManyToOne(optional = false)
|
||||
private AccountEntity account;
|
||||
@JoinColumn(name = "application", referencedColumnName = "id", insertable = false, updatable = false)
|
||||
@ManyToOne(optional = false)
|
||||
private ApplicationEntity application;
|
||||
|
||||
public ConfigEntity() {
|
||||
}
|
||||
|
||||
public ConfigEntity(ApplicationEntity application, String configKey, AccountEntity account) {
|
||||
this.configPK = new ConfigEntityPK(application, configKey, account);
|
||||
}
|
||||
|
||||
public ConfigEntity(ConfigEntityPK configPK) {
|
||||
this.configPK = configPK;
|
||||
}
|
||||
|
||||
public ConfigEntityPK getConfigPK() {
|
||||
return configPK;
|
||||
}
|
||||
|
||||
public void setConfigPK(ConfigEntityPK configPK) {
|
||||
this.configPK = configPK;
|
||||
}
|
||||
|
||||
public String getConfigValue() {
|
||||
return configValue;
|
||||
}
|
||||
|
||||
public void setConfigValue(String configValue) {
|
||||
this.configValue = configValue;
|
||||
}
|
||||
|
||||
public String getConfigKeyGroup() {
|
||||
return configKeyGroup;
|
||||
}
|
||||
|
||||
public void setConfigKeyGroup(String configKeyGroup) {
|
||||
this.configKeyGroup = configKeyGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (configPK != null ? configPK.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
// TODO: Warning - this method won't work in the case the id fields are not set
|
||||
if (!(object instanceof ConfigEntity)) {
|
||||
return false;
|
||||
}
|
||||
ConfigEntity other = (ConfigEntity) object;
|
||||
if ((this.configPK == null && other.configPK != null) || (this.configPK != null && !this.configPK.equals(other.configPK))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "de.muehlencord.shared.account.business.config.entity.Config[ configPK=" + configPK + " ]";
|
||||
}
|
||||
|
||||
public AccountEntity getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(AccountEntity account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public ApplicationEntity getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(ApplicationEntity application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.config.entity;
|
||||
|
||||
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@Embeddable
|
||||
public class ConfigEntityPK implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8133795368429249008L;
|
||||
|
||||
@Basic(optional = false)
|
||||
@NotNull
|
||||
@Size(min = 1, max = 100)
|
||||
@Column(name = "config_key")
|
||||
private String configKey;
|
||||
@JoinColumn(name = "config_key_account", referencedColumnName = "id", insertable = false, updatable = false)
|
||||
@ManyToOne(optional = false)
|
||||
private AccountEntity configKeyAccount;
|
||||
@JoinColumn(name = "application", referencedColumnName = "id", insertable = false, updatable = false)
|
||||
@ManyToOne(optional = false)
|
||||
private ApplicationEntity application;
|
||||
|
||||
public ConfigEntityPK() {
|
||||
// empty constructor required for JPA
|
||||
}
|
||||
|
||||
public ConfigEntityPK(ApplicationEntity application, String configKey, AccountEntity configKeyAccount) {
|
||||
this.application = application;
|
||||
this.configKey = configKey;
|
||||
this.configKeyAccount = configKeyAccount;
|
||||
}
|
||||
|
||||
public String getConfigKey() {
|
||||
return configKey;
|
||||
}
|
||||
|
||||
public void setConfigKey(String configKey) {
|
||||
this.configKey = configKey;
|
||||
}
|
||||
|
||||
public AccountEntity getConfigKeyAccount() {
|
||||
return configKeyAccount;
|
||||
}
|
||||
|
||||
public void setConfigKeyAccount(AccountEntity configKeyAccount) {
|
||||
this.configKeyAccount = configKeyAccount;
|
||||
}
|
||||
|
||||
public ApplicationEntity getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(ApplicationEntity application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
hash += (configKey != null ? configKey.hashCode() : 0);
|
||||
hash += (configKeyAccount != null ? configKeyAccount.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
// TODO: Warning - this method won't work in the case the id fields are not set
|
||||
if (!(object instanceof ConfigEntityPK)) {
|
||||
return false;
|
||||
}
|
||||
ConfigEntityPK other = (ConfigEntityPK) object;
|
||||
if ((this.configKey == null && other.configKey != null) || (this.configKey != null && !this.configKey.equals(other.configKey))) {
|
||||
return false;
|
||||
}
|
||||
if ((this.configKeyAccount == null && other.configKeyAccount != null) || (this.configKeyAccount != null && !this.configKeyAccount.equals(other.configKeyAccount))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "de.muehlencord.shared.account.business.config.entity.ConfigPK[ configKey=" + configKey + ", configKeyAccount=" + configKeyAccount + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.config.entity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public class ConfigException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 7246584814637280123L;
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>ConfigException</code> without detail
|
||||
* message.
|
||||
*/
|
||||
public ConfigException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>ConfigException</code> with the specified
|
||||
* detail message.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
*/
|
||||
public ConfigException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>ConfigException</code> with the specified
|
||||
* detail message and root cause.
|
||||
*
|
||||
* @param msg the detail message.
|
||||
* @param th the root cause
|
||||
*/
|
||||
public ConfigException(String msg, Throwable th) {
|
||||
super(msg, th);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.instance.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.util.Permission;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
public enum ApplicationPermissions implements Permission {
|
||||
|
||||
APP_LIST("application:list", "Allows to list all avaiable applications"),
|
||||
APP_ADD("application:add", "Allow to add a new application"),
|
||||
APP_EDIT("application:edit", "Allow to edit an application"),
|
||||
APP_DELETE("application:delete", "Allow to delete an application"),
|
||||
PERMISSION_ADD("permission:add", "Allow to add a permission to an application"),
|
||||
PERMISSION_EDIT("permission:edit", "Allow to edit a permission"),
|
||||
PERMISSION_DELETE("permmission:delete", "Allow to delete a permission"),
|
||||
ROLE_ADD("role:add", "Allow to add a role to an application"),
|
||||
ROLE_EDIT("role:edit", "Allow to edit a role"),
|
||||
ROLE_DELETE("role:delete", "Allow to delete a role"),
|
||||
ROLE_PERMISSION_ASSIGN("role:permission:assign", "Allow to assign a permission to role"),
|
||||
ROLE_PERMISSION_REVOKE("role:permission:revoke", "All ow to revoke a permission from a role"),
|
||||
ACCOUNT_LIST ("account:list", "Allow to list all accounts of an application"),
|
||||
ACCOUNT_ADD ("account:add", "Allow to create a new account"),
|
||||
ACCOUNT_EDIT ("account:edit", "Allow to edit an existing account"),
|
||||
ACCOUNT_DELETE ("account:delete", "Allow to delete an existing account"),
|
||||
ACCOUNT_LOGIN_ADD ("account:login:add", "Allow to add a login to an account"),
|
||||
ACCOUNT_LOGIN_EDIT ("account:login:edit", "Allow to overwrite the password of an account"),
|
||||
ACCOUNT_LOGIN_DELETE ("account:login:delete", "Allow to delete the login of an account");
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
||||
private ApplicationPermissions(String permissionName, String permissionDesc) {
|
||||
this.name = permissionName;
|
||||
this.description = permissionDesc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2018 joern.muehlencord.
|
||||
*
|
||||
* 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.business.instance.boundary;
|
||||
|
||||
import de.muehlencord.shared.account.business.ControllerException;
|
||||
import de.muehlencord.shared.account.business.application.entity.ApplicationEntity;
|
||||
import de.muehlencord.shared.account.business.config.boundary.ConfigService;
|
||||
import de.muehlencord.shared.account.business.config.entity.ConfigException;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.enterprise.context.Initialized;
|
||||
import javax.enterprise.event.Observes;
|
||||
import javax.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Joern Muehlencord <joern at muehlencord.de>
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class StartupBean {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StartupBean.class);
|
||||
|
||||
@Inject
|
||||
ConfigService configService;
|
||||
|
||||
@Inject
|
||||
ApplicationEntity application;
|
||||
|
||||
public void init(@Observes @Initialized(ApplicationScoped.class) Object init) {
|
||||
if (application == null) {
|
||||
LOGGER.error("Application not initialized");
|
||||
throw new RuntimeException ("Application not initilized, validate applicationUID mapping");
|
||||
} 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
|
||||
configService.getConfigValue("account.maxFailedLogins", "5", true);
|
||||
|
||||
LOGGER.info("Application startup complete");
|
||||
} catch (ConfigException | ControllerException ex) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(ex.toString(), ex);
|
||||
} else {
|
||||
LOGGER.error(ex.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
LOGGER.info("Shutting down application {}", application.getApplicationName());
|
||||
|
||||
LOGGER.info("Application shutdown complete");
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user