149 Commits
v1.0 ... v1.1

Author SHA1 Message Date
cf433524cd [maven-release-plugin] prepare release v1.1 2019-06-05 12:36:58 +02:00
a64051dc28 removed obsolete exception factory, will use admin-faces provided one instead 2019-06-04 09:50:25 +02:00
91f8c2b2f1 added generic filter support 2019-06-03 18:06:34 +02:00
7161c32a61 Merge origin/master 2019-05-28 17:41:40 +02:00
be34fa9e8d added LDAP search support 2019-05-28 17:41:10 +02:00
4ec319fc45 fixed reset password process 2019-05-17 16:35:34 +02:00
bb3d08360e disabled caching, changes made in account ui are not reflected in application 2019-05-17 16:35:16 +02:00
336e76f536 added caching and exception handling 2019-05-01 13:52:39 +02:00
70a45b1919 updated libraries 2019-04-29 09:37:14 +02:00
59969ccef4 added checks to avoid NPEs 2019-04-29 08:13:27 +02:00
4f3c3d4673 fixed tx join interceptor handling 2019-04-27 14:07:31 +01:00
70829f9204 improved project setup 2019-04-03 23:29:51 +02:00
91b967f008 improved account controller 2019-04-03 23:29:25 +02:00
eb332461aa updated to Primefaces 7.0, JDK11 2019-04-02 16:07:21 +02:00
4fd1710503 fixed typo 2019-04-02 14:52:50 +02:00
3d1f2a93cf fixed broken permission check 2019-04-02 14:48:09 +02:00
f8fe805ba6 added missing tx annoations 2019-03-28 12:30:22 +01:00
cc7e1b5e73 introduced EndDateable 2019-03-24 17:15:06 +01:00
4aafdb1222 added Serializable 2019-03-17 13:09:04 +01:00
cdc5a2ae8c added missing libraries 2019-03-16 12:44:23 +01:00
b49fb0e19d improved templating service 2019-03-16 12:44:13 +01:00
e75523e9dd upgraded to jackson2 2019-03-16 12:41:31 +01:00
0d0984bb53 added debug code and tests 2019-03-14 17:37:56 +01:00
827b110e7c added more date util functions 2019-03-14 15:11:47 +01:00
701f0dd3a0 removed obsolete dbm backup file 2019-03-06 08:22:36 +00:00
a7e845d514 improved API key handling 2019-03-06 01:55:33 +01:00
7b315f6fd0 made JTW expiration configurable 2019-02-18 22:17:52 +01:00
70bebd4ef8 hide comments by default 2019-02-17 17:45:56 +01:00
d76154e279 added more POI helper methods 2019-02-15 17:54:53 +01:00
b30b40769c improved error logging 2019-02-14 17:28:41 +01:00
be31b12d0a added default value for StringParameters 2019-02-14 17:28:33 +01:00
03281643ad added missing ControllerExceptions 2019-02-09 14:15:26 +01:00
6117cc6c10 ensured transaction is rolled back in case a ControllerException occurs 2019-02-06 16:55:37 +01:00
479145af6f fixed broken return value of getConfigValue 2019-02-06 16:55:01 +01:00
b32b756da1 fixed missing transaction handling 2019-02-04 16:51:19 +01:00
1c6bb1769c generalized nullSafeGet 2019-02-02 14:58:58 +01:00
6933900635 minor changes 2019-02-01 17:04:53 +01:00
763d6f57f2 simplified project setup 2019-02-01 16:55:33 +01:00
2540d2a454 convert controller exception to runtime exception to ensure container rolls back transactions 2019-01-30 16:05:07 +01:00
76795365c0 updated license header 2019-01-30 00:56:33 +01:00
5c7abf4c47 added convenience methods for multiple ui messages to be displayed 2019-01-30 00:56:18 +01:00
a3e7f6c527 upgrade to POI 4.0.1 2019-01-26 13:29:24 +01:00
73bb6a18ca upgrade to POI 4.0.1 2019-01-25 17:42:38 +01:00
15dc1f9e50 added PoiUtil class 2019-01-25 17:42:09 +01:00
825aa6cbf5 added missing license header 2019-01-25 17:41:53 +01:00
9f301136f0 added getUtcDate methods 2019-01-25 17:17:29 +01:00
4daa43ad04 made applyUpdateableChanges callable from parent class 2019-01-25 17:17:06 +01:00
5ab4d99dd3 moved some debug messages to trace 2019-01-24 16:58:11 +01:00
333508632c made JWTAuthentication filter work again. Ensured realms not supporting
the JTWtoken based are not considdered when logging in via API key
2019-01-24 13:18:05 +01:00
38592887c5 optimized project structure 2019-01-22 09:55:18 +01:00
49e0c9651e added Controller Exception on delete method 2019-01-22 09:55:05 +01:00
ac2cf1b784 improved caching 2019-01-18 18:05:55 +01:00
584bdfce4f fixed broken mainmenu 2019-01-18 16:47:51 +01:00
48a6ca9231 fixed broken persistence settings 2019-01-18 16:47:32 +01:00
37290a2ed7 simplified persistence settings 2019-01-18 16:27:26 +01:00
3ba593a15c enabled caching 2019-01-18 16:26:43 +01:00
c1410554f5 removed obsolete files 2019-01-18 16:23:36 +01:00
dc2ec093d2 added method to determine wether a call is render or response request 2019-01-18 14:29:14 +01:00
1aab667a8e fixed broken persistence implementation after refactor generalization 2019-01-18 14:28:35 +01:00
2659ba1ff5 removed obsolete version from pom 2019-01-18 14:27:43 +01:00
2965c6be4b added more generalizations 2019-01-17 18:24:15 +01:00
7790a6fe50 generalizhed database connection 2019-01-16 09:50:21 +01:00
12da8c2d8c ensured deployment is stopped, if application cannot be read 2019-01-14 19:26:27 +01:00
698b4477a2 fixed NPE when Account could not be injected 2019-01-11 17:33:14 +01:00
8291d3e3aa added summary only global messages 2019-01-11 17:32:44 +01:00
ecedc1872b restructured code
enhanced permission system
2019-01-10 18:08:36 +01:00
b552e0b8bc centralized used modules 2019-01-10 15:32:10 +01:00
f4145ca2fc improved logging 2019-01-09 20:08:44 +01:00
e114dcb9eb fixed broken isEmpty implementation 2019-01-04 10:16:36 +01:00
257def9140 generalized autoResizeColumns(sheet) function 2019-01-02 16:33:19 +01:00
d8e47d1eb0 improved logging 2018-12-20 13:48:40 +01:00
df86b707a6 ensured standard API response header is returned if an APIKeyError occurs 2018-12-18 15:43:57 +01:00
c3fafa4331 fixed broken POM which included javaee-api into application scope 2018-12-17 18:14:23 +01:00
0b123bec0f updated license headers 2018-12-14 10:48:10 +01:00
452ffa4f9d added obsolete netbeans files to .gitignore 2018-12-14 10:47:59 +01:00
aaa67135a8 switched to AccountRealm support including JWT support
updated setup according to new code structure
2018-12-14 09:59:13 +01:00
7acc23892b restructured code and introduced JWTAuthenticationFilter support to ensure user and roles are loaded when using JWT to sign in 2018-12-14 09:55:32 +01:00
6bad0e75a6 fixed missing check for disabled accounts 2018-12-12 16:37:09 +01:00
7ad25dc734 allowed removal of existing config values 2018-12-12 16:36:42 +01:00
5e42012907 Merge origin/master 2018-12-12 15:42:34 +01:00
c09a27583b added missing h:form for menu 2018-12-12 15:42:10 +01:00
1fed967100 fixed imports 2018-12-12 15:42:00 +01:00
87f389fc2d ensured transactions do not interfear with other transactions. This would require a XA data source 2018-12-12 14:25:37 +01:00
057dfd9c05 ensured exchange autodiscovery is disabled for null and empty string 2018-12-12 14:24:36 +01:00
f712e269c5 fixed potential NPE 2018-11-28 17:05:42 +01:00
350d045eb0 fixed broken query in ConfigService 2018-11-25 15:14:57 +01:00
9b8284a2cf fixed update of account role mapping 2018-11-25 15:14:39 +01:00
ac39be3848 added findByName for default application 2018-11-24 17:49:02 +01:00
0b044bac78 removed obsolete version information 2018-11-24 00:00:41 +01:00
c05ba11044 started to implement permission handling into views and pages 2018-11-22 14:54:48 +01:00
79c9ab623c started to introduce permission checks into controller 2018-11-22 14:53:46 +01:00
8c11d3424e updated to hibernate 5.3.6 to support Wildfly 14 2018-11-20 13:06:36 +01:00
c822b30ca0 added ldap support 2018-11-19 17:48:34 +01:00
ed0892b1dc added missing transactional annotation 2018-11-19 17:47:09 +01:00
06d625013b added parameter to clean database before reinstall 2018-11-19 17:46:55 +01:00
4f6f851e2b added batch job to backup productive database 2018-11-19 13:45:58 +01:00
798a178f42 updated prefil script 2018-11-19 13:45:43 +01:00
1e3d2986c5 fixed injection issue when using WF14 2018-11-19 13:45:22 +01:00
4d69e8e70a added missing accountPU annotations 2018-11-17 17:13:18 +01:00
46f2827338 migrated API key support from PCD 2018-11-16 16:21:43 +01:00
b2c2619dc4 added multi persistence unit support 2018-11-16 12:43:20 +01:00
349310ccf9 updated SQL scripts 2018-11-16 12:42:41 +01:00
1bb9b24bcf removed obsolete class 2018-11-16 12:19:30 +01:00
2dc317b84f renamed datasource 2018-11-15 12:39:46 +01:00
7b1d4f24ab reogranized source code 2018-11-15 11:48:27 +01:00
389e3a6a73 optimized startup sequence 2018-11-15 10:54:34 +01:00
6f5baaaa69 ensured only users which have a role assigned to the application can login 2018-11-14 17:41:39 +01:00
6533451d06 updated .gitignore 2018-11-14 17:17:56 +01:00
765589afdf completed update of account, splitted login from account 2018-11-14 17:17:04 +01:00
939f043b01 started to introduce application to config and to split account_login from account. 2018-11-12 22:11:05 +01:00
ed63692c0c updated gitignore, removed obsolete eclipse files 2018-11-12 17:51:52 +01:00
13da4a3e04 fixed broken filter on web.xml and shiro.ini 2018-11-12 17:19:34 +01:00
7fceccc109 fixed account handling
fixed view CDI integration
2018-11-12 16:13:00 +01:00
00925aa389 completed role permission setup 2018-11-12 10:02:23 +01:00
76114f6cf2 completed role permission setup 2018-11-12 10:01:55 +01:00
d1f72db6ac started to add application support for roles 2018-11-11 16:31:33 +01:00
ea3ebdddf5 added application support for permissions 2018-11-11 15:08:37 +01:00
c5a70b9d11 added filter to support project stage 2018-11-11 15:08:09 +01:00
634b51b051 converted permission edit to dialog based
and simplified implemenation
2018-11-10 16:49:05 +01:00
17c080faa2 added create / update methods 2018-11-10 16:48:32 +01:00
9ebb649458 added first draft of mapplication support 2018-11-10 14:03:34 +01:00
c30af64604 added frist draft of account ui 2018-11-10 14:03:10 +01:00
c254d27e84 added frist draft of account ui 2018-11-10 14:02:29 +01:00
b95ffdb417 introduced possibility to fallback to different principalSuffix to combine different users together 2018-11-06 00:40:28 +01:00
14e4c2cc6e improved logging 2018-11-06 00:37:19 +01:00
f07467fd3e improved logging 2018-10-29 12:35:35 +01:00
4eb6bb77e2 updated to POI 4.0.0 2018-10-26 14:02:50 +02:00
8c4f304d18 fixed pom.xml 2018-10-22 19:07:14 +02:00
d3e0e09718 improved logging 2018-10-22 19:07:03 +02:00
95d93ec222 added Date / LocalDate conversion helper 2018-10-22 19:06:45 +02:00
4d79cbc35b ensure autodiscover is only used if hostname is not set 2018-10-16 12:34:05 +02:00
530b6f500a add valuelist support 2018-09-19 19:01:06 +02:00
8b2da09418 introduced global messages 2018-09-18 13:02:41 +02:00
64c5d3b370 introduced option to load user preferences and fallback to system preference 2018-09-17 12:00:07 +02:00
c3961f9a28 added possibility to enable / disable redirect and configure the redirect URL 2018-09-12 18:33:54 +02:00
65cdf950c9 exposed user first and lastname 2018-08-28 16:35:03 +02:00
ecf8558e92 Delete 'configuration/nb-configuration.xml' 2018-08-24 11:22:58 +00:00
81cd34817b changed generic handling 2018-08-24 13:17:32 +02:00
a0cda5f498 added support for default values 2018-08-23 17:00:30 +02:00
4b2e4e9055 fixed pom settings 2018-08-22 10:53:52 +02:00
a0d77ca744 disabled tests to avoid network issues 2018-08-22 10:52:47 +02:00
9da7004c6b generalized poi workbook handler 2018-08-22 10:52:20 +02:00
0a47471b49 fixed pom settings 2018-08-22 10:50:44 +02:00
4559f20170 introduced account based configuration 2018-08-21 18:07:51 +02:00
077ab22438 moved mail related entities 2018-08-21 18:05:39 +02:00
da46ca4c16 restructured code 2018-08-21 11:16:51 +02:00
a68f19b868 added script to create a new release 2018-08-20 23:39:44 +02:00
6a05de2b30 removed obslete code 2018-08-20 23:39:18 +02:00
1feb529c9e [maven-release-plugin] prepare for next development iteration 2018-08-20 23:25:52 +02:00
220 changed files with 14549 additions and 4379 deletions

