initial commit
This commit is contained in:
11
dev/tekton/examples/example-bank/bank-app-backend/user-service/Dockerfile
Executable file
11
dev/tekton/examples/example-bank/bank-app-backend/user-service/Dockerfile
Executable file
@@ -0,0 +1,11 @@
|
||||
FROM open-liberty:19.0.0.12-kernel-java8-openj9
|
||||
|
||||
USER root
|
||||
RUN apt-get update && apt-get upgrade -y e2fsprogs libgnutls30 libgcrypt20 libsasl2-2
|
||||
USER 1001
|
||||
|
||||
COPY --chown=1001:0 src/main/liberty/config/ /config/
|
||||
COPY --chown=1001:0 src/main/resources/security/ /config/resources/security/
|
||||
COPY --chown=1001:0 target/*.war /config/apps/
|
||||
COPY --chown=1001:0 target/jdbc/* /config/jdbc/
|
||||
RUN configure.sh
|
||||
@@ -0,0 +1,51 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: user-service
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: user-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
containers:
|
||||
- name: user-service
|
||||
image: anthonyamanse/user-service:example-bank-1.0
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http-server
|
||||
containerPort: 9080
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: bank-db-secret
|
||||
- secretRef:
|
||||
name: bank-oidc-secret
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: user-service
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 9080
|
||||
targetPort: 9080
|
||||
selector:
|
||||
app: user-service
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: user-service
|
||||
spec:
|
||||
to:
|
||||
kind: Service
|
||||
name: user-service
|
||||
|
||||
78
dev/tekton/examples/example-bank/bank-app-backend/user-service/pom.xml
Executable file
78
dev/tekton/examples/example-bank/bank-app-backend/user-service/pom.xml
Executable file
@@ -0,0 +1,78 @@
|
||||
<?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>com.ibm.codey.bank</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>user-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Open Liberty Features -->
|
||||
<dependency>
|
||||
<groupId>io.openliberty.features</groupId>
|
||||
<artifactId>microProfile-3.0</artifactId>
|
||||
<type>esa</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ibm.codey.bank</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Add JDBC driver to package -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-jdbc-driver</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.2.8</version>
|
||||
<outputDirectory>${project.build.directory}/jdbc</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Plugin to run unit tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Plugin to run functional tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Liveness;
|
||||
|
||||
@Liveness
|
||||
@ApplicationScoped
|
||||
public class LivenessCheck implements HealthCheck {
|
||||
|
||||
private boolean isAlive() {
|
||||
// perform health checks here
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
boolean up = isAlive();
|
||||
return HealthCheckResponse.named(this.getClass().getSimpleName()).state(up).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
@ApplicationPath("/bank")
|
||||
public class LoyaltyApplication extends Application {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ibm.codey.bank;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
|
||||
import org.eclipse.microprofile.health.HealthCheck;
|
||||
import org.eclipse.microprofile.health.HealthCheckResponse;
|
||||
import org.eclipse.microprofile.health.Readiness;
|
||||
|
||||
@Readiness
|
||||
@ApplicationScoped
|
||||
public class ReadinessCheck implements HealthCheck {
|
||||
|
||||
private boolean isReady() {
|
||||
// perform readiness checks, e.g. database connection, etc.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckResponse call() {
|
||||
boolean up = isReady();
|
||||
return HealthCheckResponse.named(this.getClass().getSimpleName()).state(up).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.ibm.codey.bank.accounts;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.interceptor.Interceptors;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import com.ibm.codey.bank.BaseResource;
|
||||
import com.ibm.codey.bank.accounts.dao.UserDao;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistration;
|
||||
import com.ibm.codey.bank.accounts.json.UserRegistrationInfo;
|
||||
import com.ibm.codey.bank.accounts.models.User;
|
||||
import com.ibm.codey.bank.interceptor.LoggingInterceptor;
|
||||
|
||||
@RequestScoped
|
||||
@Interceptors(LoggingInterceptor.class)
|
||||
@Path("v1/users")
|
||||
public class UserResource extends BaseResource {
|
||||
|
||||
@Inject
|
||||
private UserDao userDAO;
|
||||
|
||||
/**
|
||||
* This method creates a new user.
|
||||
*/
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response registerUser(UserRegistration userRegistration) {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
if (userDAO.findUserByRegistryId(subject) != null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("User is already registered").build();
|
||||
}
|
||||
User newUser = new User();
|
||||
newUser.setSubject(subject);
|
||||
newUser.setConsentGiven(userRegistration.isConsentGiven());
|
||||
userDAO.createUser(newUser);
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the user registration data for a user.
|
||||
*/
|
||||
@GET
|
||||
@Path("self")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response getUser() {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
User prevUser = userDAO.findUserByRegistryId(subject);
|
||||
if (prevUser == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User is not registered").build();
|
||||
}
|
||||
UserRegistrationInfo userRegistration = new UserRegistrationInfo();
|
||||
userRegistration.setUserId(prevUser.getUserId());
|
||||
userRegistration.setConsentGiven(prevUser.isConsentGiven());
|
||||
return Response.status(Response.Status.OK).entity(userRegistration).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method updates the user registration data for a user.
|
||||
*/
|
||||
@PUT
|
||||
@Path("self")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Transactional
|
||||
public Response updateUser(UserRegistration userRegistration) {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
User prevUser = userDAO.findUserByRegistryId(subject);
|
||||
if (prevUser == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User is not registered").build();
|
||||
}
|
||||
if (prevUser.isDeleteRequested()) {
|
||||
return Response.status(Response.Status.CONFLICT).entity("User has requested deletion").build();
|
||||
}
|
||||
prevUser.setConsentGiven(userRegistration.isConsentGiven());
|
||||
userDAO.updateUser(prevUser);
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method schedules an asynchronous process to remove the user from the system.
|
||||
*/
|
||||
@DELETE
|
||||
@Path("self")
|
||||
@Transactional
|
||||
public Response deleteUser() {
|
||||
String subject = this.getCallerSubject();
|
||||
if (subject == null) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).entity("Missing subject").build();
|
||||
}
|
||||
User prevUser = userDAO.findUserByRegistryId(subject);
|
||||
if (prevUser == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).entity("User is not registered").build();
|
||||
}
|
||||
prevUser.setDeleteRequested(true);
|
||||
prevUser.setSubject(null);
|
||||
userDAO.updateUser(prevUser);
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ibm.codey.bank.accounts.dao;
|
||||
|
||||
import java.util.List;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import com.ibm.codey.bank.accounts.models.User;
|
||||
|
||||
@RequestScoped
|
||||
public class UserDao {
|
||||
|
||||
@PersistenceContext(name = "jpa-unit")
|
||||
private EntityManager em;
|
||||
|
||||
public void createUser(User user) {
|
||||
em.persist(user);
|
||||
}
|
||||
|
||||
public void updateUser(User user) {
|
||||
em.merge(user);
|
||||
}
|
||||
|
||||
public User findUserByRegistryId(String subject) {
|
||||
try {
|
||||
return em.createNamedQuery("User.findUserByRegistryId", User.class)
|
||||
.setParameter("subject", subject).getSingleResult();
|
||||
} catch(NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ibm.codey.bank.accounts.models;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "User.findUserByRegistryId", query = "SELECT e FROM User e WHERE e.subject = :subject"),
|
||||
})
|
||||
@Getter @Setter
|
||||
public class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column(name = "user_id")
|
||||
@Id
|
||||
@Setter(AccessLevel.NONE)
|
||||
private String userId;
|
||||
|
||||
@Column(name = "subject", unique=true)
|
||||
private String subject;
|
||||
|
||||
@Column(name = "consent_given")
|
||||
private boolean consentGiven;
|
||||
|
||||
@Column(name = "delete_requested")
|
||||
private boolean deleteRequested;
|
||||
|
||||
public User() {
|
||||
this.userId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
default.http.port=9080
|
||||
default.https.port=9443
|
||||
@@ -0,0 +1,2 @@
|
||||
# This option is needed when using an IBM JRE to avoid a handshake failure when making a secure JDBC connection.
|
||||
-Dcom.ibm.jsse2.overrideDefaultTLS=true
|
||||
@@ -0,0 +1,53 @@
|
||||
<server description="Liberty server">
|
||||
|
||||
<featureManager>
|
||||
<feature>jpa-2.2</feature>
|
||||
<feature>microProfile-3.0</feature>
|
||||
<feature>mpJwt-1.1</feature>
|
||||
</featureManager>
|
||||
|
||||
<logging traceSpecification="eclipselink=all" maxFileSize="20" maxFiles="10"/>
|
||||
|
||||
<keyStore id="digicertRootCA" password="digicert" location="${server.config.dir}/resources/security/digicert-root-ca.jks"/>
|
||||
<ssl id="defaultSSLConfig" keyStoreRef="defaultKeyStore" trustStoreRef="digicertRootCA" />
|
||||
|
||||
<httpEndpoint host="*" httpPort="${default.http.port}"
|
||||
httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
|
||||
|
||||
<mpJwt
|
||||
id="jwt"
|
||||
issuer="${OIDC_ISSUERIDENTIFIER}"
|
||||
jwksUri="${OIDC_JWKENDPOINTURL}"
|
||||
audiences="${OIDC_AUDIENCES}"
|
||||
userNameAttribute="sub"
|
||||
/>
|
||||
|
||||
<library id="PostgresLib">
|
||||
<fileset dir="${server.config.dir}/jdbc"/>
|
||||
</library>
|
||||
|
||||
<dataSource id="AccountsDataSource" jndiName="jdbc/AccountsDataSource">
|
||||
<jdbcDriver libraryRef="PostgresLib" />
|
||||
<!-- Idle connections to this server are timing out after 5 minutes.
|
||||
It is recommended to set maxIdleTime to half of that value to avoid jdbc failures (e.g. broken pipe).
|
||||
Reap time is reduced from default of 3 minutes to close idle connections in time. -->
|
||||
<connectionManager maxIdleTime="2m30s" reapTime="60s"/>
|
||||
<properties.postgresql
|
||||
serverName="${DB_SERVERNAME}"
|
||||
portNumber="${DB_PORTNUMBER}"
|
||||
databaseName="${DB_DATABASENAME}"
|
||||
user="${DB_USER}"
|
||||
password="${DB_PASSWORD}"
|
||||
ssl="false"
|
||||
/>
|
||||
</dataSource>
|
||||
|
||||
<webApplication location="user-service.war" contextRoot="/">
|
||||
<application-bnd>
|
||||
<security-role name="authenticated">
|
||||
<special-subject type="ALL_AUTHENTICATED_USERS"/>
|
||||
</security-role>
|
||||
</application-bnd>
|
||||
</webApplication>
|
||||
|
||||
</server>
|
||||
@@ -0,0 +1,10 @@
|
||||
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
|
||||
version="2.0">
|
||||
<persistence-unit-metadata>
|
||||
<persistence-unit-defaults>
|
||||
<schema>bank</schema>
|
||||
</persistence-unit-defaults>
|
||||
</persistence-unit-metadata>
|
||||
</entity-mappings>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<persistence version="2.2"
|
||||
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_2.xsd">
|
||||
<persistence-unit name="jpa-unit" transaction-type="JTA">
|
||||
<jta-data-source>jdbc/AccountsDataSource</jta-data-source>
|
||||
<shared-cache-mode>NONE</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="eclipselink.target-database" value="PostgreSQL"/>
|
||||
<property name="eclipselink.logging.level" value="ALL"/>
|
||||
<property name="eclipselink.logging.parameters" value="true"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
<?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"
|
||||
bean-discovery-mode="all">
|
||||
</beans>
|
||||
@@ -0,0 +1,27 @@
|
||||
<web-app
|
||||
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"
|
||||
version="3.1">
|
||||
|
||||
<display-name>user-service</display-name>
|
||||
|
||||
<security-role>
|
||||
<role-name>authenticated</role-name>
|
||||
</security-role>
|
||||
|
||||
<security-constraint>
|
||||
<display-name>Security Constraints</display-name>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>ProtectedArea</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>authenticated</role-name>
|
||||
</auth-constraint>
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>NONE</transport-guarantee>
|
||||
</user-data-constraint>
|
||||
</security-constraint>
|
||||
|
||||
</web-app>
|
||||
Reference in New Issue
Block a user