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/
|
/**/.settings/
|
||||||
/network/target/
|
**/target/
|
||||||
/util/target/
|
.classpath
|
||||||
/sharepoint/api/target/
|
.project
|
||||||
/security/target/
|
**/nbproject/
|
||||||
/jeeutil/target/
|
*.dump
|
||||||
/account/target/
|
**/_dump
|
||||||
/shiro-faces/target/
|
**/nb-configuration.xml
|
||||||
/pdf/target/
|
|
||||||
/shared-poi-util/target/
|
|
||||||
|
|||||||
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.
|
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.
|
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 xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||||
<!--
|
<!--
|
||||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
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).
|
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.
|
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>
|
</properties>
|
||||||
</project-shared-configuration>
|
</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>
|
<groupId>de.muehlencord.shared</groupId>
|
||||||
<artifactId>shared-account</artifactId>
|
<artifactId>shared-account</artifactId>
|
||||||
<version>1.0</version>
|
|
||||||
<packaging>ejb</packaging>
|
<packaging>ejb</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>shared</artifactId>
|
<artifactId>shared</artifactId>
|
||||||
<groupId>de.muehlencord</groupId>
|
<groupId>de.muehlencord</groupId>
|
||||||
<version>1.0</version>
|
<version>1.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>shared-account</name>
|
<name>shared-account</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>10</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>10</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -41,10 +40,13 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.muehlencord.shared</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<artifactId>shared-jeeutil</artifactId>
|
<artifactId>shared-jeeutil</artifactId>
|
||||||
<type>jar</type>
|
<type>jar</type>
|
||||||
<version>1.0</version>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>shared-account-dao</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
@ -66,9 +68,23 @@
|
|||||||
<artifactId>jcl-over-slf4j</artifactId>
|
<artifactId>jcl-over-slf4j</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>javax</groupId>
|
<groupId>javax</groupId>
|
||||||
<artifactId>javaee-api</artifactId>
|
<artifactId>javaee-api</artifactId>
|
||||||
|
<type>jar</type>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<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.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -17,44 +35,36 @@ import javax.persistence.ManyToMany;
|
|||||||
import javax.persistence.NamedQueries;
|
import javax.persistence.NamedQueries;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* IMPORANT: DO NOT CACHE - e.g. password changes are not synchronized
|
||||||
* @author joern.muehlencord
|
* @author joern.muehlencord
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "account")
|
@Table(name = "account")
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
@NamedQueries({
|
@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.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.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.findActiveAccounts", query = "SELECT a FROM AccountEntity a WHERE a.status <> :status"),
|
||||||
@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.findByCreatedOn", query = "SELECT a FROM AccountEntity a WHERE a.createdOn = :createdOn"),
|
@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.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.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 {
|
public class AccountEntity implements Serializable, Account {
|
||||||
|
|
||||||
private static final long serialVersionUID = 6216991757526150935L;
|
private static final long serialVersionUID = 2174163529615355336L;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Basic(optional = false)
|
@Basic(optional = false)
|
||||||
@ -86,36 +96,11 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
private String lastname;
|
private String lastname;
|
||||||
@Basic(optional = false)
|
@Basic(optional = false)
|
||||||
@NotNull
|
@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)
|
@Size(min = 1, max = 10)
|
||||||
@Column(name = "status")
|
@Column(name = "status")
|
||||||
private String status;
|
private String status;
|
||||||
@Basic(optional = false)
|
@Basic(optional = false)
|
||||||
@NotNull
|
@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")
|
@Column(name = "created_on")
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date createdOn;
|
private Date createdOn;
|
||||||
@ -141,30 +126,23 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
private List<ApplicationRoleEntity> applicationRoleList;
|
private List<ApplicationRoleEntity> applicationRoleList;
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountId", fetch = FetchType.LAZY)
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountId", fetch = FetchType.LAZY)
|
||||||
private List<AccountHistoryEntity> accountHistoryList;
|
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() {
|
public AccountEntity() {
|
||||||
|
// empty constructor required for JPA
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountEntity(UUID id) {
|
public void addApplicationRole(ApplicationRoleEntity applicationRole) {
|
||||||
this.id = id;
|
if (applicationRoleList == null) {
|
||||||
}
|
applicationRoleList = new ArrayList<>();
|
||||||
|
}
|
||||||
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) {
|
applicationRoleList.add(applicationRole);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* **** getter / setter **** */
|
||||||
public UUID getId() {
|
public UUID getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -190,6 +168,7 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.emailaddress = emailaddress;
|
this.emailaddress = emailaddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getFirstname() {
|
public String getFirstname() {
|
||||||
return firstname;
|
return firstname;
|
||||||
}
|
}
|
||||||
@ -198,6 +177,7 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.firstname = firstname;
|
this.firstname = firstname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getLastname() {
|
public String getLastname() {
|
||||||
return lastname;
|
return lastname;
|
||||||
}
|
}
|
||||||
@ -206,38 +186,6 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.lastname = lastname;
|
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() {
|
public String getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -246,30 +194,6 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.status = status;
|
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() {
|
public Date getCreatedOn() {
|
||||||
return createdOn;
|
return createdOn;
|
||||||
}
|
}
|
||||||
@ -302,7 +226,6 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.lastUpdatedBy = lastUpdatedBy;
|
this.lastUpdatedBy = lastUpdatedBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlTransient
|
|
||||||
public List<ApplicationRoleEntity> getApplicationRoleList() {
|
public List<ApplicationRoleEntity> getApplicationRoleList() {
|
||||||
return applicationRoleList;
|
return applicationRoleList;
|
||||||
}
|
}
|
||||||
@ -311,7 +234,6 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.applicationRoleList = applicationRoleList;
|
this.applicationRoleList = applicationRoleList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlTransient
|
|
||||||
public List<AccountHistoryEntity> getAccountHistoryList() {
|
public List<AccountHistoryEntity> getAccountHistoryList() {
|
||||||
return accountHistoryList;
|
return accountHistoryList;
|
||||||
}
|
}
|
||||||
@ -320,6 +242,22 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
this.accountHistoryList = accountHistoryList;
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 0;
|
int hash = 0;
|
||||||
@ -344,5 +282,4 @@ public class AccountEntity implements Serializable, Account {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "de.muehlencord.shared.account.entity.Account[ id=" + id + " ]";
|
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.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.persistence.Basic;
|
import javax.persistence.Basic;
|
||||||
|
import javax.persistence.Cacheable;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
@ -27,6 +43,7 @@ import org.hibernate.annotations.Type;
|
|||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "account_history")
|
@Table(name = "account_history")
|
||||||
|
@Cacheable
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "AccountHistoryEntity.findAll", query = "SELECT a FROM AccountHistoryEntity a"),
|
@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.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.persistence.Basic;
|
import javax.persistence.Basic;
|
||||||
|
import javax.persistence.Cacheable;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToMany;
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.NamedQueries;
|
import javax.persistence.NamedQueries;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.QueryHint;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
@ -26,11 +45,25 @@ import org.hibernate.annotations.Type;
|
|||||||
@Entity
|
@Entity
|
||||||
@Table(name = "application_permission")
|
@Table(name = "application_permission")
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
|
@Cacheable
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a order by a.permissionName"),
|
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application 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"),
|
hints = {
|
||||||
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionName", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionName = :permissionName"),
|
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
|
||||||
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionDescription", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionDescription = :permissionDescription")})
|
@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 {
|
public class ApplicationPermissionEntity implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -8985982754544829534L;
|
private static final long serialVersionUID = -8985982754544829534L;
|
||||||
@ -53,8 +86,11 @@ public class ApplicationPermissionEntity implements Serializable {
|
|||||||
@Size(min = 1, max = 200)
|
@Size(min = 1, max = 200)
|
||||||
@Column(name = "permission_description")
|
@Column(name = "permission_description")
|
||||||
private String permissionDescription;
|
private String permissionDescription;
|
||||||
|
@JoinColumn(name = "application", referencedColumnName = "id")
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
private ApplicationEntity application;
|
||||||
@ManyToMany(mappedBy = "applicationPermissionList")
|
@ManyToMany(mappedBy = "applicationPermissionList")
|
||||||
private List<ApplicationRoleEntity> applicationRoleList;
|
private List<ApplicationRoleEntity> applicationRoles;
|
||||||
|
|
||||||
public ApplicationPermissionEntity() {
|
public ApplicationPermissionEntity() {
|
||||||
}
|
}
|
||||||
@ -69,8 +105,16 @@ public class ApplicationPermissionEntity implements Serializable {
|
|||||||
this.permissionDescription = permissionDescription;
|
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.id = id;
|
||||||
|
this.application = application;
|
||||||
this.permissionName = permissionName;
|
this.permissionName = permissionName;
|
||||||
this.permissionDescription = permissionDescription;
|
this.permissionDescription = permissionDescription;
|
||||||
}
|
}
|
||||||
@ -100,12 +144,12 @@ public class ApplicationPermissionEntity implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
public List<ApplicationRoleEntity> getApplicationRoleList() {
|
public List<ApplicationRoleEntity> getApplicationRoles() {
|
||||||
return applicationRoleList;
|
return applicationRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
|
public void setApplicationRoles(List<ApplicationRoleEntity> applicationRoles) {
|
||||||
this.applicationRoleList = applicationRoleList;
|
this.applicationRoles = applicationRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -133,4 +177,12 @@ public class ApplicationPermissionEntity implements Serializable {
|
|||||||
return "de.muehlencord.shared.account.entity.ApplicationPermission[ id=" + id + " ]";
|
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.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.persistence.Basic;
|
import javax.persistence.Basic;
|
||||||
|
import javax.persistence.Cacheable;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
@ -11,8 +28,10 @@ import javax.persistence.Id;
|
|||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.JoinTable;
|
import javax.persistence.JoinTable;
|
||||||
import javax.persistence.ManyToMany;
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.NamedQueries;
|
import javax.persistence.NamedQueries;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.QueryHint;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
@ -27,11 +46,22 @@ import org.hibernate.annotations.Type;
|
|||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "application_role")
|
@Table(name = "application_role")
|
||||||
|
@Cacheable
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a ORDER BY a.roleName")
|
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application ORDER BY a.roleName",
|
||||||
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleName", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleName = :roleName")
|
hints = {
|
||||||
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleDescription", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleDescription = :roleDescription")})
|
@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 {
|
public class ApplicationRoleEntity implements Serializable {
|
||||||
|
|
||||||
@ -62,22 +92,30 @@ public class ApplicationRoleEntity implements Serializable {
|
|||||||
@JoinColumn(name = "role_permission", referencedColumnName = "id")})
|
@JoinColumn(name = "role_permission", referencedColumnName = "id")})
|
||||||
@ManyToMany
|
@ManyToMany
|
||||||
private List<ApplicationPermissionEntity> applicationPermissionList;
|
private List<ApplicationPermissionEntity> applicationPermissionList;
|
||||||
|
@JoinColumn(name = "application", referencedColumnName = "id")
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
private ApplicationEntity application;
|
||||||
|
|
||||||
public ApplicationRoleEntity() {
|
public ApplicationRoleEntity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationRoleEntity(UUID id) {
|
public ApplicationRoleEntity(ApplicationEntity application) {
|
||||||
this.id = id;
|
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.id = null;
|
||||||
|
this.application = application;
|
||||||
this.roleName = roleName;
|
this.roleName = roleName;
|
||||||
this.roleDescription = roleDescription;
|
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.id = id;
|
||||||
|
this.application = application;
|
||||||
this.roleName = roleName;
|
this.roleName = roleName;
|
||||||
this.roleDescription = roleDescription;
|
this.roleDescription = roleDescription;
|
||||||
}
|
}
|
||||||
@ -124,6 +162,14 @@ public class ApplicationRoleEntity implements Serializable {
|
|||||||
this.applicationPermissionList = applicationPermissionList;
|
this.applicationPermissionList = applicationPermissionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApplicationEntity getApplication() {
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApplication(ApplicationEntity application) {
|
||||||
|
this.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hash = 0;
|
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