18
.gitignore vendored
View File

@ -1,10 +1,8 @@
/configuration/target/
/network/target/
/util/target/
/sharepoint/api/target/
/security/target/
/jeeutil/target/
/account/target/
/shiro-faces/target/
/pdf/target/
/shared-poi-util/target/
/**/.settings/
**/target/
.classpath
.project
**/nbproject/
*.dump
**/_dump
**/nb-configuration.xml

5
_bin/createRelease.bat Normal file
View 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
View 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>

View File

@ -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;
}
}

View File

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

View File

@ -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;
}
}

View File

@ -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));
}
}

View 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>

View File

@ -6,6 +6,7 @@ The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<libraries xmlns="http://www.netbeans.org/ns/cdnjs-libraries/1"/>
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
@ -13,6 +14,9 @@ You can copy and paste the single properties, into the pom.xml file and the IDE
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.checkstyle.format>true</netbeans.checkstyle.format>
<org-netbeans-modules-projectapi.jsf_2e_language>Facelets</org-netbeans-modules-projectapi.jsf_2e_language>
<org-netbeans-modules-maven-j2ee.netbeans_2e_hint_2e_deploy_2e_server>WildFly</org-netbeans-modules-maven-j2ee.netbeans_2e_hint_2e_deploy_2e_server>
<netbeans.hint.license>apache20</netbeans.hint.license>
<org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder>js/libs</org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder>
</properties>
</project-shared-configuration>

