Commit decc3ef4 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '174-oauth-client-credentials-grant' into 'master'

Resolve "OAuth Client Credentials Grant"

Closes #174

See merge request !84
parents 2f4814cb 0097c5d8
......@@ -54,11 +54,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.target>1.8</jdk.target>
<jdk.source>1.8</jdk.source>
<show.deprecations>false</show.deprecations>
<show.deprecations>true</show.deprecations>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
<junit.version>4.12</junit.version>
<application.blocks.version>1.2-SNAPSHOT</application.blocks.version>
<application.blocks.version>1.3-SNAPSHOT</application.blocks.version>
<commons.beanutils.version>1.9.2</commons.beanutils.version>
<commons.collections.version>3.2.1</commons.collections.version>
<commons.fileupload.version>1.3.1</commons.fileupload.version>
......@@ -75,7 +75,7 @@
<spring-data-jpa.version>1.10.4.RELEASE</spring-data-jpa.version>
<spring.data.release-train>Hopper-SR1</spring.data.release-train>
<spring.security.version>4.1.3.RELEASE</spring.security.version>
<spring.security.oauth2.version>1.0.5.RELEASE</spring.security.oauth2.version>
<spring.security.oauth2.version>2.0.14.RELEASE</spring.security.oauth2.version>
<org.springframework.social-version>1.1.4.RELEASE</org.springframework.social-version>
<org.springframework.social-google-version>1.0.0.RELEASE</org.springframework.social-google-version>
<querydsl.version>4.1.4</querydsl.version>
......@@ -96,7 +96,8 @@
<jackson.version>2.6.4</jackson.version>
<jaxb-api.version>2.2.12</jaxb-api.version>
<liquibase.version>3.5.3</liquibase.version>
<swagger.version>2.7.0</swagger.version>
<!--Container -->
<jetty.version>9.3.6.v20151106</jetty.version>
......@@ -296,6 +297,17 @@
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- App blocks -->
<dependency>
<groupId>org.genesys-pgr</groupId>
......@@ -591,9 +603,10 @@
<configuration>
<source>${jdk.source}</source>
<target>${jdk.target}</target>
<optimize>true</optimize>
<optimize>false</optimize>
<showDeprecation>${show.deprecations}</showDeprecation>
<showWarnings>true</showWarnings>
<failOnError>true</failOnError>
</configuration>
</plugin>
<plugin>
......
......@@ -3,37 +3,41 @@
== Managing Passport Data
Passport data is based on FAO Multi-Crop Passport Descriptors <<mcpd2>> format.
Passport data is based on FAO Multi-Crop Passport Descriptors <<mcpd2>> format.
Accession records are *upserted*, meaning that when the matching accession record
Accession records are *upserted*, meaning that when the matching accession record
. exists, it will be updated
. does not exist, a new record will be created
Accession data in the database will be updated with whatever data is provided in the
Accession data in the database will be updated with whatever data is provided in the
request JSON.
=== Accession identity
Prior to full adoption of Permanent Unique Identifiers for Germplasm, accessions could be
identified by the holding institute code (INSTCODE) and the accession number (ACCENUMB).
Genebanks maintaining two or more collections of crops would sometimes use the same
accession number, unique within one collection.
Genebanks maintaining two or more collections of crops would sometimes use the same
accession number, unique within one collection.
Genesys uses the *instCode*, *acceNumb* and *genus* triplet to uniquely identify an
accession in an institute:
Genesys uses the *instCode*, *acceNumb* and *genus* triplet to uniquely identify an
accession in an institute. For accessions with a <<doi, DOI>> assigned in <<glis, GLIS>> it will
use the DOI to uniquely identify the record:
[source,json,linenums]
----
{
"instCode": "NGA039", <1>
"acceNumb": "TMp-123", <2>
"genus": "Musa" <3>
"doi": "10.1054/12A1~", <2>
"acceNumb": "TMp-123", <3>
"genus": "Musa", <4>
...
}
----
<1> Holding institute code (INSTCODE)
<2> Accession number (ACCENUMB)
<3> Genus (GENUS)
<2> DOI of the accession as registered in GLIS
<3> Accession number (ACCENUMB)
<4> Genus (GENUS)
=== JSON data model
......@@ -54,6 +58,7 @@ All other fields are optional.
----
{
"instCode": "XYZ111",
"doi": "10.1231/14S41Q",
"acceNumb": "M12345",
"cropName": "banana",
"genus": "Musa",
......@@ -177,8 +182,8 @@ as `null`. Not providing a field means the field in the database should not be m
=== Insert or update accessions
REST endpoint URL `/api/v0/acn/{instCode}/upsert` allows for inserting new accessions
or updating existing records in Genesys. It accepts a JSON array of Accession JSON objects.
REST endpoint URL `/api/v0/acn/{instCode}/upsert` allows for inserting new accessions
or updating existing records in Genesys. It accepts a JSON array of Accession JSON objects.
The array provides for sending batches of 50 or 100 accessions in one call, reducing
the HTTP overhead and improving performance.
......@@ -223,6 +228,7 @@ POST /api/v0/acn/SYR002/delete
[bibliography]
- [[[mcpd2]]] Alercia, A; Diulgheroff, S; Mackay, M.
- [[[mcpd2]]] Alercia, A; Diulgheroff, S; Mackay, M.
http://www.bioversityinternational.org/e-library/publications/detail/faobioversity-multi-crop-passport-descriptors-v2-mcpd-v2/[FAO/Bioversity Multi-Crop Passport Descriptors V.2]. 2012.
- [[[glis]]] ITPGRFA Secretariat
http://www.fao.org/plant-treaty/areas-of-work/global-information-system/en/[Global Information System].
......@@ -6,12 +6,12 @@ Access to selected resources in Genesys is protected and user permissions are ch
any API action is executed. Each organization contributing data to Genesys will have
one or more registered user accounts on Genesys.
To modify any data in Genesys, you will need appropriate permissions.
Permission to access and manage data for the organization is granted by
helpdesk@genesys-pgr.org upon request. Please contact helpdesk@genesys-pgr.org with the list
To modify any data in Genesys, you will need appropriate permissions.
Permission to access and manage data for the organization is granted by
helpdesk@genesys-pgr.org upon request. Please contact helpdesk@genesys-pgr.org with the list
of WIEWS codes of institutes you wish to manage.
To access resources with the APIs described in this manual, you will first need to
To access resources with the APIs described in this manual, you will first need to
create a user account. The simplest is to https://sandbox.genesys-pgr.org/google/login[use your Google+ account]
or alternatively https://sandbox.genesys-pgr.org/registration[creating an account manually].
......@@ -20,17 +20,17 @@ image::user-account-create.png[role="text-center"]
Access to the APIs is managed by https://en.wikipedia.org/wiki/OAuth#OAuth_2.0[OAuth 2.0] protocol and implemented
using http://docs.spring.io/spring-security/oauth/[spring-security OAuth]
modules. All API access is over HTTPS, and accessed from the https://www.genesys-pgr.org domain or
through https://sandbox.genesys-pgr.org for testing purposes.
modules. All API access is over HTTPS, and accessed from the https://www.genesys-pgr.org domain or
through https://sandbox.genesys-pgr.org for testing purposes.
To obtain OAuth access and refresh tokens, you will first need a valid Client ID and Client Secret.
These are generated by helpdesk@genesys-pgr.org for each individual consumer application.
These are generated by helpdesk@genesys-pgr.org for each individual consumer application.
The ID and Secret listed below are valid for the Sandbox environment and allows of out-of-band authentication
when using `curl` in the examples in this manual.
[cols="1,2"]
.Client ID and Secret for OOB
[cols="1,2"]
.Client ID and Secret for OOB
|===
|Client ID
|`dLCiR.MzwkNha18ImEcw0ADli0@sandbox.genesys-pgr.org`
......@@ -42,17 +42,17 @@ when using `curl` in the examples in this manual.
=== Obtaining the access token
Most OAuth libraries, including https://bitbucket.org/genesys2/genesys-client-api[genesys-client-api]
Java library, will automatically obtain the access token following the OAuth protocol. This
Java library, will automatically obtain the access token following the OAuth protocol. This
section describes how to manually obtain the tokens.
Log-in to Genesys with your account or Google+
Obtain a verifier code by granting access to the "Genesys API reference" client. This is
Obtain a verifier code by granting access to the "Genesys API reference" client. This is
initiated by opening the authorization URL in a browser (please substitute the CLIENTID and SECRET
with valid data):
----
https://sandbox.genesys-pgr.org/oauth/authorize?client_id=CLIENTID&client_secret=SECRET&response_type=code&redirect_uri=oob&scope=read%2Cwrite
https://sandbox.genesys-pgr.org/oauth/authorize?client_id=CLIENTID&client_secret=SECRET&response_type=code&redirect_uri=oob&scope=write
----
The server will prompt you to authorize the access to your protected resources on Genesys.
......@@ -63,7 +63,7 @@ Copy the authorization code: *THECODE* (looks like: 7wXP1r) and from shell, exec
$ curl 'https://sandbox.genesys-pgr.org/oauth/token?grant_type=authorization_code&client_id=CLIENTID&client_secret=SECRET&redirect_uri=oob&code=THECODE'
----
The server will respond with access token details in JSON format:
The server will respond with access token details in JSON format:
[source,json]
----
......@@ -76,7 +76,7 @@ The server will respond with access token details in JSON format:
}
----
You can use the access token to sign future HTTP requests to the API by adding a HTTP request header:
You can use the access token to sign future HTTP requests to the API by adding a HTTP request header:
[source,http]
----
......@@ -97,10 +97,20 @@ or include it in the request URL as a query string parameter:
$ curl 'https://sandbox.genesys-pgr.org/api/v0/me?access_token=OAUTH-ACCESS-TOKEN'
----
=== System-to-System integration
With Genesys release 2.3 the *Client authentication grant* is also supported. Kindly
contact the helpdesk@genesys-pgr.org for information how to enable system-to-system
integration and to grant your Client the appropriate permissions on Genesys.
**Note**: The client will be able to modify any data on Genesys on your behalf.
Read the section <<oauth, Client Credential Grant>>.
=== Using the refresh token
OAuth access tokens have a fairly short lifetime. When an access token expires, the
refresh token can be used to obtain a new access token. Refresh token is returned as
OAuth access tokens have a fairly short lifetime. When an access token expires, the
refresh token can be used to obtain a new access token. Refresh token is returned as
part of JSON response when verification code is used to obtain the access token:
[source,json]
......@@ -122,10 +132,9 @@ $ curl 'https://sandbox.genesys-pgr.org/oauth/token?grant_type=refresh_token&cli
----
== Client errors
The API returns descriptive information in the response body about why the request failed to execute.
The API returns descriptive information in the response body about why the request failed to execute.
=== 401 Unauthorized
......@@ -141,3 +150,9 @@ WWW-Authenticate: Bearer realm="genesys2", error="unauthorized", error_descripti
{"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}
----
[bibliography]
- [[[doi]]] https://en.wikipedia.org
https://en.wikipedia.org/wiki/Digital_object_identifier[Digital object identifier].
- [[[oauth]]] A Bilbie
https://alexbilbie.com/guide-to-oauth-2-grants/[A Guide To OAuth 2.0 Grants].
......@@ -50,6 +50,7 @@ public class CreateAdminListener extends RunAsAdminListener {
private void createDefaultAccounts() throws UserException, PasswordPolicyException {
createAdmin("SYSTEM", "SYSTEM", null, AccountType.SYSTEM);
// TODO read from props
createAdmin("admin@example.com", "First Admin", "Admin123!", AccountType.LOCAL);
}
......
/**
* Copyright 2014 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.model.oauth;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.genesys.blocks.model.BasicModel;
@Entity
@Table(name = "oauthaccesstoken")
public class OAuthAccessToken extends BasicModel implements OAuthToken {
private static final long serialVersionUID = -424404058531521676L;
@Column(nullable = false)
private String clientId;
@Column(length = 36)
private String refreshToken;
@Column
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
@Column
private Date expiration;
@Column(length = 100)
private String tokenType;
@Column(length = 100, nullable = false)
private String value;
@Column(length = 100)
private String scopes;
@Column(length = 500)
private String additionalInfo;
@Column(length = 64)
private String userUuid;
@Column(length = 200)
private String redirectUri;
@Column(length = 200, nullable = false, unique = true)
private String authenticationId;
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public String getAuthenticationId() {
return authenticationId;
}
public void setAuthenticationId(String authenticationId) {
this.authenticationId = authenticationId;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public void setExpiration(Date expiration) {
this.expiration = expiration;
}
public Date getExpiration() {
return expiration;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getTokenType() {
return tokenType;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setScopes(String scopes) {
this.scopes = scopes;
}
public String getScopes() {
return scopes;
}
public void setAdditionalInfo(String additionalInfo) {
this.additionalInfo = additionalInfo;
}
public String getAdditionalInfo() {
return additionalInfo;
}
public void setUserUuid(String uuid) {
this.userUuid = uuid;
}
public String getUserUuid() {
return userUuid;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
}
/**
* Copyright 2014 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.model.oauth;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.genesys.blocks.model.BasicModel;
@Entity
@Table(name = "authorities")
public class OAuthAuthorities extends BasicModel {
private static final long serialVersionUID = 1637383923219682635L;
@Column(name = "username")
private String username;
@Column(name = "authority")
private String authority;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
/**
* Copyright 2014 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.genesys2.server.model.oauth;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.genesys.blocks.model.AuditedVersionedModel;
import org.genesys.blocks.security.model.AclAwareModel;
import org.hibernate.annotations.Type;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.ClientDetails;
@Entity
@Table(name = "oauthclient")
public class OAuthClientDetails extends AuditedVersionedModel implements ClientDetails, AclAwareModel {
private static final long serialVersionUID = 5328458631619687041L;
@Column(length = 200)
private String title;
@Lob
@Column
@Type(type = "org.hibernate.type.TextType")
private String description;
@Column(unique = true, nullable = false)
private String clientId;
@Column(length = 100)
private String clientSecret;
@Column(length = 400)
private String resourceIds;
@Column(length = 400)
private String scope;
@Column(length = 400)
private String authorities;
@Column(length = 400)
private String authorizedGrantTypes;
@Column(length = 400)
private String redirectUris;
@Column
private Integer accessTokenValiditySeconds;
@Column
private Integer refreshTokenValiditySeconds;
@Column(name = "additional_information")
private String additionalInformation;
// @Enumerated(EnumType.STRING)
// @Column(name = "clientType", length=50)
// private OAuthClientType clientType;
public OAuthClientDetails() {
}
public OAuthClientDetails(String clientId, String resourceIds, String scopes, String grantTypes, String authorities) {
this(clientId, resourceIds, scopes, grantTypes, authorities, null);
}
public OAuthClientDetails(String clientId, String resourceIds, String scopes, String grantTypes, String authorities, String redirectUris) {
this.clientId = clientId;
if (StringUtils.isNotBlank(resourceIds)) {
this.resourceIds = resourceIds;
}
if (StringUtils.isNotBlank(scopes)) {
this.scope = scopes;
}
if (StringUtils.isNotBlank(grantTypes)) {
this.authorizedGrantTypes = grantTypes;
} else {
this.authorizedGrantTypes = "authorization_code,refresh_token";
}
if (StringUtils.isNotBlank(authorities)) {
this.authorities = authorities;
}
if (StringUtils.isNotBlank(redirectUris)) {
this.redirectUris = redirectUris;
}
}
// public OAuthClientType getClientType() {
// return clientType;
// }
//
// public void setClientType(OAuthClientType clientType) {
// this.clientType = clientType;