First implentation. Add personal-info and registered services methods.

This commit is contained in:
Konstantinos Triantafyllou 2023-06-26 15:41:22 +03:00
parent 733f972916
commit b0a4b3306f
30 changed files with 1397 additions and 1 deletions

16
pom.xml
View File

@ -18,7 +18,7 @@
<dependency>
<groupId>eu.dnetlib</groupId>
<artifactId>uoa-login-core</artifactId>
<version>2.0.0</version>
<version>2.0.1-SNAPSHOT</version>
</dependency>
<!--swagger-->
<dependency>
@ -32,6 +32,20 @@
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger-version}</version>
</dependency>
<!-- PostgresSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1211.jre7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<resources>

26
scripts/init.sql Normal file
View File

@ -0,0 +1,26 @@
CREATE TABLE IF NOT EXISTS registered_service
(
id integer NOT NULL,
client_id character varying(255) NOT NULL,
owner character varying(255) NOT NULL,
name character varying(255) NOT NULL,
creation_date timestamp without time zone NOT NULL,
registration_access_token text NOT NULL,
key_type character varying(255)
);
CREATE SEQUENCE IF NOT EXISTS registered_service_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE registered_service_id_seq OWNED BY registered_service.id;
ALTER TABLE ONLY registered_service ALTER COLUMN id SET DEFAULT nextval('registered_service_id_seq'::regclass);
ALTER TABLE ONLY registered_service DROP CONSTRAINT IF EXISTS registered_service_pkey;
ALTER TABLE ONLY registered_service ADD CONSTRAINT registered_service_pkey PRIMARY KEY (id);

View File

@ -0,0 +1,15 @@
ALTER TABLE ONLY registered_service ADD COLUMN url character varying(255);
CREATE TABLE IF NOT EXISTS personal_info
(
id character varying(255) NOT NULL,
affiliation character varying(255),
email character varying(255),
name character varying(255),
"position" character varying(255),
surname character varying(255)
);
ALTER TABLE ONLY personal_info DROP CONSTRAINT IF EXISTS personal_info_pkey;
ALTER TABLE ONLY personal_info ADD CONSTRAINT personal_info_pkey PRIMARY KEY (id);

View File

@ -1,8 +1,10 @@
package eu.dnetlib.clientmanagement;
import eu.dnetlib.authentication.configuration.AuthenticationConfiguration;
import eu.dnetlib.clientmanagement.configuration.Properties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
@ -10,9 +12,11 @@ import org.springframework.context.annotation.PropertySources;
@SpringBootApplication(scanBasePackages = {"eu.dnetlib.clientmanagement"})
@PropertySources({
@PropertySource("classpath:authentication.properties"),
@PropertySource("classpath:client-management.properties"),
@PropertySource(value = "classpath:dnet-override.properties", ignoreResourceNotFound = true)
})
@Import({AuthenticationConfiguration.class})
@EnableConfigurationProperties({Properties.class})
public class ClientManagementApplication {
public static void main(String[] args) {

View File

@ -0,0 +1,29 @@
package eu.dnetlib.clientmanagement.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
Datasource datasource;
@Autowired
public DataSourceConfig(Properties properties) {
this.datasource = properties.getDatasource();
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(this.datasource.getDriver());
dataSource.setUrl(this.datasource.getUrl());
dataSource.setUsername(this.datasource.getUsername());
dataSource.setPassword(this.datasource.getPassword());
return dataSource;
}
}

View File

@ -0,0 +1,43 @@
package eu.dnetlib.clientmanagement.configuration;
public class Datasource {
private String driver;
private String url;
private String username;
private String password;
public Datasource() {
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,28 @@
package eu.dnetlib.clientmanagement.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("client-management")
public class Properties {
Datasource datasource;
String issuer;
public Properties() {
}
public Datasource getDatasource() {
return datasource;
}
public void setDatasource(Datasource datasource) {
this.datasource = datasource;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
}

View File

@ -0,0 +1,79 @@
package eu.dnetlib.clientmanagement.configuration;
import eu.dnetlib.authentication.configuration.APIProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger configuration class
*/
@Configuration
@Profile({"swagger"})
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurerAdapter {
private final APIProperties apiProperties;
@Autowired
public SwaggerConfig(APIProperties apiProperties) {
this.apiProperties = apiProperties;
}
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("eu.dnetlib.clientmanagement.controllers"))
.paths(PathSelectors.any())
.build();
}
@Bean
public Docket createRestApiLoginCore() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("Login Core")
.select()
.apis(RequestHandlerSelectors.basePackage("eu.dnetlib.authentication.controllers"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(this.apiProperties.getTitle())
.description(this.apiProperties.getDescription())
.version(this.apiProperties.getVersion())
.build();
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/v2/api-docs", "/v2/api-docs");
registry.addRedirectViewController("/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
registry.addRedirectViewController("/swagger-resources/configuration/security", "/swagger-resources/configuration/security");
registry.addRedirectViewController("/swagger-resources", "/swagger-resources");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/swagger-ui.html**").addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

View File

@ -0,0 +1,64 @@
package eu.dnetlib.clientmanagement.controllers;
import eu.dnetlib.clientmanagement.dto.RegisteredAT;
import eu.dnetlib.clientmanagement.dto.API;
import eu.dnetlib.clientmanagement.dto.ServiceForm;
import eu.dnetlib.clientmanagement.services.APIService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController()
@RequestMapping("/apis")
@CrossOrigin(origins = "*")
public class APIController {
@Autowired
private APIService service;
@Autowired
public APIController(APIService service) {
this.service = service;
}
@PreAuthorize("hasAnyAuthority('PORTAL_ADMINISTRATOR')")
@RequestMapping(value = "/all", method = RequestMethod.GET)
public ResponseEntity<List<API>> getAll() {
return ResponseEntity.ok(this.service.getAll());
}
@PreAuthorize("hasAnyAuthority('PORTAL_ADMINISTRATOR')")
@RequestMapping(value = "/migrate/accessToken", method = RequestMethod.POST)
public ResponseEntity<List<API>> migrateAccessToken(@RequestBody List<RegisteredAT> registeredATS) {
return ResponseEntity.ok(this.service.migrateAccessToken(registeredATS));
}
@PreAuthorize("hasAnyAuthority('REGISTERED_USER') && @AuthorizationService.hasPersonalInfo()")
@RequestMapping(value = "/my-services", method = RequestMethod.GET)
public ResponseEntity<List<API>> getMyServices() {
return ResponseEntity.ok(this.service.getMyServices());
}
@PreAuthorize("hasAnyAuthority('REGISTERED_USER') && @AuthorizationService.hasPersonalInfo()")
@RequestMapping(value = "/save/new", method = RequestMethod.POST)
public ResponseEntity<API> create(@RequestBody ServiceForm form) {
return ResponseEntity.ok(this.service.save(form));
}
@PreAuthorize("hasAnyAuthority('REGISTERED_USER') && @AuthorizationService.hasPersonalInfo() && @AuthorizationService.isMyService(#id)")
@RequestMapping(value = "/save/{id}", method = RequestMethod.POST)
public ResponseEntity<API> update(@PathVariable Long id, @RequestBody ServiceForm form) {
return ResponseEntity.ok(this.service.save(form, id));
}
@PreAuthorize("hasAnyAuthority('REGISTERED_USER') && @AuthorizationService.hasPersonalInfo() && @AuthorizationService.isMyService(#id)")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> delete(@PathVariable Long id) {
this.service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,41 @@
package eu.dnetlib.clientmanagement.controllers;
import eu.dnetlib.clientmanagement.entities.PersonalInfo;
import eu.dnetlib.clientmanagement.services.PersonalInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController()
@RequestMapping("/personal/")
@CrossOrigin(origins = "*")
public class PersonalInfoController {
PersonalInfoService service;
@Autowired
public PersonalInfoController(PersonalInfoService service) {
this.service = service;
}
@PreAuthorize("hasAnyAuthority('PORTAL_ADMINISTRATOR')")
@RequestMapping(value = "/all", method = RequestMethod.GET)
public ResponseEntity<List<PersonalInfo>> getAll() {
return ResponseEntity.ok(this.service.getAll());
}
@PreAuthorize("hasAnyAuthority('REGISTERED_USER')")
@RequestMapping(value = "/my-info", method = RequestMethod.GET)
public ResponseEntity<PersonalInfo> getMyPersonalInfo() {
return ResponseEntity.ok(this.service.getPersonalInfo());
}
@PreAuthorize("hasAnyAuthority('REGISTERED_USER')")
@RequestMapping(value = "/save", method = RequestMethod.POST)
public ResponseEntity<PersonalInfo> savePersonalInfo(@RequestBody PersonalInfo personalInfo) {
return ResponseEntity.ok(this.service.savePersonalInfo(personalInfo));
}
}

View File

@ -0,0 +1,27 @@
package eu.dnetlib.clientmanagement.dto;
import eu.dnetlib.clientmanagement.entities.RegisteredService;
public class API {
private RegisteredService service;
private ServiceResponse details;
public API() {
}
public RegisteredService getService() {
return service;
}
public void setService(RegisteredService service) {
this.service = service;
}
public ServiceResponse getDetails() {
return details;
}
public void setDetails(ServiceResponse details) {
this.details = details;
}
}

View File

@ -0,0 +1,19 @@
package eu.dnetlib.clientmanagement.dto;
import java.io.Serializable;
public class Jwks implements Serializable {
Key[] keys;
public Key[] getKeys() {
return keys;
}
public void setKeys(Key[] keys) {
this.keys = keys;
}
public boolean isValid() {
return this.keys != null && this.keys.length > 0;
}
}

View File

@ -0,0 +1,48 @@
package eu.dnetlib.clientmanagement.dto;
import com.google.gson.*;
import java.lang.reflect.Type;
public class JwksDeserializer implements JsonDeserializer<Jwks> {
@Override
public Jwks deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject == null) throw new JsonParseException("Jwks not valid.");
JsonArray jsonArray = jsonObject.getAsJsonArray("keys");
if (jsonArray == null ) throw new JsonParseException("Jwks not valid.");
Jwks jwks = new Jwks();
Key[] keys = new Key[jsonArray.size()];
Key key = null;
for (int i = 0; i < jsonArray.size(); i++) {
key = new Key();
JsonElement je = jsonArray.get(i);
if (je == null) throw new JsonParseException("Jwks not valid.");
if (je.getAsJsonObject().get("kty")==null) throw new JsonParseException("Jwks not valid.");
key.setKty(je.getAsJsonObject().get("kty").getAsString());
if (je.getAsJsonObject().get("e")==null) throw new JsonParseException("Jwks not valid.");
key.setE(je.getAsJsonObject().get("e").getAsString());
if (je.getAsJsonObject().get("kid")==null) throw new JsonParseException("Jwks not valid.");
key.setKid(je.getAsJsonObject().get("kid").getAsString());
if (je.getAsJsonObject().get("alg")==null) throw new JsonParseException("Jwks not valid.");
key.setAlg(je.getAsJsonObject().get("alg").getAsString());
if (je.getAsJsonObject().get("n")==null) throw new JsonParseException("Jwks not valid.");
key.setN(je.getAsJsonObject().get("n").getAsString());
keys[i] = key;
}
jwks.setKeys(keys);
return jwks;
}
}

View File

@ -0,0 +1,51 @@
package eu.dnetlib.clientmanagement.dto;
import java.io.Serializable;
public class Key implements Serializable {
String kty;
String e;
String kid;
String alg;
String n;
public String getKty() {
return kty;
}
public void setKty(String kty) {
this.kty = kty;
}
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
public String getKid() {
return kid;
}
public void setKid(String kid) {
this.kid = kid;
}
public String getAlg() {
return alg;
}
public void setAlg(String alg) {
this.alg = alg;
}
public String getN() {
return n;
}
public void setN(String n) {
this.n = n;
}
}

View File

@ -0,0 +1,29 @@
package eu.dnetlib.clientmanagement.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
public class RegisteredAT {
@JsonProperty("client_id")
private String clientId;
@JsonProperty("registration_access_token")
private String registrationAccessToken;
public RegisteredAT() {
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getRegistrationAccessToken() {
return registrationAccessToken;
}
public void setRegistrationAccessToken(String registrationAccessToken) {
this.registrationAccessToken = registrationAccessToken;
}
}

View File

@ -0,0 +1,79 @@
package eu.dnetlib.clientmanagement.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.URL;
public class ServiceForm {
@JsonProperty(required = true)
private String name;
@URL(message = "Service URL must be a valid URL")
private String url;
@URL(message = "Logo URL must be a valid URL")
private String logoURL;
private String keyType;
@URL(message = "uri must be a valid URL", protocol = "https")
private String uri;
private String value;
@JsonIgnore
private Jwks jwks;
public ServiceForm() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getLogoURL() {
return logoURL;
}
public void setLogoURL(String logoURL) {
this.logoURL = logoURL;
}
public String getKeyType() {
return keyType;
}
public void setKeyType(String keyType) {
this.keyType = keyType;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Jwks getJwks() {
return jwks;
}
public void setJwks(Jwks jwks) {
this.jwks = jwks;
}
}

View File

@ -0,0 +1,144 @@
package eu.dnetlib.clientmanagement.dto;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class ServiceRequest {
String client_name;
String client_id;
String logo_uri;
String policy_uri;
String[] contacts;
String[] redirect_uris = new String[]{};
String[] grant_types = new String[] {"client_credentials"};
String token_endpoint_auth_method = "private_key_jwt";
String token_endpoint_auth_signing_alg = "RS256";
String jwks_uri;
Jwks jwks;
public String getClientName() {
return client_name;
}
public void setClientName(String clientName) {
this.client_name = clientName;
}
public String getClientId() {
return client_id;
}
public void setClientId(String clientId) {
this.client_id = clientId;
}
public String[] getRedirectUris() {
return redirect_uris;
}
public void setRedirectUris(String[] redirectUris) {
this.redirect_uris = redirectUris;
}
public String getLogoUri() {
return logo_uri;
}
public void setLogoUri(String logoUri) {
this.logo_uri = logoUri;
}
public String getPolicyUri() {
return policy_uri;
}
public void setPolicyUri(String policyUri) {
this.policy_uri = policyUri;
}
public String[] getContacts() {
return contacts;
}
public void setContacts(String[] contacts) {
this.contacts = contacts;
}
public String[] getGrantTypes() {
return grant_types;
}
public void setGrantTypes(String[] grantTypes) {
this.grant_types = grantTypes;
}
public String getToken_endpoint_auth_method() {
return token_endpoint_auth_method;
}
public void setToken_endpoint_auth_method(String token_endpoint_auth_method) {
this.token_endpoint_auth_method = token_endpoint_auth_method;
}
public String getTokenEndpointAuthSigningAlg() {
return token_endpoint_auth_signing_alg;
}
public void setTokenEndpointAuthSigningAlg(String tokenEndpointAuthSigningAlg) {
this.token_endpoint_auth_signing_alg = tokenEndpointAuthSigningAlg;
}
public String getJwksUri() {
return jwks_uri;
}
public void setJwksUri(String jwksUri) {
this.jwks_uri = jwksUri;
}
public Jwks getJwks() {
return jwks;
}
public void setJwks(Jwks jwks) {
this.jwks = jwks;
}
public String toJson() {
GsonBuilder builder = new GsonBuilder();
builder.serializeNulls();
Gson gson = builder.create();
return gson.toJson(this);
}
public static ServiceRequest createServiceRequest(String client_id, String name, String logoURL, String email) {
ServiceRequest request = new ServiceRequest();
request.setClientId(client_id);
request.setClientName(name);
request.setContacts(new String[]{email});
request.setToken_endpoint_auth_method("client_secret_basic");
request.setTokenEndpointAuthSigningAlg(null);
request.setLogoUri(logoURL);
return request;
}
public static ServiceRequest createServiceRequest(String client_id, String name, String logoURL, String email, String uri) {
ServiceRequest request = new ServiceRequest();
request.setClientId(client_id);
request.setClientName(name);
request.setContacts(new String[]{email});
request.setJwksUri(uri);
request.setLogoUri(logoURL);
return request;
}
public static ServiceRequest createServiceRequest(String client_id, String name, String logoURL, String email, Jwks jwks) {
ServiceRequest request = new ServiceRequest();
request.setClientId(client_id);
request.setClientName(name);
request.setContacts(new String[]{email});
request.setJwks(jwks);
request.setLogoUri(logoURL);
return request;
}
}

View File

@ -0,0 +1,107 @@
package eu.dnetlib.clientmanagement.dto;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.Serializable;
public class ServiceResponse implements Serializable {
String client_id;
Long client_id_issued_at;
String client_secret;
Long client_secret_expires_at;
String registration_access_token;
String registration_client_uri;
String[] redirect_uris;
String client_name;
String logo_uri;
String policy_uri;
String[] contacts;
String[] grant_types;
String token_endpoint_auth_method;
String token_endpoint_auth_signing_alg;
String scope;
String jwks_uri;
Jwks jwks;
public String getClientId() {
return client_id;
}
public Long getClientIdIssuedAt() {
return client_id_issued_at;
}
public String getClientSecret() {
return client_secret;
}
public Long getClientSecretExpiresAt() {
return client_secret_expires_at;
}
public String getRegistrationAccessToken() {
return registration_access_token;
}
public String getRegistrationClientUri() {
return registration_client_uri;
}
public String[] getRedirectUris() {
return redirect_uris;
}
public String getClientName() {
return client_name;
}
public String getLogoUri() {
return logo_uri;
}
public String getPolicyUri() {
return policy_uri;
}
public String[] getContacts() {
return contacts;
}
public String[] getGrantTypes() {
return grant_types;
}
public String getTokenEndpointAuthMethod() {
return token_endpoint_auth_method;
}
public String getTokenEndpointAuthSigningAlg() {
return token_endpoint_auth_signing_alg;
}
public String getScope() {
return scope;
}
public String getJwksUri() {
return jwks_uri;
}
public Jwks getJwks() {
return jwks;
}
public String toJson() {
GsonBuilder builder = new GsonBuilder();
builder.serializeNulls();
Gson gson = builder.create();
return gson.toJson(this);
}
public static ServiceResponse fromString(String response) {
return new Gson().fromJson(response, ServiceResponse.class);
}
}

View File

@ -0,0 +1,67 @@
package eu.dnetlib.clientmanagement.entities;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class PersonalInfo {
@Id
String id;
String name;
String surname;
String email;
String affiliation;
String position;
public PersonalInfo() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAffiliation() {
return affiliation;
}
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}

View File

@ -0,0 +1,95 @@
package eu.dnetlib.clientmanagement.entities;
import javax.persistence.*;
import java.util.Date;
@Entity
public class RegisteredService {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String clientId;
String owner;
String name;
@Temporal(TemporalType.TIMESTAMP)
Date creationDate;
String registrationAccessToken;
String keyType;
String url;
public RegisteredService() {
this.creationDate = new Date();
}
public RegisteredService(String owner, String name, String url, String keyType) {
this.owner = owner;
this.name = name;
this.keyType = keyType;
this.creationDate = new Date();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public String getRegistrationAccessToken() {
return registrationAccessToken;
}
public void setRegistrationAccessToken(String registrationAccessToken) {
this.registrationAccessToken = registrationAccessToken;
}
public String getKeyType() {
return keyType;
}
public void setKeyType(String keyType) {
this.keyType = keyType;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@ -0,0 +1,11 @@
package eu.dnetlib.clientmanagement.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public class EntityMissingException extends RuntimeException {
public EntityMissingException(String message){
super(message);
}
}

View File

@ -0,0 +1,11 @@
package eu.dnetlib.clientmanagement.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(String message){
super(message);
}
}

View File

@ -0,0 +1,11 @@
package eu.dnetlib.clientmanagement.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.FORBIDDEN)
public class ForbiddenException extends RuntimeException {
public ForbiddenException(String message){
super(message);
}
}

View File

@ -0,0 +1,20 @@
package eu.dnetlib.clientmanagement.repositories;
import eu.dnetlib.clientmanagement.entities.PersonalInfo;
import eu.dnetlib.clientmanagement.entities.RegisteredService;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface PersonalInfoDAO extends CrudRepository<PersonalInfo, Long> {
List<PersonalInfo> findAll();
PersonalInfo saveAndFlush(PersonalInfo personalInfo);
Optional<PersonalInfo> findById(String id);
}

View File

@ -0,0 +1,20 @@
package eu.dnetlib.clientmanagement.repositories;
import eu.dnetlib.clientmanagement.entities.RegisteredService;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface RegisteredServiceDAO extends CrudRepository<RegisteredService, Long> {
List<RegisteredService> findAll();
Optional<RegisteredService> findById(Long id);
Optional<RegisteredService> findByClientId(String clientId);
List<RegisteredService> findAllByOwnerOrderByCreationDateAsc(String owner);
}

View File

@ -0,0 +1,187 @@
package eu.dnetlib.clientmanagement.services;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.dnetlib.clientmanagement.configuration.Properties;
import eu.dnetlib.clientmanagement.dto.*;
import eu.dnetlib.clientmanagement.entities.RegisteredService;
import eu.dnetlib.clientmanagement.exceptions.EntityMissingException;
import eu.dnetlib.clientmanagement.exceptions.EntityNotFoundException;
import eu.dnetlib.clientmanagement.exceptions.ForbiddenException;
import eu.dnetlib.clientmanagement.repositories.RegisteredServiceDAO;
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service()
public class APIService {
private RegisteredServiceDAO dao;
private RestTemplate template;
private String issuer;
@Autowired
public APIService(RegisteredServiceDAO dao, RestTemplate template, Properties properties) {
this.dao = dao;
this.template = template;
this.issuer = properties.getIssuer();
}
public List<API> getAll() {
return this.dao.findAll().stream().map(service -> {
API details = new API();
details.setService(service);
details.setDetails(this.readService(service.getClientId(), service.getRegistrationAccessToken()));
return details;
}).collect(Collectors.toList());
}
public List<API> getMyServices() {
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
return this.dao.findAllByOwnerOrderByCreationDateAsc(authentication.getSub()).stream().map(service -> {
API details = new API();
details.setService(service);
details.setDetails(this.readService(service.getClientId(), service.getRegistrationAccessToken()));
return details;
}).collect(Collectors.toList());
}
public List<API> migrateAccessToken(List<RegisteredAT> registeredAT) {
List<API> apis = new ArrayList<>();
registeredAT.forEach( element -> {
RegisteredService service = this.dao.findByClientId(element.getClientId()).orElse(null);
if(service != null) {
service.setRegistrationAccessToken(element.getRegistrationAccessToken());
API api = new API();
api.setService(this.dao.save(service));
api.setDetails(this.readService(service.getClientId(), service.getRegistrationAccessToken()));
apis.add(api);
}
});
return apis;
}
public API save(ServiceForm form, Long id) {
ServiceRequest request;
API api = new API();
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
if(id != null) {
RegisteredService service = this.dao.findById(id).orElse(null);
if(service == null) {
throw new EntityNotFoundException("Service with id " + id + " does not exist.");
}
api.setService(service);
api.setDetails(readService(service.getClientId(), service.getRegistrationAccessToken()));
} else {
api.setService(new RegisteredService(authentication.getSub(), form.getName(), form.getUrl(), form.getKeyType()));
}
String keyType = form.getKeyType();
if(keyType != null && keyType.equals("uri")) {
if(form.getUri() == null) {
throw new EntityMissingException("Uri is required in this type of key");
}
request = ServiceRequest.createServiceRequest(api.getService().getClientId(), form.getName(), form.getLogoURL(), authentication.getUserInfo().getEmail(), form.getUri());
} else if(keyType != null && keyType.equals("value")) {
Gson gson = new GsonBuilder().registerTypeAdapter(Jwks.class, new JwksDeserializer()).create();
String jwksSet = String.format("{\"keys\":[%s]}", form.getValue());
form.setJwks(gson.fromJson(jwksSet, Jwks.class));
if(!form.getJwks().isValid()) {
throw new EntityMissingException("Jwks value is not valid");
}
request = ServiceRequest.createServiceRequest(api.getService().getClientId(), form.getName(), form.getLogoURL(), authentication.getUserInfo().getEmail(), form.getJwks());
} else {
request = ServiceRequest.createServiceRequest(api.getService().getClientId(), form.getName(), form.getLogoURL(), authentication.getUserInfo().getEmail());
}
ServiceResponse response;
if(api.getService().getClientId() != null) {
response = this.updateService(api.getService().getClientId(), request, api.getService().getRegistrationAccessToken());
} else {
if(this.dao.findAllByOwnerOrderByCreationDateAsc(authentication.getSub()).size() < 5) {
response = this.createService(request);
} else {
throw new ForbiddenException("You are not allowed to own more than 5 services.");
}
}
api.getService().setKeyType(form.getKeyType());
api.getService().setUrl(form.getUrl());
api.getService().setName(request.getClientName());
api.getService().setClientId(response.getClientId());
api.getService().setRegistrationAccessToken(response.getRegistrationAccessToken());
api.setService(this.dao.save(api.getService()));
api.setDetails(response);
return api;
}
public API save(ServiceForm form) {
return this.save(form, null);
}
public void delete(Long id) {
RegisteredService service = this.dao.findById(id).orElse(null);
if(service == null) {
throw new EntityNotFoundException("Service with id " + id + " does not exist.");
} else {
this.deleteService(service.getClientId(), service.getRegistrationAccessToken());
this.dao.delete(id);
}
}
private ServiceResponse createService(ServiceRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<>(request.toJson(), headers);
try {
ResponseEntity<String> response = template.exchange(this.issuer, HttpMethod.POST, requestEntity, String.class);
return ServiceResponse.fromString(response.getBody());
} catch (Exception e) {
e.printStackTrace();
throw new EntityMissingException("Register of this service couldn't be completed. Check again your parameters");
}
}
private ServiceResponse updateService(String clientId, ServiceRequest request, String token) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
HttpEntity<String> requestEntity = new HttpEntity<>(request.toJson(), headers);
try {
ResponseEntity<String> response = template.exchange(this.issuer + "/" + clientId, HttpMethod.PUT, requestEntity, String.class);
return ServiceResponse.fromString(response.getBody());
} catch (Exception e) {
e.printStackTrace();
throw new EntityMissingException("Update of service with client id: " + clientId + " couldn't be completed. Check again your parameters");
}
}
private ServiceResponse readService(String clientId, String token) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
try {
ResponseEntity<String> response = template.exchange(this.issuer + "/" + clientId, HttpMethod.GET, requestEntity, String.class);
return ServiceResponse.fromString(response.getBody());
} catch (Exception e) {
return null;
}
}
private void deleteService(String clientId, String token) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
HttpEntity<ServiceRequest> requestEntity = new HttpEntity<>(headers);
try {
template.exchange(this.issuer + "/" + clientId, HttpMethod.DELETE, requestEntity, String.class);
} catch (Exception e) {
throw new EntityMissingException("Delete of service with client id: " + clientId + " couldn't be completed. Check again your parameters");
}
}
}

View File

@ -0,0 +1,33 @@
package eu.dnetlib.clientmanagement.services;
import eu.dnetlib.clientmanagement.entities.RegisteredService;
import eu.dnetlib.clientmanagement.repositories.PersonalInfoDAO;
import eu.dnetlib.clientmanagement.repositories.RegisteredServiceDAO;
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Component("AuthorizationService")
public class AuthorizationService {
private final RegisteredServiceDAO registeredServiceDAO;
private final PersonalInfoDAO personalInfoDAO;
@Autowired
public AuthorizationService(RegisteredServiceDAO registeredServiceDAO, PersonalInfoDAO personalInfoDAO) {
this.registeredServiceDAO = registeredServiceDAO;
this.personalInfoDAO = personalInfoDAO;
}
public boolean isMyService(Long id) {
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
RegisteredService service = this.registeredServiceDAO.findById(id).orElse(null);
return service != null && service.getOwner().equals(authentication.getSub());
}
public boolean hasPersonalInfo() {
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
return this.personalInfoDAO.findById(authentication.getSub()).orElseThrow(null) != null;
}
}

View File

@ -0,0 +1,43 @@
package eu.dnetlib.clientmanagement.services;
import eu.dnetlib.clientmanagement.entities.PersonalInfo;
import eu.dnetlib.clientmanagement.entities.RegisteredService;
import eu.dnetlib.clientmanagement.exceptions.EntityNotFoundException;
import eu.dnetlib.clientmanagement.repositories.PersonalInfoDAO;
import eu.dnetlib.clientmanagement.repositories.RegisteredServiceDAO;
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service()
public class PersonalInfoService {
private PersonalInfoDAO dao;
@Autowired
public PersonalInfoService(PersonalInfoDAO dao) {
this.dao = dao;
}
public List<PersonalInfo> getAll() {
return this.dao.findAll();
}
public PersonalInfo save(PersonalInfo personalInfo) {
return this.dao.save(personalInfo);
}
public PersonalInfo getPersonalInfo() {
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
return this.dao.findById(authentication.getSub()).orElseThrow( () -> new EntityNotFoundException("Personal Info of user with id " + authentication.getSub() + " has not been found"));
}
public PersonalInfo savePersonalInfo(PersonalInfo personalInfo) {
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
personalInfo.setId(authentication.getSub());
return this.dao.saveAndFlush(personalInfo);
}
}

View File

@ -0,0 +1,12 @@
client-management.datasource.driver=org.postgresql.Driver
client-management.datasource.url=jdbc:postgresql://dl170.madgik.di.uoa.gr:5432/usersdb
client-management.datasource.username=username
client-management.datasource.password=pass
client-management.issuer=issuer
spring.jpa.hibernate.ddl-auto=none
## API Documentation Properties
api.title = Client Management
api.description = Client Management includes authentication methods and provides functionalities to manage OpenAIRE Dynamic AAI Clients.
api.version = ${project.version}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="LOG_PATTERN">
%d %p %t [%c] - %m%n
</Property>
</Properties>
<Appenders>
<!-- Rolling File Appender -->
<RollingFile name="R" fileName="/var/log/dnet/client-management/client-management.log"
filePattern="/var/log/dnet/client-management/client-management-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>${LOG_PATTERN}</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10MB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<RollingFile name="S" fileName="/var/log/dnet/client-management/client-management-spring.log"
filePattern="/var/log/dnet/client-management/client-management-spring-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<Pattern>${LOG_PATTERN}</Pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10MB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="eu.dnetlib" level="debug" additivity="false">
<AppenderRef ref="R"/>
</Logger>
<Root level="info">
<AppenderRef ref="S"/>
</Root>
</Loggers>
</Configuration>