150
account-ui/pom.xml Normal file
View 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>

View 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

View 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

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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"));
}
}
}

View File

@ -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
}
}
}

View File

@ -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"));
}
}
}

View 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>

View 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

View File

@ -0,0 +1,3 @@
build.version=${project.version}
build.timestamp=${timestamp}
application.uuid=${applicationUuid}

View File

@ -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

View File

@ -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

View File

@ -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

View 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>

View 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>

View 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

View 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>

View 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&nbsp;</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>

View 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>

View File

@ -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>

View 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;
}

View 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>
&copy; Joern Muehlencord - Account Management - version ${applicationController.version} - build date ${applicationController.buildDate}
</ui:composition>
</body>
</html>

View 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>

View 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>

View 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 &amp;&amp; !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 &amp;&amp; !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>

View 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 &amp;&amp; !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>

View 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>

View 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 &amp;&amp; !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>

View 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 &amp;&amp; !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>

View File

@ -1,96 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-account</artifactId>
<version>1.0</version>
<packaging>ejb</packaging>
<parent>
<artifactId>shared</artifactId>
<groupId>de.muehlencord</groupId>
<version>1.0</version>
</parent>
<name>shared-account</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-jeeutil</artifactId>
<type>jar</type>
<version>1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- create EJB version 3.1 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<ejbVersion>3.1</ejbVersion>
<excludes>
<exclude>**/persistence.xml</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-account</artifactId>
<packaging>ejb</packaging>
<parent>
<artifactId>shared</artifactId>
<groupId>de.muehlencord</groupId>
<version>1.1</version>
</parent>
<name>shared-account</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>10</maven.compiler.source>
<maven.compiler.target>10</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>shared-jeeutil</artifactId>
<type>jar</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>shared-account-dao</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.muehlencord.shared</groupId>
<artifactId>shared-util</artifactId>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- create EJB version 3.1 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<ejbVersion>3.1</ejbVersion>
<excludes>
<exclude>**/persistence.xml</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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');

