Implemented OAuth2 client info controller, super clients and unlimited
authorization scopes. Enabled using Bearer tokens as user authentication
methods for many controllers including OAuth2 controllers.
Change-Id: I1043164acbe49501210a6fca7f4531d110eb81a5
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java b/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java
index 2d7c285..286195c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Initializator.java
@@ -1,8 +1,7 @@
package de.ids_mannheim.korap.config;
import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.EnumSet;
import org.springframework.beans.factory.annotation.Autowired;
@@ -35,11 +34,7 @@
}
private void setInitialAccessScope () {
- OAuth2Scope[] enums = OAuth2Scope.values();
- Set<String> scopes = new HashSet<>(enums.length);
- for (OAuth2Scope s : enums) {
- scopes.add(s.toString());
- }
+ EnumSet<OAuth2Scope> scopes = EnumSet.allOf(OAuth2Scope.class);
accessScopeDao.storeAccessScopes(scopes);
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
index 7e6c973..c44cc53 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/constant/OAuth2Scope.java
@@ -1,8 +1,40 @@
package de.ids_mannheim.korap.oauth2.constant;
public enum OAuth2Scope {
-
- OPENID, SEARCH, SERIALIZE_QUERY, MATCH_INFO, CREATE_VC, LIST_VC, EDIT_VC, VC_INFO, CLIENT_INFO;
+
+ ALL,
+ ADMIN,
+
+ OPENID,
+ AUTHORIZE,
+
+ CLIENT_INFO,
+ REGISTER_CLIENT,
+ DEREGISTER_CLIENT,
+ RESET_CLIENT_SECRET,
+
+ SEARCH,
+ SERIALIZE_QUERY,
+ MATCH_INFO,
+
+ USER_GROUP_INFO,
+ CREATE_USER_GROUP,
+ DELETE_USER_GROUP,
+
+ DELETE_USER_GROUP_MEMBER,
+ ADD_USER_GROUP_MEMBER,
+
+ ADD_USER_GROUP_MEMBER_ROLE,
+ DELETE_USER_GROUP_MEMBER_ROLE,
+
+ CREATE_VC,
+ VC_INFO,
+ EDIT_VC,
+ DELETE_VC,
+
+ SHARE_VC,
+ DELETE_VC_ACCESS,
+ VC_ACCESS_INFO;
@Override
public String toString () {
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
index a94b506..aa158a3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/AccessScopeDao.java
@@ -13,6 +13,7 @@
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
import de.ids_mannheim.korap.oauth2.entity.AccessScope;
@Repository
@@ -33,10 +34,10 @@
return q.getResultList();
}
- public void storeAccessScopes (Set<String> scopes) {
+ public void storeAccessScopes (Set<OAuth2Scope> scopes) {
List<AccessScope> existingScopes = retrieveAccessScopes();
AccessScope newScope;
- for (String scope : scopes) {
+ for (OAuth2Scope scope : scopes) {
newScope = new AccessScope(scope);
if (!existingScopes.contains(newScope)) {
entityManager.persist(newScope);
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
index 2b8a2eb..31fa2b3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dao/OAuth2ClientDao.java
@@ -27,9 +27,9 @@
private EntityManager entityManager;
public void registerClient (String id, String secretHashcode, String name,
- OAuth2ClientType type, boolean isNative, String url,
- int urlHashCode, String redirectURI, String registeredBy,
- String description) throws KustvaktException {
+ OAuth2ClientType type, String url, int urlHashCode,
+ String redirectURI, String registeredBy, String description)
+ throws KustvaktException {
ParameterChecker.checkStringValue(id, "client id");
ParameterChecker.checkStringValue(name, "client name");
ParameterChecker.checkObjectValue(type, "client type");
@@ -44,7 +44,6 @@
client.setName(name);
client.setSecret(secretHashcode);
client.setType(type);
- client.setNative(isNative);
if (urlHashCode != 0) {
OAuth2ClientUrl clientUrl = new OAuth2ClientUrl();
clientUrl.setUrl(url);
@@ -84,7 +83,8 @@
}
}
- public void deregisterClient (OAuth2Client client) throws KustvaktException {
+ public void deregisterClient (OAuth2Client client)
+ throws KustvaktException {
ParameterChecker.checkObjectValue(client, "client");
if (!entityManager.contains(client)) {
client = entityManager.merge(client);
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/OAuth2ClientDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientDto.java
similarity index 94%
rename from full/src/main/java/de/ids_mannheim/korap/dto/OAuth2ClientDto.java
rename to full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientDto.java
index b714404..3f6199b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/OAuth2ClientDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientDto.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.dto;
+package de.ids_mannheim.korap.oauth2.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
new file mode 100644
index 0000000..85003ae
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/dto/OAuth2ClientInfoDto.java
@@ -0,0 +1,89 @@
+package de.ids_mannheim.korap.oauth2.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
+import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
+
+@JsonInclude(Include.NON_EMPTY)
+public class OAuth2ClientInfoDto {
+
+ private String id;
+ private String name;
+ private String description;
+ private String isSuper;
+ private String redirectURI;
+ private String registeredBy;
+ private OAuth2ClientType type;
+
+ public OAuth2ClientInfoDto (OAuth2Client client) {
+ this.id = client.getId();
+ this.name = client.getName();
+ this.description = client.getDescription();
+ this.setType(client.getType());
+ this.redirectURI = client.getRedirectURI();
+ this.registeredBy = client.getRegisteredBy();
+
+ if (client.isSuper()) {
+ this.isSuper = "true";
+ }
+ }
+
+ 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 getDescription () {
+ return description;
+ }
+
+ public void setDescription (String description) {
+ this.description = description;
+ }
+
+ public String getIsSuper () {
+ return isSuper;
+ }
+
+ public void setIsSuper (String isSuper) {
+ this.isSuper = isSuper;
+ }
+
+ public String getRedirectURI () {
+ return redirectURI;
+ }
+
+ public void setRedirectURI (String redirectURI) {
+ this.redirectURI = redirectURI;
+ }
+
+ public String getRegisteredBy () {
+ return registeredBy;
+ }
+
+ public void setRegisteredBy (String registeredBy) {
+ this.registeredBy = registeredBy;
+ }
+
+ public OAuth2ClientType getType () {
+ return type;
+ }
+
+ public void setType (OAuth2ClientType type) {
+ this.type = type;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
index f7951d9..b26a90a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/AccessScope.java
@@ -4,11 +4,14 @@
import java.util.List;
import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
import lombok.Getter;
import lombok.Setter;
@@ -21,11 +24,12 @@
private static final long serialVersionUID = -7356877266702636705L;
@Id
- private String id;
+ @Enumerated(EnumType.STRING)
+ private OAuth2Scope id;
public AccessScope () {}
- public AccessScope (String scope) {
+ public AccessScope (OAuth2Scope scope) {
this.id = scope;
}
@@ -37,7 +41,7 @@
@Override
public String toString () {
- return id;
+ return id.toString();
}
@Override
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
index 0fb0688..09c6e35 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/entity/OAuth2Client.java
@@ -12,15 +12,11 @@
import javax.persistence.Table;
import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
-import lombok.Getter;
-import lombok.Setter;
/**
* @author margaretha
*
*/
-@Getter
-@Setter
@Entity
@Table(name = "oauth2_client")
public class OAuth2Client {
@@ -32,23 +28,95 @@
private String secret;
@Enumerated(EnumType.STRING)
private OAuth2ClientType type;
- @Column(name = "native")
- private boolean isNative;
+ @Column(name = "super")
+ private boolean isSuper;
@Column(name = "redirect_uri")
private String redirectURI;
@Column(name = "registered_by")
private String registeredBy;
private String description;
- @OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
+ @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "url_id")
private OAuth2ClientUrl clientUrl;
@Override
public String toString () {
return "id=" + id + ", name=" + name + ", secret=" + secret + ", type="
- + type + ", isNative=" + isNative + ", redirectURI="
+ + type + ", isSuper=" + isSuper() + ", redirectURI="
+ redirectURI + ", registeredBy=" + registeredBy
+ ", description=" + description;
}
+
+ public boolean isSuper () {
+ return isSuper;
+ }
+
+ public void setSuper (boolean isSuper) {
+ this.isSuper = isSuper;
+ }
+
+ 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 getSecret () {
+ return secret;
+ }
+
+ public void setSecret (String secret) {
+ this.secret = secret;
+ }
+
+ public OAuth2ClientType getType () {
+ return type;
+ }
+
+ public void setType (OAuth2ClientType type) {
+ this.type = type;
+ }
+
+ public String getRedirectURI () {
+ return redirectURI;
+ }
+
+ public void setRedirectURI (String redirectURI) {
+ this.redirectURI = redirectURI;
+ }
+
+ public String getRegisteredBy () {
+ return registeredBy;
+ }
+
+ public void setRegisteredBy (String registeredBy) {
+ this.registeredBy = registeredBy;
+ }
+
+ public String getDescription () {
+ return description;
+ }
+
+ public void setDescription (String description) {
+ this.description = description;
+ }
+
+ public OAuth2ClientUrl getClientUrl () {
+ return clientUrl;
+ }
+
+ public void setClientUrl (OAuth2ClientUrl clientUrl) {
+ this.clientUrl = clientUrl;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
index 7ce105f..cbd638d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
@@ -1,9 +1,7 @@
package de.ids_mannheim.korap.oauth2.oltu.service;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.net.URLEncoder;
import java.time.ZonedDateTime;
import javax.servlet.http.HttpServletRequest;
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
index bb9baa5..16ff581 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuTokenService.java
@@ -2,6 +2,7 @@
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -211,14 +212,16 @@
OAuth2Client client =
clientService.authenticateClient(clientId, clientSecret);
- if (!client.isNative()) {
+ if (!client.isSuper()) {
throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
"Password grant is not allowed for third party clients",
OAuth2Error.UNAUTHORIZED_CLIENT);
}
if (scopes == null || scopes.isEmpty()) {
- scopes = config.getDefaultAccessScopes();
+ scopes = new HashSet<String>(1);
+ scopes.add("all");
+// scopes = config.getDefaultAccessScopes();
}
ZonedDateTime authenticationTime =
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
index 341ff97..5997ca0 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdAuthorizationService.java
@@ -111,9 +111,6 @@
if (scope != null) {
scopeSet = new HashSet<>(scope.toStringList());
}
- else {
- scopeSet = config.getDefaultAccessScopes();
- }
createAuthorization(username, clientId, redirectUriStr, scopeSet,
code.getValue(), authenticationTime, nonce);
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
index 91e724a..a17b4c8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/openid/service/OpenIdTokenService.java
@@ -165,7 +165,7 @@
clientCredentials[1]);
}
- if (!client.isNative()) {
+ if (!client.isSuper()) {
throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
"Password grant is not allowed for third party clients",
OAuth2Error.UNAUTHORIZED_CLIENT);
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
index 53027f4..cdd6a66 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ClientService.java
@@ -13,7 +13,6 @@
import de.ids_mannheim.korap.config.FullConfiguration;
import de.ids_mannheim.korap.dao.AdminDao;
-import de.ids_mannheim.korap.dto.OAuth2ClientDto;
import de.ids_mannheim.korap.encryption.RandomCodeGenerator;
import de.ids_mannheim.korap.exceptions.KustvaktException;
import de.ids_mannheim.korap.exceptions.StatusCodes;
@@ -22,6 +21,8 @@
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
import de.ids_mannheim.korap.oauth2.dao.OAuth2ClientDao;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
import de.ids_mannheim.korap.oauth2.entity.AccessToken;
import de.ids_mannheim.korap.oauth2.entity.Authorization;
import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
@@ -86,7 +87,7 @@
redirectURI + " is invalid.", OAuth2Error.INVALID_REQUEST);
}
- boolean isNative = isNativeClient(url, redirectURI);
+ // boolean isNative = isNativeClient(url, redirectURI);
String secret = null;
String secretHashcode = null;
@@ -111,8 +112,8 @@
String id = codeGenerator.createRandomCode();
try {
clientDao.registerClient(id, secretHashcode, clientJson.getName(),
- clientJson.getType(), isNative, url, urlHashCode,
- redirectURI, registeredBy, clientJson.getDescription());
+ clientJson.getType(), url, urlHashCode, redirectURI,
+ registeredBy, clientJson.getDescription());
}
catch (Exception e) {
Throwable cause = e;
@@ -131,7 +132,7 @@
return new OAuth2ClientDto(id, secret);
}
-
+ @Deprecated
private boolean isNativeClient (String url, String redirectURI)
throws KustvaktException {
if (url == null || url.isEmpty() || redirectURI == null
@@ -170,7 +171,6 @@
return true;
}
-
public void deregisterClient (String clientId, String clientSecret,
String username) throws KustvaktException {
@@ -185,13 +185,13 @@
clientDao.deregisterClient(client);
// revoke all related authorization tokens
- List<Authorization> authList = authorizationDao
- .retrieveAuthorizationsByClientId(clientId);
- for (Authorization authorization : authList){
+ List<Authorization> authList =
+ authorizationDao.retrieveAuthorizationsByClientId(clientId);
+ for (Authorization authorization : authList) {
authorization.setRevoked(true);
authorizationDao.updateAuthorization(authorization);
}
-
+
// revoke all related access tokens
List<AccessToken> tokens =
tokenDao.retrieveAccessTokenByClientId(clientId);
@@ -212,8 +212,7 @@
OAuth2Client client = authenticateClient(clientId, clientSecret);
if (!client.getType().equals(OAuth2ClientType.CONFIDENTIAL)) {
- throw new KustvaktException(
- StatusCodes.NOT_ALLOWED,
+ throw new KustvaktException(StatusCodes.NOT_ALLOWED,
"Operation is not allowed for public clients",
OAuth2Error.INVALID_REQUEST);
}
@@ -259,6 +258,14 @@
OAuth2Error.INVALID_REQUEST);
}
}
+ else if (client.getSecret() == null || client.getSecret().isEmpty()){
+ if (client.getType().equals(OAuth2ClientType.CONFIDENTIAL)){
+ throw new KustvaktException(
+ StatusCodes.CLIENT_AUTHENTICATION_FAILED,
+ "Client secret was not registered",
+ OAuth2Error.INVALID_CLIENT);
+ }
+ }
else if (!encryption.checkHash(clientSecret, client.getSecret(),
config.getPasscodeSaltField())) {
throw new KustvaktException(
@@ -267,7 +274,6 @@
}
}
-
public OAuth2Client authenticateClientId (String clientId)
throws KustvaktException {
if (clientId == null || clientId.isEmpty()) {
@@ -279,4 +285,36 @@
return clientDao.retrieveClientById(clientId);
}
+
+ public void updatePrivilege (String username, String clientId,
+ boolean isSuper) throws KustvaktException {
+
+ if (adminDao.isAdmin(username)) {
+ OAuth2Client client = clientDao.retrieveClientById(clientId);
+ if (isSuper && !client.getType()
+ .equals(OAuth2ClientType.CONFIDENTIAL)) {
+ throw new KustvaktException(StatusCodes.NOT_ALLOWED,
+ "Only confidential clients are allowed to be super clients.");
+ }
+ client.setSuper(isSuper);
+ clientDao.updateClient(client);
+ }
+ else {
+ throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ }
+ }
+
+ public OAuth2ClientInfoDto retrieveClientInfo (String username,
+ String clientId) throws KustvaktException {
+ OAuth2Client client = clientDao.retrieveClientById(clientId);
+ if (adminDao.isAdmin(username)
+ || username.equals(client.getRegisteredBy())) {
+ return new OAuth2ClientInfoDto(client);
+ }
+ else {
+ throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ }
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
index c820a30..bfc35c1 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
@@ -45,8 +45,18 @@
Set<AccessScope> requestedScopes =
new HashSet<AccessScope>(scopes.size());
int index;
+ OAuth2Scope oauth2Scope = null;
for (String scope : scopes) {
- index = definedScopes.indexOf(new AccessScope(scope));
+ try{
+ oauth2Scope = Enum.valueOf(OAuth2Scope.class, scope.toUpperCase());
+ }
+ catch (IllegalArgumentException e) {
+ throw new KustvaktException(StatusCodes.INVALID_SCOPE,
+ scope + " is an invalid scope",
+ OAuth2Error.INVALID_SCOPE);
+ }
+
+ index = definedScopes.indexOf(new AccessScope(oauth2Scope));
if (index == -1) {
throw new KustvaktException(StatusCodes.INVALID_SCOPE,
scope + " is an invalid scope",
@@ -88,15 +98,16 @@
return filteredScopes;
}
- public void verifyScope (TokenContext context, OAuth2Scope requestScope)
+ public void verifyScope (TokenContext context, OAuth2Scope requiredScope)
throws KustvaktException {
if (!adminDao.isAdmin(context.getUsername())
&& context.getTokenType().equals(TokenType.BEARER)) {
Map<String, Object> parameters = context.getParameters();
- String scope = (String) parameters.get(Attributes.SCOPE);
- if (!scope.contains(requestScope.toString())) {
+ String authorizedScope = (String) parameters.get(Attributes.SCOPE);
+ if (!authorizedScope.contains(OAuth2Scope.ALL.toString())
+ && !authorizedScope.contains(requiredScope.toString())) {
throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
- "Scope " + requestScope + " is not authorized");
+ "Scope " + requiredScope + " is not authorized");
}
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index 082f976..ac082a9 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -27,10 +27,12 @@
import com.sun.jersey.spi.container.ResourceFilters;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2AuthorizationRequest;
import de.ids_mannheim.korap.oauth2.oltu.OAuth2RevokeTokenRequest;
import de.ids_mannheim.korap.oauth2.oltu.service.OltuAuthorizationService;
import de.ids_mannheim.korap.oauth2.oltu.service.OltuTokenService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
@@ -47,6 +49,8 @@
private OltuTokenService tokenService;
@Autowired
private OltuAuthorizationService authorizationService;
+ @Autowired
+ private OAuth2ScopeService scopeService;
/**
* Requests an authorization code.
@@ -73,7 +77,6 @@
@Path("authorize")
@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response requestAuthorizationCode (
@Context HttpServletRequest request,
@Context SecurityContext context, @FormParam("state") String state,
@@ -82,10 +85,12 @@
TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
String username = tokenContext.getUsername();
ZonedDateTime authTime = tokenContext.getAuthenticationTime();
-
- HttpServletRequest requestWithForm =
- new FormRequestWrapper(request, form);
+
try {
+ scopeService.verifyScope(tokenContext, OAuth2Scope.AUTHORIZE);
+
+ HttpServletRequest requestWithForm =
+ new FormRequestWrapper(request, form);
OAuth2AuthorizationRequest authzRequest =
new OAuth2AuthorizationRequest(requestWithForm);
String uri = authorizationService.requestAuthorizationCode(
@@ -218,7 +223,6 @@
@POST
@Path("revoke")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response revokeAccessToken (@Context HttpServletRequest request,
MultivaluedMap<String, String> form) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
index 43e940b..e9aa803 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2WithOpenIdController.java
@@ -32,12 +32,14 @@
import com.sun.jersey.spi.container.ResourceFilters;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
import de.ids_mannheim.korap.oauth2.openid.OpenIdConfiguration;
import de.ids_mannheim.korap.oauth2.openid.OpenIdHttpRequestWrapper;
import de.ids_mannheim.korap.oauth2.openid.service.JWKService;
import de.ids_mannheim.korap.oauth2.openid.service.OpenIdAuthorizationService;
import de.ids_mannheim.korap.oauth2.openid.service.OpenIdConfigService;
import de.ids_mannheim.korap.oauth2.openid.service.OpenIdTokenService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.web.OpenIdResponseHandler;
import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
@@ -56,7 +58,9 @@
private JWKService jwkService;
@Autowired
private OpenIdConfigService configService;
-
+ @Autowired
+ private OAuth2ScopeService scopeService;
+
@Autowired
private OpenIdResponseHandler openIdResponseHandler;
@@ -135,6 +139,8 @@
URI uri = null;
try {
+ scopeService.verifyScope(tokenContext, OAuth2Scope.AUTHORIZE);
+
if (isAuthentication) {
authzService.checkRedirectUriParam(map);
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
index 293685e..387194c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuthClientController.java
@@ -3,6 +3,7 @@
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -17,31 +18,50 @@
import com.sun.jersey.spi.container.ResourceFilters;
-import de.ids_mannheim.korap.dto.OAuth2ClientDto;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientDto;
+import de.ids_mannheim.korap.oauth2.dto.OAuth2ClientInfoDto;
import de.ids_mannheim.korap.oauth2.service.OAuth2ClientService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.web.OAuth2ResponseHandler;
import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
import de.ids_mannheim.korap.web.filter.BlockingFilter;
import de.ids_mannheim.korap.web.input.OAuth2ClientJson;
-
/**
* Defines controllers for OAuth2 clients, namely applications
* performing actions such as searching and retrieving match
* information on behalf of users.
*
+ * <br /><br />
+ * According to its privileges, clients are categorized into super and
+ * normal clients. Super clients are intended only for clients that
+ * are part of KorAP. They has special privileges to use controllers
+ * that usually are not allowed for normal clients, for instance using
+ * OAuth2 password grant to obtain access tokens.
+ *
+ * <br /><br />
+ * By default, clients are set as normal clients. Super clients has to
+ * be set manually by an admin, e.g by using
+ * {@link #updateClientPrivilege(SecurityContext, String, boolean)}
+ * controller. Only confidential clients are allowed to be super
+ * clients.
+ *
* @author margaretha
*
*/
@Controller
@Path("oauth2/client")
+@ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
public class OAuthClientController {
@Autowired
private OAuth2ClientService clientService;
@Autowired
+ private OAuth2ScopeService scopeService;
+ @Autowired
private OAuth2ResponseHandler responseHandler;
/**
@@ -70,13 +90,13 @@
@Path("register")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
- @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
public OAuth2ClientDto registerClient (
@Context SecurityContext securityContext,
OAuth2ClientJson clientJson) {
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.REGISTER_CLIENT);
return clientService.registerClient(clientJson,
context.getUsername());
}
@@ -85,7 +105,6 @@
}
}
-
/**
* Deregisters a client requires client owner authentication. For
* confidential clients, client authentication is also required.
@@ -101,7 +120,6 @@
@DELETE
@Path("deregister/{client_id}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
- @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
public Response deregisterPublicClient (
@Context SecurityContext securityContext,
@PathParam("client_id") String clientId,
@@ -109,6 +127,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DEREGISTER_CLIENT);
clientService.deregisterClient(clientId, clientSecret,
context.getUsername());
return Response.ok().build();
@@ -132,7 +151,6 @@
@Path("reset")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
- @ResourceFilters({ AuthenticationFilter.class, BlockingFilter.class })
public OAuth2ClientDto resetClientSecret (
@Context SecurityContext securityContext,
@FormParam("client_id") String clientId,
@@ -140,6 +158,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.RESET_CLIENT_SECRET);
return clientService.resetSecret(clientId, clientSecret,
context.getUsername());
}
@@ -148,4 +167,51 @@
}
}
+ /**
+ * Facilitates editing client privileges for admin purposes, e.g.
+ * setting a specific client to be a super client, and vice versa.
+ * Only confidential clients are allowed to be super clients.
+ *
+ * @param securityContext
+ * @param clientId
+ * @param super true indicating super client, false otherwise
+ * @return Response status OK, if successful
+ */
+ @POST
+ @Path("privilege")
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response updateClientPrivilege (
+ @Context SecurityContext securityContext,
+ @FormParam("client_id") String clientId,
+ @FormParam("super") String isSuper) {
+ TokenContext context =
+ (TokenContext) securityContext.getUserPrincipal();
+ try {
+ scopeService.verifyScope(context, OAuth2Scope.ADMIN);
+ clientService.updatePrivilege(context.getUsername(), clientId,
+ Boolean.valueOf(isSuper));
+ return Response.ok().build();
+ }
+ catch (KustvaktException e) {
+ throw responseHandler.throwit(e);
+ }
+ }
+
+ @GET
+ @Path("info/{client_id}")
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public OAuth2ClientInfoDto retrieveClientInfo (
+ @Context SecurityContext securityContext,
+ @PathParam("client_id") String clientId) {
+ TokenContext context =
+ (TokenContext) securityContext.getUserPrincipal();
+ try {
+ scopeService.verifyScope(context, OAuth2Scope.CLIENT_INFO);
+ return clientService.retrieveClientInfo(context.getUsername(),
+ clientId);
+ }
+ catch (KustvaktException e) {
+ throw responseHandler.throwit(e);
+ }
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index a0850ce..8e82de0 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -24,6 +24,8 @@
import de.ids_mannheim.korap.constant.UserGroupStatus;
import de.ids_mannheim.korap.dto.UserGroupDto;
import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
import de.ids_mannheim.korap.security.context.TokenContext;
import de.ids_mannheim.korap.service.UserGroupService;
import de.ids_mannheim.korap.web.KustvaktResponseHandler;
@@ -54,6 +56,8 @@
private KustvaktResponseHandler kustvaktResponseHandler;
@Autowired
private UserGroupService service;
+ @Autowired
+ private OAuth2ScopeService scopeService;
/**
* Returns all user-groups in which a user is an active or a
@@ -74,6 +78,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.USER_GROUP_INFO);
return service.retrieveUserGroup(context.getUsername());
}
catch (KustvaktException e) {
@@ -106,6 +111,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.ADMIN);
return service.retrieveUserGroupByStatus(username,
context.getUsername(), status);
}
@@ -131,6 +137,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.ADMIN);
return service.searchById(context.getUsername(), groupId);
}
catch (KustvaktException e) {
@@ -168,6 +175,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.CREATE_USER_GROUP);
service.createUserGroup(group, context.getUsername());
return Response.ok().build();
}
@@ -178,8 +186,7 @@
/**
* Deletes a user-group specified by the group id. Only group
- * owner
- * and system admins can delete groups.
+ * owner and system admins can delete groups.
*
* @param securityContext
* @param groupId
@@ -192,6 +199,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP);
service.deleteGroup(groupId, context.getUsername());
return Response.ok().build();
}
@@ -219,6 +227,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER);
service.deleteGroupMember(memberId, groupId, context.getUsername());
return Response.ok().build();
}
@@ -247,6 +256,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER);
service.inviteGroupMembers(group, context.getUsername());
return Response.ok().build();
}
@@ -279,6 +289,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER_ROLE);
service.addMemberRoles(context.getUsername(), groupId,
memberUsername, roleIds);
return Response.ok().build();
@@ -290,8 +301,7 @@
/**
* Deletes roles of a member of a user-group. Only user-group
- * admins
- * and system admins are allowed.
+ * admins and system admins are allowed.
*
* @param securityContext
* @param groupId
@@ -312,6 +322,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER_ROLE);
service.deleteMemberRoles(context.getUsername(), groupId,
memberUsername, roleIds);
return Response.ok().build();
@@ -339,6 +350,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.ADD_USER_GROUP_MEMBER);
service.acceptInvitation(groupId, context.getUsername());
return Response.ok().build();
}
@@ -367,6 +379,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DELETE_USER_GROUP_MEMBER);
service.deleteGroupMember(context.getUsername(), groupId,
context.getUsername());
return Response.ok().build();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index 24d6e52..2627bdc 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -21,13 +21,11 @@
import com.sun.jersey.spi.container.ResourceFilters;
-import de.ids_mannheim.korap.constant.TokenType;
import de.ids_mannheim.korap.constant.VirtualCorpusAccessStatus;
import de.ids_mannheim.korap.constant.VirtualCorpusType;
import de.ids_mannheim.korap.dto.VirtualCorpusAccessDto;
import de.ids_mannheim.korap.dto.VirtualCorpusDto;
import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
import de.ids_mannheim.korap.oauth2.constant.OAuth2Scope;
import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
import de.ids_mannheim.korap.security.context.TokenContext;
@@ -229,11 +227,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
- if (context.getTokenType().equals(TokenType.BEARER)){
- throw new KustvaktException(
- StatusCodes.AUTHENTICATION_FAILED,
- "Token type Bearer is not allowed");
- }
+ scopeService.verifyScope(context, OAuth2Scope.ADMIN);
return service.listVCByType(context.getUsername(), createdBy, type);
}
catch (KustvaktException e) {
@@ -258,6 +252,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DELETE_VC);
service.deleteVC(context.getUsername(), vcId);
}
catch (KustvaktException e) {
@@ -289,6 +284,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.SHARE_VC);
service.shareVC(context.getUsername(), vcId, groupId);
}
catch (KustvaktException e) {
@@ -315,6 +311,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.DELETE_VC_ACCESS);
service.deleteVCAccess(accessId, context.getUsername());
}
catch (KustvaktException e) {
@@ -348,6 +345,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.VC_ACCESS_INFO);
return service.listVCAccessByVC(context.getUsername(), vcId);
}
catch (KustvaktException e) {
@@ -377,6 +375,7 @@
TokenContext context =
(TokenContext) securityContext.getUserPrincipal();
try {
+ scopeService.verifyScope(context, OAuth2Scope.VC_ACCESS_INFO);
return service.listVCAccessByGroup(context.getUsername(), groupId);
}
catch (KustvaktException e) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
index d0a6cf6..57d232d 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
@@ -75,15 +75,6 @@
context = authenticationManager.getTokenContext(
TokenType.BEARER, authData.getToken(), host,
ua);
- if (request.getPath().startsWith("oauth2")
- || request.getPath().startsWith("vc/access")
- || request.getPath().startsWith("vc/delete")
- || request.getPath().startsWith("group")
- || request.getPath().startsWith("user")) {
- throw new KustvaktException(
- StatusCodes.AUTHORIZATION_FAILED,
- "Token type Bearer is not allowed");
- }
break;
// EM: JWT token-based authentication scheme
case API: