finalize 1.3.2

This commit is contained in:
Ralph Soika 2025-03-12 19:03:21 +01:00
parent 2aa5501b03
commit 216589537c
6 changed files with 441 additions and 1 deletions

View file

@ -134,6 +134,7 @@
<version>${org.imixs.office.version}</version> <version>${org.imixs.office.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@ -142,7 +143,7 @@
<webResources> <webResources>
<resource> <resource>
<filtering>true</filtering> <filtering>true</filtering>
<directory>oidc/</directory> <directory>src/oidc/</directory>
<includes> <includes>
<include>**/*</include> <include>**/*</include>
</includes> </includes>
@ -457,5 +458,13 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId>
<version>26.0.4</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -0,0 +1,9 @@
{
"client-id" : "imixs",
"provider-url" : "https://sso.alexander-logistics.office-workflow.de/realms/imixs-office-workflow",
"ssl-required" : "EXTERNAL",
"bearer-only" : "true",
"credentials" : {
"secret" : "O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V"
}
}

View file

@ -0,0 +1,8 @@
{
"client-id" : "imixs",
"provider-url" : "https://sso.alexander-logistics.office-workflow.de/realms/imixs-office-workflow",
"bearer-only" : "true",
"credentials" : {
"secret" : "O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V"
}
}

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>imixs-office-workflow</display-name>
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
<!-- -->
<context-param>
<param-name>jakarta.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>jakarta.faces.CONFIG_FILES</param-name>
<param-value>
/WEB-INF/faces-config-office.xml,
/WEB-INF/faces-config-custom.xml
</param-value>
</context-param>
<!-- project stage is computed by the maven profile settings -->
<context-param>
<param-name>jakarta.faces.PROJECT_STAGE</param-name>
<param-value>Production</param-value>
</context-param>
<!-- Session Timeout 180 = 3h -->
<session-config>
<session-timeout>60</session-timeout>
</session-config>
<!-- Facelets -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>0</load-on-startup>
<multipart-config>
<location>/tmp</location>
<!-- 20MB (20848820) *2MB (2097152)-->
<max-file-size>2097152</max-file-size>
<!-- 400MB (418018841) *20MB (20848820)-->
<max-request-size>20848820</max-request-size>
<file-size-threshold>1048576</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<!-- Error handling -->
<error-page>
<error-code>401</error-code>
<location>/loginerror.jsf</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/authorizationerror.jsf</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.jsf</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsf</location>
</error-page>
<!-- Login Modul - Form-Login
<login-config>
<auth-method>FORM</auth-method>
<realm-name>imixsrealm</realm-name>
<form-login-config>
<form-login-page>/login.jsf</form-login-page>
<form-error-page>/loginerror.jsf</form-error-page>
</form-login-config>
</login-config>
-->
<!--
<login-config>
<auth-method>OIDC</auth-method>
</login-config>
-->
<!-- Security Roles -->
<security-constraint>
<display-name>Access Manager Security Constraint</display-name>
<web-resource-collection>
<web-resource-name>AUTHENTICATED_RESOURCE</web-resource-name>
<url-pattern>/pages/*</url-pattern>
<url-pattern>/api/workflow/*</url-pattern>
<url-pattern>/api/model/*</url-pattern>
<url-pattern>/api/documents/*</url-pattern>
<url-pattern>/api/report/*</url-pattern>
<url-pattern>/api/snapshot/*</url-pattern>
<url-pattern>/api/adminp/*</url-pattern>
<url-pattern>/api/eventlog/*</url-pattern>
<url-pattern>/api/logout</url-pattern>
<!-- exclude /api/metrics and /api/wopi -->
</web-resource-collection>
<auth-constraint>
<role-name>org.imixs.ACCESSLEVEL.READERACCESS</role-name>
<role-name>org.imixs.ACCESSLEVEL.AUTHORACCESS</role-name>
<role-name>org.imixs.ACCESSLEVEL.EDITORACCESS</role-name>
<role-name>org.imixs.ACCESSLEVEL.MANAGERACCESS</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>AUTHENTICATED_ADMIN_RESOURCE</web-resource-name>
<url-pattern>/pages/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>org.imixs.ACCESSLEVEL.MANAGERACCESS</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>org.imixs.ACCESSLEVEL.NOACCESS</role-name>
</security-role>
<security-role>
<role-name>org.imixs.ACCESSLEVEL.READERACCESS</role-name>
</security-role>
<security-role>
<role-name>org.imixs.ACCESSLEVEL.AUTHORACCESS</role-name>
</security-role>
<security-role>
<role-name>org.imixs.ACCESSLEVEL.EDITORACCESS</role-name>
</security-role>
<security-role>
<role-name>org.imixs.ACCESSLEVEL.MANAGERACCESS</role-name>
</security-role>
</web-app>

View file

@ -0,0 +1,105 @@
package com.alexanderlogistics.rest;
import java.io.IOException;
import java.util.logging.Logger;
import org.imixs.melman.RestAPIException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.client.ClientRequestFilter;
import jakarta.ws.rs.core.HttpHeaders;
/**
* This RequestFilter performs a keycloak authentication.
*
*
*
* The client configuration is defined in a keycloak.json file as follows:
*
* {
* "realm": "imixs-office-workflow",
* "auth-server-url" : "https://my.keycloak.host/",
* "resource" : "the clientid",
* "credentials": {
* "secret": "xxx"
* }
* }
*
*
* @author rsoika
*
*/
public class KeycloakAuthenticator implements ClientRequestFilter {
private String token = null;
public static final String KEYCLOAK_ENDPOINT = "keycloak.endpoint";
public static final String KEYCLOAK_REALM = "keycloak.realm";
public static final String KEYCLOAK_CLIENTID = "keycloak.clientid";
public static final String KEYCLOAK_SECRET = "keycloak.secret";
private final static Logger logger = Logger.getLogger(KeycloakAuthenticator.class.getName());
// Keycloak
String keycloakServer;
String keycloakRealm;
String keycloakClientId;
String keycloakSecret;
/**
* Creates a new FormAuthenticator based on a baseUri and a username, password.
* The constructor post the user credentials to the endpoint /j_security_check
* to receive a JSESSIONID.
*
* @param _baseUri
* @param username
* @param password
* @throws RestAPIException
*/
public KeycloakAuthenticator(String token) throws RestAPIException {
this.token = token;
logger.info("token=" + token);
AccessToken accessToken;
try {
accessToken = new JWSInput(token).readJsonContent(AccessToken.class);
logger.info("---email=" + accessToken.getEmail());
} catch (JWSInputException cause) {
throw new IllegalArgumentException("Failed to deserialize token", cause);
}
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
/**
* This filter method is called for each request. The method adds teh bearer
* token into the header.
*
*/
public void filter(ClientRequestContext requestContext) throws IOException {
if (token != null && !"".equals(token)) {
logger.info(" add authroization header....v2");
// requestContext.getHeaders().add("Bearer", getToken());
// Authorization: bearer $TOKEN
// requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, getToken());
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + getToken());
}
}
}

View file

@ -0,0 +1,166 @@
package com.alexanderlogistics.rest;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import org.apache.http.impl.client.HttpClients;
import org.imixs.melman.RestAPIException;
import org.imixs.melman.WorkflowClient;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.exceptions.PluginException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import com.oracle.truffle.api.library.ExportMessage.Ignore;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response;
/**
* Test to request data via keycloak authentication....
*
*
* Curl Beispiel:
* https://stackoverflow.com/questions/50256433/obtaining-an-access-token-via-curl
*
*
* Beispiel:
*
* curl \
* -d "client_id=imixs" \
* -d "client_secret=O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V" \
* -d "grant_type=client_credentials" \
* "https://sso.alexander-logistics.office-workflow.de/realms/imixs-office-workflow/protocol/openid-connect/token"
*
*
*
* curl -X GET
* "https://test.alexander-logistics.office-workflow.de/api/documents/xxxxxx" \
* -H "Authorization: Bearer
* eyJhbGciOiJSUzI1NiIsInR5cCIxxxxxxxxxxxxxlvPDoPf4l5w"
*
*
*
*
* curl -c cookies.txt -b cookies.txt -v -H "Authorization: Bearer
* eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQT0g5Q0RPOUJjdGxEZFBVc3RlSzR4blVCM0VQNThKOENqaXdJb2hIUFRFIn0.eyJleHAiOjE3NDE2ODAwNDEsImlhdCI6MTc0MTY3OTc0MSwianRpIjoiMTJhZWMzZmItMDZlYy00ODY1LWI2MTAtYzBhNDczNjk1MWViIiwiaXNzIjoiaHR0cHM6Ly9zc28uYWxleGFuZGVyLWxvZ2lzdGljcy5vZmZpY2Utd29ya2Zsb3cuZGUvcmVhbG1zL2ltaXhzLW9mZmljZS13b3JrZmxvdyIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiIyYjAyMTJiMy0wMTE4LTQ1ZmMtYjJiOS05ZjM4MmUyODlkMDgiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJpbWl4cyIsInNlc3Npb25fc3RhdGUiOiI2MWVlZWEwYy01ODRiLTRhMTctYjg3ZC0zNGE0NzY5Mzg3YmYiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vdGVzdC5hbGV4YW5kZXItbG9naXN0aWNzLm9mZmljZS13b3JrZmxvdy5kZSJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1pbWl4cy1vZmZpY2Utd29ya2Zsb3ciLCJvZmZsaW5lX2FjY2VzcyIsIm9yZy5pbWl4cy5BQ0NFU1NMRVZFTC5BVVRIT1JBQ0NFU1MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjYxZWVlYTBjLTU4NGItNGExNy1iODdkLTM0YTQ3NjkzODdiZiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYW1lIjoiQW5uYSBNdXN0ZXJtYW5uIiwiZ3JvdXBzIjpbImRlZmF1bHQtcm9sZXMtaW1peHMtb2ZmaWNlLXdvcmtmbG93Iiwib2ZmbGluZV9hY2Nlc3MiLCJvcmcuaW1peHMuQUNDRVNTTEVWRUwuQVVUSE9SQUNDRVNTIiwidW1hX2F1dGhvcml6YXRpb24iXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiYW5uYSIsImdpdmVuX25hbWUiOiJBbm5hIiwiZmFtaWx5X25hbWUiOiJNdXN0ZXJtYW5uIiwiZW1haWwiOiJhbm5hQGltaXhzLmNvbSJ9.oitC9iBQSBEtZl3b65nWOSCwJ3MQjS0ES_YYJ4faL4IKZ3hN2XLFOL1kjW_guDF7Rg1k7dYm2WRAyI-QH1oXit7ANW-Z4DBpC0lbwepOsE5a5trjospYFtiLX5STO0WZuosa3b8XV7-F97Ru8Atox2eOqbJ4M-E_swnBjVxHvwLYIJXUBkQM_VXiF0acmwmcIjgolOl-mTC0NnnBClsZzbPfWxz0Ds2lc2lsQZQVjEaJ0mrMw3XCmf7rNC1LmSu6oJ8yiZ2UPMkkXDIRq0KV-hQ2sBqdfZdr4yFdbszXHLL0bDJixeS8xkBRkB7c0JSAxAgDmML1Y8Iuc7NuV6mg8Q"
* "https://test.alexander-logistics.office-workflow.de/api/documents/4eea4e54-be93-4297-9574-3a61e826b4f4"
*
*
*
*
*
* curl POST \
* -H "Content-Type: application/x-www-form-urlencoded" \
* -d "grant_type=password" \
* -d "client_id=imixs" \
* -d "client_secret=O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V" \
* -d "username=anna" \
* -d "password=anna" \
* "https://sso.alexander-logistics.office-workflow.de/realms/imixs-office-workflow/protocol/openid-connect/token"
*
*
* curl -d 'client_id=imixs' -d 'username=anna' -d 'password=anna' -d
* 'grant_type=password' -d 'client_secret=O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V' \
* 'https://sso.alexander-logistics.office-workflow.de/realms/imixs-office-workflow/protocol/openid-connect/token'
* | \
* python -m json.tool
*/
@Ignore
public class TestKeycloakClient {
private static Logger logger = Logger.getLogger(TestKeycloakClient.class.getName());
private static final String KEYCLOAK_TOKEN_URL = "https://sso.alexander-logistics.office-workflow.de/auth/realms/imixs-office-workflow/protocol/openid-connect/token";
private static final String CLIENT_ID = "imixs";
private static final String CLIENT_SECRET = "O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V";
private static final String USERNAME = "anna";
private static final String PASSWORD = "anna";
private String token = null;
@Before
public void setup() throws PluginException {
try {
final Configuration configuration = new Configuration(
"https://sso.alexander-logistics.office-workflow.de/",
"imixs-office-workflow", // keycloak realm
"imixs", // keycloak client
Collections.singletonMap("secret", "O8YjWcoW2nxb7jIgBePBKMvoDLUruZ3V"),
HttpClients.createDefault());
token = AuthzClient.create(configuration).obtainAccessToken("anna", "anna").getToken();
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
// "Could not obtain configuration from server
// [https://sso.alexander-logistics.office-workflow.de/auth/realms/imixs-office-workflow/.well-known/uma2-configuration]."
// https://sso.alexander-logistics.office-workflow.de/realms/imixs-office-workflow/.well-known/openid-configuration
throw new IllegalArgumentException("Token can't be obtained", e);
}
}
/**
* Test token....
*
*/
@Test
public void testToken() {
System.out.println("Token = " + token);
String instanceEndpoint = "https://test.alexander-logistics.office-workflow.de/api/";
String requestUri = "documents/6df8a656-9188-48a3-a0e0-c3a6274a586a";
WorkflowClient documentClient = new WorkflowClient(instanceEndpoint);
KeycloakAuthenticator keycloakAuth;
try {
keycloakAuth = new KeycloakAuthenticator(token);
documentClient.registerClientRequestFilter(keycloakAuth);
WebTarget target = documentClient.getWebTarget(requestUri);
// Erstelle eine Invocation von der WebTarget
Invocation.Builder invocationBuilder = target.request();
Invocation invocation = invocationBuilder.buildGet();
// Sende die Anfrage und erhalte die Antwort
Response response = invocation.invoke();
// Überprüfe den Status der Antwort
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
// Extrahiere den Inhalt der Antwort als String
String responseBody = response.readEntity(String.class);
// Gib den Inhalt aus (z.B. auf der Konsole)
System.out.println("Response Body: " + responseBody);
} else {
System.err.println("Fehler bei der Anfrage: " + response.getStatus());
}
// Schließe die Response, um Ressourcen freizugeben
response.close();
List<ItemCollection> result = documentClient.getCustomResource(requestUri);
logger.info(" result size = " + result.size());
} catch (RestAPIException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Assert.fail();
}
}
}