View File

@ -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();

View File

@ -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
View 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
View 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 --

View 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');

View 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;

View 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;

View 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

View File

@ -1,2 +0,0 @@
\i 01_accounts.sql
\i 02_templates.sql

View 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;

View 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;

View 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;

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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.
}
}

View File

@ -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
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -1,348 +1,285 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
*
* @author joern.muehlencord
*/
@Entity
@Table(name = "account")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a"),
@NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username"),
@NamedQuery(name = "AccountEntity.findByEmailaddress", query = "SELECT a FROM AccountEntity a WHERE a.emailaddress = :emailaddress"),
@NamedQuery(name = "AccountEntity.findByFirstname", query = "SELECT a FROM AccountEntity a WHERE a.firstname = :firstname"),
@NamedQuery(name = "AccountEntity.findByLastname", query = "SELECT a FROM AccountEntity a WHERE a.lastname = :lastname"),
@NamedQuery(name = "AccountEntity.findByAccountPassword", query = "SELECT a FROM AccountEntity a WHERE a.accountPassword = :accountPassword"),
@NamedQuery(name = "AccountEntity.findByLastLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastLogin = :lastLogin"),
@NamedQuery(name = "AccountEntity.findByLastFailedLogin", query = "SELECT a FROM AccountEntity a WHERE a.lastFailedLogin = :lastFailedLogin"),
@NamedQuery(name = "AccountEntity.findByFailureCount", query = "SELECT a FROM AccountEntity a WHERE a.failureCount = :failureCount"),
@NamedQuery(name = "AccountEntity.findByStatus", query = "SELECT a FROM AccountEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountEntity.findByPasswordResetOngoing", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetOngoing = :passwordResetOngoing"),
@NamedQuery(name = "AccountEntity.findByPasswordResetValidTo", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetValidTo = :passwordResetValidTo"),
@NamedQuery(name = "AccountEntity.findByPasswordResetHash", query = "SELECT a FROM AccountEntity a WHERE a.passwordResetHash = :passwordResetHash"),
@NamedQuery(name = "AccountEntity.findByCreatedOn", query = "SELECT a FROM AccountEntity a WHERE a.createdOn = :createdOn"),
@NamedQuery(name = "AccountEntity.findByCreatedBy", query = "SELECT a FROM AccountEntity a WHERE a.createdBy = :createdBy"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
public class AccountEntity implements Serializable, Account {
private static final long serialVersionUID = 6216991757526150935L;
@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 = 32)
@Column(name = "username")
private String username;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "emailaddress")
private String emailaddress;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "firstname")
private String firstname;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "lastname")
private String lastname;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "account_password", columnDefinition = "bpchar(200)")
private String accountPassword;
@Column(name = "last_login")
@Temporal(TemporalType.TIMESTAMP)
private Date lastLogin;
@Column(name = "last_failed_login")
@Temporal(TemporalType.TIMESTAMP)
private Date lastFailedLogin;
@Basic(optional = false)
@NotNull
@Column(name = "failure_count")
private int failureCount;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 10)
@Column(name = "status")
private String status;
@Basic(optional = false)
@NotNull
@Column(name = "password_reset_ongoing")
private boolean passwordResetOngoing;
@Column(name = "password_reset_valid_to")
@Temporal(TemporalType.TIMESTAMP)
private Date passwordResetValidTo;
@Size(max = 200)
@Column(name = "password_reset_hash", columnDefinition = "bpchar(200)")
private String passwordResetHash;
@Basic(optional = false)
@NotNull
@Column(name = "created_on")
@Temporal(TemporalType.TIMESTAMP)
private Date createdOn;
@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;
@JoinTable(name = "account_role", joinColumns = {
@JoinColumn(name = "account", referencedColumnName = "id")}, inverseJoinColumns = {
@JoinColumn(name = "account_role", referencedColumnName = "id")})
@ManyToMany(fetch = FetchType.LAZY)
private List<ApplicationRoleEntity> applicationRoleList;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountId", fetch = FetchType.LAZY)
private List<AccountHistoryEntity> accountHistoryList;
public AccountEntity() {
}
public AccountEntity(UUID id) {
this.id = id;
}
public AccountEntity(UUID id, String username, String emailaddress, String firstname, String lastname, String accountPassword, int failureCount, String status, boolean passwordResetOngoing, Date createdOn, String createdBy, Date lastUpdatedOn, String lastUpdatedBy) {
this.id = id;
this.username = username;
this.emailaddress = emailaddress;
this.firstname = firstname;
this.lastname = lastname;
this.accountPassword = accountPassword;
this.failureCount = failureCount;
this.status = status;
this.passwordResetOngoing = passwordResetOngoing;
this.createdOn = createdOn;
this.createdBy = createdBy;
this.lastUpdatedOn = lastUpdatedOn;
this.lastUpdatedBy = lastUpdatedBy;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmailaddress() {
return emailaddress;
}
public void setEmailaddress(String emailaddress) {
this.emailaddress = emailaddress;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getAccountPassword() {
return accountPassword;
}
public void setAccountPassword(String accountPassword) {
this.accountPassword = accountPassword;
}
public Date getLastLogin() {
return lastLogin;
}
public void setLastLogin(Date lastLogin) {
this.lastLogin = lastLogin;
}
public Date getLastFailedLogin() {
return lastFailedLogin;
}
public void setLastFailedLogin(Date lastFailedLogin) {
this.lastFailedLogin = lastFailedLogin;
}
public int getFailureCount() {
return failureCount;
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
public String getStatus() {
return status;
}
public void setStatus(String 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() {
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;
}
@XmlTransient
public List<ApplicationRoleEntity> getApplicationRoleList() {
return applicationRoleList;
}
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
this.applicationRoleList = applicationRoleList;
}
@XmlTransient
public List<AccountHistoryEntity> getAccountHistoryList() {
return accountHistoryList;
}
public void setAccountHistoryList(List<AccountHistoryEntity> accountHistoryList) {
this.accountHistoryList = accountHistoryList;
}
@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 AccountEntity)) {
return false;
}
AccountEntity other = (AccountEntity) 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.entity.Account[ id=" + id + " ]";
}
}
/*
* Copyright 2018 joern.muehlencord.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.account.business.account.entity;
import de.muehlencord.shared.account.business.application.entity.ApplicationRoleEntity;
import de.muehlencord.shared.account.business.config.entity.ConfigEntity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
* IMPORANT: DO NOT CACHE - e.g. password changes are not synchronized
* @author joern.muehlencord
*/
@Entity
@Table(name = "account")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountEntity.findAll", query = "SELECT a FROM AccountEntity a ORDER by a.lastname, a.firstname"),
@NamedQuery(name = "AccountEntity.findByUsername", query = "SELECT a FROM AccountEntity a WHERE a.username = :username"),
@NamedQuery(name = "AccountEntity.findByStatus", query = "SELECT a FROM AccountEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountEntity.findActiveAccounts", query = "SELECT a FROM AccountEntity a WHERE a.status <> :status"),
@NamedQuery(name = "AccountEntity.findByCreatedOn", query = "SELECT a FROM AccountEntity a WHERE a.createdOn = :createdOn"),
@NamedQuery(name = "AccountEntity.findByCreatedBy", query = "SELECT a FROM AccountEntity a WHERE a.createdBy = :createdBy"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")
})
public class AccountEntity implements Serializable, Account {
private static final long serialVersionUID = 2174163529615355336L;
@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 = 32)
@Column(name = "username")
private String username;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "emailaddress")
private String emailaddress;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "firstname")
private String firstname;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 100)
@Column(name = "lastname")
private String lastname;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 10)
@Column(name = "status")
private String status;
@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;
@JoinTable(name = "account_role", joinColumns = {
@JoinColumn(name = "account", referencedColumnName = "id")}, inverseJoinColumns = {
@JoinColumn(name = "account_role", referencedColumnName = "id")})
@ManyToMany(fetch = FetchType.LAZY)
private List<ApplicationRoleEntity> applicationRoleList;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountId", fetch = FetchType.LAZY)
private List<AccountHistoryEntity> accountHistoryList;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "account")
private AccountLoginEntity accountLogin;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account")
private List<ConfigEntity> configItems;
public AccountEntity() {
// empty constructor required for JPA
}
public void addApplicationRole(ApplicationRoleEntity applicationRole) {
if (applicationRoleList == null) {
applicationRoleList = new ArrayList<>();
}
applicationRoleList.add(applicationRole);
}
/* **** getter / setter **** */
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmailaddress() {
return emailaddress;
}
public void setEmailaddress(String emailaddress) {
this.emailaddress = emailaddress;
}
@Override
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@Override
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
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 List<ApplicationRoleEntity> getApplicationRoleList() {
return applicationRoleList;
}
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
this.applicationRoleList = applicationRoleList;
}
public List<AccountHistoryEntity> getAccountHistoryList() {
return accountHistoryList;
}
public void setAccountHistoryList(List<AccountHistoryEntity> 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
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 AccountEntity)) {
return false;
}
AccountEntity other = (AccountEntity) 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.entity.Account[ id=" + id + " ]";
}
}

