Commit b5a54112 authored by redmitry@list.ru's avatar redmitry@list.ru

add security authorizers

parent 61b87f9b
### OpenEBench REST API for submission and querying benchmarking data
###### Enterprise Java 8 (JEE8) Platform
OpenEBench REST API is strictly adherent to JEE8 specification.
The API is developed and deployed on [WildFly 17.1](http://wildfly.org/) server,
but should run on other servers (i.e. [Apache Tomcat](http://tomcat.apache.org/)).
###### MongoDB
The data is stored in [MongoDB](www.mongodb.com)
###### Apache Maven build system
To simplify build process API uses [Apache Maven](https://maven.apache.org/) build system.
To build OpenEBench REST application:
Compiling:
```shell
>git clone https://gitlab.bsc.es/inb/elixir/openebench/openebench-rest-api.git
>cd openebench-rest-api
>mvn install
```
##### REST API
- __GET__ /{collection} ```ACCEPT: application/json``` return an array of all accessible collection data objects
- __GET__ /{collection} ```ACCEPT: text/uri-list``` returns a plain list of data identifiers found in the collection
---
- __GET__ /{collection}/{id} return data object reffered by its id
- __HEAD__ /{collection}/{id} check whether the object exists
##### SECURITY
OpenEBench REST API filters output according permissions assigned to the authenticated user.
If no authentication (OpenID Connect token) data provided, only publicly available data is returned.
![](oeb-authz.png) "*Simple security chart*"
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
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.
-->
<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.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
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.
-->
<org-netbeans-modules-maven-jaxws.rest_2e_config_2e_type>ide</org-netbeans-modules-maven-jaxws.rest_2e_config_2e_type>
</properties>
</project-shared-configuration>
<?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>es.bsc.inb.elixir</groupId>
<artifactId>openebench-rest-api</artifactId>
<version>0.2.0</version>
<packaging>war</packaging>
<name>OpenEBench Data Access API</name>
<organization>
<name>Barcelona Supercomputing Center</name>
<url>https://www.bsc.es/</url>
</organization>
<developers>
<developer>
<id>redmitry</id>
<name>Dmitry Repchevsky</name>
<email>redmitry@list.ru</email>
</developer>
</developers>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.enterprise.concurrent</groupId>
<artifactId>javax.enterprise.concurrent-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>es.bsc.inb.elixir</groupId>
<artifactId>openebench-data-model</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>es.elixir.bsc.json.schema</groupId>
<artifactId>jaronuinga</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.12.5</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.0.10</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>inb-bsc-maven</id>
<url>https://gitlab.bsc.es/inb/maven/raw/master</url>
</repository>
<repository>
<id>jaronuinga</id>
<url>https://raw.github.com/inab/jaronuinga/maven/</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<webResources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</webResources>
<failOnMissingWebXml>false</failOnMissingWebXml>
</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>es.bsc.inb.elixir</groupId>
<artifactId>openebench-rest-api</artifactId>
<version>0.9.0</version>
<packaging>war</packaging>
<name>OpenEBench REST API</name>
<organization>
<name>Barcelona Supercomputing Center</name>
<url>https://www.bsc.es/</url>
</organization>
<developers>
<developer>
<id>redmitry</id>
<name>Dmitry Repchevsky</name>
<email>redmitry@list.ru</email>
</developer>
</developers>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.enterprise.concurrent</groupId>
<artifactId>javax.enterprise.concurrent-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<version>11.0.2</version>
</dependency>
<dependency>
<groupId>es.bsc.inb.elixir</groupId>
<artifactId>openebench-data-model</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>es.elixir.bsc.json.schema</groupId>
<artifactId>jaronuinga</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.12.5</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.0.10</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>inb-bsc-maven</id>
<url>https://gitlab.bsc.es/inb/maven/raw/master</url>
</repository>
<repository>
<id>jaronuinga</id>
<url>https://raw.github.com/inab/jaronuinga/maven/</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<webResources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</webResources>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
/**
* *****************************************************************************
* Copyright (C) 2020 ELIXIR ES, Spanish National Bioinformatics Institute (INB)
* and Barcelona Supercomputing Center (BSC)
*
* Modifications to the initial code base are copyright of their respective
* authors, or their employers as appropriate.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*****************************************************************************
*/
package es.bsc.inb.elixir.openebench.rest;
import es.bsc.inb.elixir.openebench.rest.dao.ProductionDatabase;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.Path;
/**
* @author Dmitry Repchevsky
*/
@Path("/access")
@RequestScoped
public class OEBProductionService extends OpenEBenchService {
@Inject
protected ProductionDatabase production;
@Override
public ProductionDatabase dao() {
return production;
}
}
/**
* *****************************************************************************
* Copyright (C) 2020 ELIXIR ES, Spanish National Bioinformatics Institute (INB)
* and Barcelona Supercomputing Center (BSC)
*
* Modifications to the initial code base are copyright of their respective
* authors, or their employers as appropriate.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*****************************************************************************
*/
package es.bsc.inb.elixir.openebench.rest;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import es.bsc.inb.elixir.openebench.rest.auth.Authorization;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.ws.rs.Path;
/**
* @author Dmitry Repchevsky
*/
@Path("/query")
@RequestScoped
public class OEBQueryService {
}
/**
* *****************************************************************************
* Copyright (C) 2020 ELIXIR ES, Spanish National Bioinformatics Institute (INB)
* and Barcelona Supercomputing Center (BSC)
*
* Modifications to the initial code base are copyright of their respective
* authors, or their employers as appropriate.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*****************************************************************************
*/
package es.bsc.inb.elixir.openebench.rest;
import java.net.URI;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.JsonValue.ValueType;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
/**
* @author Dmitry Repchevsky
*/
public class OpenEBenchKeysValidationBean {
@Inject private ServletContext context;
@Inject private HttpServletRequest request;
public boolean validatePKs(final Map<String, String> values,
final JsonObject object,
final String community) {
final JsonString _schema = object.getJsonString("_schema");
final String[] path = _schema.getString().split("/");
final String collection = path[path.length - 1];
for(String primary_key : values.values()) {
if (isValid(new StringBuilder(primary_key), collection, community)) {
return false;
}
}
return true;
}
public boolean validateFKs(final StringBuilder value,
final JsonArray fkeys,
final String community) {
for (int i = 0, n = fkeys.size(); i < n; i++) {
final JsonValue fk_schema = fkeys.get(i);
if (ValueType.OBJECT == fk_schema.getValueType()) {
if (!isValid(value, fk_schema.asJsonObject(), community)) {
return false;
}
} else {
Logger.getLogger(OpenEBenchKeysValidationBean.class.getName()).log(Level.SEVERE, "foreign_key is not an object");
}
}
return true;
}
private boolean isValid(final StringBuilder value,
final JsonObject fk_schema,
final String community) {
final JsonValue _id = fk_schema.get("schema_id");
if (_id == null) {
Logger.getLogger(OpenEBenchKeysValidationBean.class.getName()).log(Level.SEVERE, "schema_id == null");
return false;
}
if (ValueType.STRING != _id.getValueType()) {
Logger.getLogger(OpenEBenchKeysValidationBean.class.getName()).log(Level.SEVERE, "schema_id must be STRING");
return false;
}
return isValid(value, ((JsonString)_id).getString(), community);
}
private boolean isValid(final StringBuilder value,
final String collection,
final String community) {
final String id = value.toString();
final String _id = getId(community, collection, id);
if (_id == null) {
return false;
}
if (!id.equals(_id)) {
value.setLength(0);
value.append(_id);
}
URI uri = URI.create(request.getRequestURL().toString());
UriBuilder builder = UriBuilder.fromUri(uri.resolve(context.getContextPath())).path(OEBProductionService.class).path(collection).path(_id);
Response response = ClientBuilder.newClient().target(builder).request(MediaType.APPLICATION_JSON).head();
if (response.getStatus() != 404) {
return true;
}
builder = UriBuilder.fromUri(uri.resolve(context.getContextPath())).path(OEBProvisionalService.class).path(collection).path(_id);
response = ClientBuilder.newClient().target(builder).request(MediaType.APPLICATION_JSON).head();
return response.getStatus() != 404;
}
public static String getId(final String communicty_code,
final String collection,
final String id) {
final char ch;
switch(collection) {
case "Dataset": ch = 'D'; break;
case "BenchmarkingEvent": ch = 'E'; break;
case "Challenge": ch = 'X'; break;
case "Metrics": ch = 'M'; break;
case "MetricsCategory": ch = 'Y'; break;
case "TestAction": ch = 'A'; break;
case "Tool": ch = 'T'; break;
case "Community": return id.startsWith("OEB") && id.length() == 7 ? id
: "OEBC" + communicty_code;
default: return id;
}
if (id.startsWith("OEB")) {
return id.length() == 14 ? id : null;
}
int hash = 7;
for (int i = 0; i < id.length(); i++) {
hash = hash * 31 + id.charAt(i);
}
final String code = Integer.toString(Math.abs(hash), 36).toUpperCase();
return "OEB" + ch + communicty_code + "t" + "000000".substring(code.length()) + code;
}
}
/**
* *****************************************************************************
* Copyright (C) 2020 ELIXIR ES, Spanish National Bioinformatics Institute (INB)
* and Barcelona Supercomputing Center (BSC)
*
* Modifications to the initial code base are copyright of their respective
* authors, or their employers as appropriate.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*****************************************************************************
*/
package es.bsc.inb.elixir.openebench.rest.auth;
import javax.ws.rs.core.SecurityContext;
import org.bson.Document;
/**
* @author Dmitry Repchevsky
*/
public abstract class AbstractAuthorization {
protected final SecurityContext sc;
public AbstractAuthorization(final SecurityContext sc) {
this.sc = sc;
}
public abstract boolean isReadable(Document doc);
public abstract boolean isWritable(Document doc);
}
/**
* *****************************************************************************
* Copyright (C) 2020 ELIXIR ES, Spanish National Bioinformatics Institute (INB)
* and Barcelona Supercomputing Center (BSC)
*
* Modifications to the initial code base are copyright of their respective
* authors, or their employers as appropriate.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*****************************************************************************
*/
package es.bsc.inb.elixir.openebench.rest.auth;
import com.mongodb.client.MongoDatabase;
import es.bsc.inb.elixir.openebench.rest.dao.OEBCollection;
import javax.ws.rs.core.SecurityContext;
/**
* Utility class to get MongoDB authentication filters.
*
* @author Dmitry Repchevsky
*/
public class Authorization {
private final MongoDatabase production;
private final MongoDatabase provisional;
public Authorization(
final MongoDatabase production,
final MongoDatabase provisional) {
this.production = production;
this.provisional = provisional;
}
/**
* Get the authorization object for a given mongodb collection.
*
* @param sc security context to be used for the authorization
* @param collection a collection for which the authorization rules should apply
*
* @return the authorization object to validate documents over collection specific rules.
* may return null if no rules found (basically an error).
*/
public AbstractAuthorization getAuthorization(final SecurityContext sc,
final String collection) {
switch(collection) {
case OEBCollection.DATASET: return getDatasetAuthorization(sc);
case OEBCollection.CHALLENGE: return getChallengeAuthorization(sc);
case OEBCollection.CONTACT: return getContactAuthorization(sc);
case OEBCollection.COMMUNITY: return getCommunityAuthorization(sc);
case OEBCollection.ID_SOLV: return getIdSolvAuthorization(sc);
case OEBCollection.PRIVILEGE: return getPrivilegeAuthorization(sc);
case OEBCollection.REFERENCE: return getReferenceAuthorization(sc);
case OEBCollection.TOOL: return getToolAuthorization(sc);
case OEBCollection.TEST_ACTION: return getTestActionAuthorization(sc);
case OEBCollection.METRICS: return getMetricsAuthorization(sc);
case OEBCollection.BENCHMARKING_EVENT: return getBenchmarkingEventAuthorization(sc);
}
return null;
}
public ContactAuthorization getContactAuthorization(final SecurityContext sc) {
return new ContactAuthorization(sc);
}
public CommunityAuthorization getCommunityAuthorization(final SecurityContext sc) {
return new CommunityAuthorization(sc);
}
public PrivilegeAuthorization getPrivilegeAuthorization(final SecurityContext sc) {
return new PrivilegeAuthorization(sc);
}
public ReferenceAuthorization getReferenceAuthorization(final SecurityContext sc) {
return new ReferenceAuthorization(sc);
}
public ChallengeAuthorization getChallengeAuthorization(final SecurityContext sc) {
return new ChallengeAuthorization(sc, production, provisional);
}
public DatasetAuthorization getDatasetAuthorization(final SecurityContext sc) {
return new DatasetAuthorization(sc, production, provisional);
}
public IdSolvAuthorization getIdSolvAuthorization(final SecurityContext sc) {
return new IdSolvAuthorization(sc);
}
public ToolAuthorization getToolAuthorization(final SecurityContext sc) {
return new ToolAuthorization(sc);
}
public TestActionAuthorization getTestActionAuthorization(final SecurityContext sc) {
return new TestActionAuthorization(sc);
}
public MetricsAuthorization getMetricsAuthorization(final SecurityContext sc) {
return new MetricsAuthorization(sc);