View File

@ -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.
}
}

View File

@ -1,172 +1,189 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Basic;
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.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
*/
@Entity
@Table(name = "account_history")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountHistoryEntity.findAll", query = "SELECT a FROM AccountHistoryEntity a"),
@NamedQuery(name = "AccountHistoryEntity.findById", query = "SELECT a FROM AccountHistoryEntity a WHERE a.id = :id"),
@NamedQuery(name = "AccountHistoryEntity.findByMessage", query = "SELECT a FROM AccountHistoryEntity a WHERE a.message = :message"),
@NamedQuery(name = "AccountHistoryEntity.findByFailureCount", query = "SELECT a FROM AccountHistoryEntity a WHERE a.failureCount = :failureCount"),
@NamedQuery(name = "AccountHistoryEntity.findByStatus", query = "SELECT a FROM AccountHistoryEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
public class AccountHistoryEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
@Size(max = 200)
@Column(name = "message")
private String message;
@Basic(optional = false)
@NotNull
@Column(name = "failure_count")
private int failureCount;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 20)
@Column(name = "status")
private String status;
@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_id", referencedColumnName = "id")
@ManyToOne(optional = false)
private AccountEntity accountId;
public AccountHistoryEntity() {
}
public AccountHistoryEntity(UUID id) {
this.id = id;
}
public AccountHistoryEntity(UUID id, int failureCount, String status, Date lastUpdatedOn, String lastUpdatedBy) {
this.id = id;
this.failureCount = failureCount;
this.status = status;
this.lastUpdatedOn = lastUpdatedOn;
this.lastUpdatedBy = lastUpdatedBy;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getFailureCount() {
return failureCount;
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
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 getAccountId() {
return accountId;
}
public void setAccountId(AccountEntity accountId) {
this.accountId = accountId;
}
@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 AccountHistoryEntity)) {
return false;
}
AccountHistoryEntity other = (AccountHistoryEntity) 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.entity.AccountHistory[ id=" + id + " ]";
}
}
/*
* 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.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
*/
@Entity
@Table(name = "account_history")
@Cacheable
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "AccountHistoryEntity.findAll", query = "SELECT a FROM AccountHistoryEntity a"),
@NamedQuery(name = "AccountHistoryEntity.findById", query = "SELECT a FROM AccountHistoryEntity a WHERE a.id = :id"),
@NamedQuery(name = "AccountHistoryEntity.findByMessage", query = "SELECT a FROM AccountHistoryEntity a WHERE a.message = :message"),
@NamedQuery(name = "AccountHistoryEntity.findByFailureCount", query = "SELECT a FROM AccountHistoryEntity a WHERE a.failureCount = :failureCount"),
@NamedQuery(name = "AccountHistoryEntity.findByStatus", query = "SELECT a FROM AccountHistoryEntity a WHERE a.status = :status"),
@NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedOn", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedOn = :lastUpdatedOn"),
@NamedQuery(name = "AccountHistoryEntity.findByLastUpdatedBy", query = "SELECT a FROM AccountHistoryEntity a WHERE a.lastUpdatedBy = :lastUpdatedBy")})
public class AccountHistoryEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
@Size(max = 200)
@Column(name = "message")
private String message;
@Basic(optional = false)
@NotNull
@Column(name = "failure_count")
private int failureCount;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 20)
@Column(name = "status")
private String status;
@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_id", referencedColumnName = "id")
@ManyToOne(optional = false)
private AccountEntity accountId;
public AccountHistoryEntity() {
}
public AccountHistoryEntity(UUID id) {
this.id = id;
}
public AccountHistoryEntity(UUID id, int failureCount, String status, Date lastUpdatedOn, String lastUpdatedBy) {
this.id = id;
this.failureCount = failureCount;
this.status = status;
this.lastUpdatedOn = lastUpdatedOn;
this.lastUpdatedBy = lastUpdatedBy;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getFailureCount() {
return failureCount;
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
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 getAccountId() {
return accountId;
}
public void setAccountId(AccountEntity accountId) {
this.accountId = accountId;
}
@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 AccountHistoryEntity)) {
return false;
}
AccountHistoryEntity other = (AccountHistoryEntity) 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.entity.AccountHistory[ id=" + id + " ]";
}
}

View File

@ -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 + " ]";
}
}

View File

@ -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;
}
}

View File

@ -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 + " ]";
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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());
}
}
}
}
}
}
}

View File

@ -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());
}
}
}
}
}
}
}

View File

@ -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 + " ]";
}
}

View File

@ -1,136 +1,188 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
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
*/
@Entity
@Table(name = "application_permission")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a order by a.permissionName"),
@NamedQuery(name = "ApplicationPermissionEntity.findNotAssigned", query = "SELECT a FROM ApplicationPermissionEntity a LEFT OUTER JOIN a.applicationRoleList r WHERE r NOT IN :permissions"),
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionName", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionName = :permissionName"),
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionDescription", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.permissionDescription = :permissionDescription")})
public class ApplicationPermissionEntity implements Serializable {
private static final long serialVersionUID = -8985982754544829534L;
@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 = 80)
@Column(name = "permission_name")
private String permissionName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "permission_description")
private String permissionDescription;
@ManyToMany(mappedBy = "applicationPermissionList")
private List<ApplicationRoleEntity> applicationRoleList;
public ApplicationPermissionEntity() {
}
public ApplicationPermissionEntity(UUID id) {
this.id = id;
}
public ApplicationPermissionEntity(String permissionName, String permissionDescription) {
this.id = null;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public ApplicationPermissionEntity(UUID id, String permissionName, String permissionDescription) {
this.id = id;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public String getPermissionDescription() {
return permissionDescription;
}
public void setPermissionDescription(String permissionDescription) {
this.permissionDescription = permissionDescription;
}
@XmlTransient
public List<ApplicationRoleEntity> getApplicationRoleList() {
return applicationRoleList;
}
public void setApplicationRoleList(List<ApplicationRoleEntity> applicationRoleList) {
this.applicationRoleList = applicationRoleList;
}
@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 ApplicationPermissionEntity)) {
return false;
}
ApplicationPermissionEntity other = (ApplicationPermissionEntity) 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.entity.ApplicationPermission[ id=" + id + " ]";
}
}
/*
* Copyright 2018 joern.muehlencord.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.account.business.application.entity;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
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
*/
@Entity
@Table(name = "application_permission")
@XmlRootElement
@Cacheable
@NamedQueries({
@NamedQuery(name = "ApplicationPermissionEntity.findAll", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application order by a.permissionName",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApplicationPermissionEntity.findNotAssigned", query = "SELECT a FROM ApplicationPermissionEntity a LEFT OUTER JOIN a.applicationRoles r WHERE a.application=:application AND r NOT IN :permissions",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionName", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application AND a.permissionName = :permissionName",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApplicationPermissionEntity.findByPermissionDescription", query = "SELECT a FROM ApplicationPermissionEntity a WHERE a.application=:application AND a.permissionDescription = :permissionDescription",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
})
public class ApplicationPermissionEntity implements Serializable {
private static final long serialVersionUID = -8985982754544829534L;
@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 = 80)
@Column(name = "permission_name")
private String permissionName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "permission_description")
private String permissionDescription;
@JoinColumn(name = "application", referencedColumnName = "id")
@ManyToOne(optional = false)
private ApplicationEntity application;
@ManyToMany(mappedBy = "applicationPermissionList")
private List<ApplicationRoleEntity> applicationRoles;
public ApplicationPermissionEntity() {
}
public ApplicationPermissionEntity(UUID id) {
this.id = id;
}
public ApplicationPermissionEntity(String permissionName, String permissionDescription) {
this.id = null;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public ApplicationPermissionEntity(ApplicationEntity application, String permissionName, String permissionDescription) {
this.id = null;
this.application = application;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public ApplicationPermissionEntity(UUID id, ApplicationEntity application, String permissionName, String permissionDescription) {
this.id = id;
this.application = application;
this.permissionName = permissionName;
this.permissionDescription = permissionDescription;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public String getPermissionDescription() {
return permissionDescription;
}
public void setPermissionDescription(String permissionDescription) {
this.permissionDescription = permissionDescription;
}
@XmlTransient
public List<ApplicationRoleEntity> getApplicationRoles() {
return applicationRoles;
}
public void setApplicationRoles(List<ApplicationRoleEntity> applicationRoles) {
this.applicationRoles = applicationRoles;
}
@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 ApplicationPermissionEntity)) {
return false;
}
ApplicationPermissionEntity other = (ApplicationPermissionEntity) 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.entity.ApplicationPermission[ id=" + id + " ]";
}
public ApplicationEntity getApplication() {
return application;
}
public void setApplication(ApplicationEntity application) {
this.application = application;
}
}

View File

@ -1,152 +1,198 @@
package de.muehlencord.shared.account.entity;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
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
*/
@Entity
@Table(name = "application_role")
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a ORDER BY a.roleName")
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleName", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleName = :roleName")
, @NamedQuery(name = "ApplicationRoleEntity.findByRoleDescription", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.roleDescription = :roleDescription")})
public class ApplicationRoleEntity implements Serializable {
private static final long serialVersionUID = -8324054525780893823L;
@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 = 80)
@Column(name = "role_name")
private String roleName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "role_description")
private String roleDescription;
@ManyToMany(mappedBy = "applicationRoleList")
private List<AccountEntity> accountList;
@JoinTable(name = "role_permission", joinColumns = {
@JoinColumn(name = "application_role", referencedColumnName = "id")}, inverseJoinColumns = {
@JoinColumn(name = "role_permission", referencedColumnName = "id")})
@ManyToMany
private List<ApplicationPermissionEntity> applicationPermissionList;
public ApplicationRoleEntity() {
}
public ApplicationRoleEntity(UUID id) {
this.id = id;
}
public ApplicationRoleEntity(String roleName, String roleDescription) {
this.id = null;
this.roleName = roleName;
this.roleDescription = roleDescription;
}
public ApplicationRoleEntity(UUID id, String roleName, String roleDescription) {
this.id = id;
this.roleName = roleName;
this.roleDescription = roleDescription;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDescription() {
return roleDescription;
}
public void setRoleDescription(String roleDescription) {
this.roleDescription = roleDescription;
}
@XmlTransient
public List<AccountEntity> getAccountList() {
return accountList;
}
public void setAccountList(List<AccountEntity> accountList) {
this.accountList = accountList;
}
@XmlTransient
public List<ApplicationPermissionEntity> getApplicationPermissionList() {
return applicationPermissionList;
}
public void setApplicationPermissionList(List<ApplicationPermissionEntity> applicationPermissionList) {
this.applicationPermissionList = applicationPermissionList;
}
@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 ApplicationRoleEntity)) {
return false;
}
ApplicationRoleEntity other = (ApplicationRoleEntity) 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.entity.ApplicationRole[ id=" + id + " ]";
}
}
/*
* Copyright 2018 joern.muehlencord.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.muehlencord.shared.account.business.application.entity;
import de.muehlencord.shared.account.business.account.entity.AccountEntity;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
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
*/
@Entity
@Table(name = "application_role")
@Cacheable
@XmlRootElement
@NamedQueries({
@NamedQuery(name = "ApplicationRoleEntity.findAll", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application ORDER BY a.roleName",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApplicationRoleEntity.findByRoleName", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application AND a.roleName = :roleName",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")}),
@NamedQuery(name = "ApplicationRoleEntity.findByRoleDescription", query = "SELECT a FROM ApplicationRoleEntity a WHERE a.application = :application AND a.roleDescription = :roleDescription",
hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")})
})
public class ApplicationRoleEntity implements Serializable {
private static final long serialVersionUID = -8324054525780893823L;
@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 = 80)
@Column(name = "role_name")
private String roleName;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 200)
@Column(name = "role_description")
private String roleDescription;
@ManyToMany(mappedBy = "applicationRoleList")
private List<AccountEntity> accountList;
@JoinTable(name = "role_permission", joinColumns = {
@JoinColumn(name = "application_role", referencedColumnName = "id")}, inverseJoinColumns = {
@JoinColumn(name = "role_permission", referencedColumnName = "id")})
@ManyToMany
private List<ApplicationPermissionEntity> applicationPermissionList;
@JoinColumn(name = "application", referencedColumnName = "id")
@ManyToOne(optional = false)
private ApplicationEntity application;
public ApplicationRoleEntity() {
}
public ApplicationRoleEntity(ApplicationEntity application) {
this.id = null;
this.application = application;
this.roleName = "";
this.roleDescription = "";
}
public ApplicationRoleEntity(ApplicationEntity application, String roleName, String roleDescription) {
this.id = null;
this.application = application;
this.roleName = roleName;
this.roleDescription = roleDescription;
}
public ApplicationRoleEntity(UUID id, ApplicationEntity application, String roleName, String roleDescription) {
this.id = id;
this.application = application;
this.roleName = roleName;
this.roleDescription = roleDescription;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleDescription() {
return roleDescription;
}
public void setRoleDescription(String roleDescription) {
this.roleDescription = roleDescription;
}
@XmlTransient
public List<AccountEntity> getAccountList() {
return accountList;
}
public void setAccountList(List<AccountEntity> accountList) {
this.accountList = accountList;
}
@XmlTransient
public List<ApplicationPermissionEntity> getApplicationPermissionList() {
return applicationPermissionList;
}
public void setApplicationPermissionList(List<ApplicationPermissionEntity> applicationPermissionList) {
this.applicationPermissionList = applicationPermissionList;
}
public ApplicationEntity getApplication() {
return application;
}
public void setApplication(ApplicationEntity application) {
this.application = application;
}
@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 ApplicationRoleEntity)) {
return false;
}
ApplicationRoleEntity other = (ApplicationRoleEntity) 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.entity.ApplicationRole[ id=" + id + " ]";
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 + " ]";
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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