Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • dBucik/endpoint_auth_methods
  • dBucik/skip_logout_confirm
  • fix_juridiction
  • main
  • revert-d2e009d2
  • token_endpoint_auth_methods
  • mitreid-connect-0.9.0
  • mitreid-connect-0.9.1
  • mitreid-connect-0.9.2
  • mitreid-connect-0.9.3
  • mitreid-connect-1.0.0
  • mitreid-connect-1.0.1
  • mitreid-connect-1.0.10
  • mitreid-connect-1.0.11
  • mitreid-connect-1.0.12
  • mitreid-connect-1.0.13
  • mitreid-connect-1.0.14
  • mitreid-connect-1.0.15
  • mitreid-connect-1.0.16
  • mitreid-connect-1.0.17
  • mitreid-connect-1.0.18
  • mitreid-connect-1.0.19
  • mitreid-connect-1.0.2
  • mitreid-connect-1.0.20
  • mitreid-connect-1.0.21
  • mitreid-connect-1.0.22
  • mitreid-connect-1.0.3
  • mitreid-connect-1.0.4
  • mitreid-connect-1.0.5
  • mitreid-connect-1.0.6
  • mitreid-connect-1.0.7
  • mitreid-connect-1.0.8
  • mitreid-connect-1.0.9
  • mitreid-connect-1.1.0
  • mitreid-connect-1.1.1
  • mitreid-connect-1.1.10
  • mitreid-connect-1.1.11
  • mitreid-connect-1.1.12
  • mitreid-connect-1.1.13
  • mitreid-connect-1.1.14
  • mitreid-connect-1.1.15
  • mitreid-connect-1.1.16
  • mitreid-connect-1.1.17
  • mitreid-connect-1.1.18
  • mitreid-connect-1.1.19
  • mitreid-connect-1.1.2
  • mitreid-connect-1.1.3
  • mitreid-connect-1.1.4
  • mitreid-connect-1.1.5
  • mitreid-connect-1.1.6
  • mitreid-connect-1.1.7
  • mitreid-connect-1.1.8
  • mitreid-connect-1.1.9
  • mitreid-connect-1.2.0
  • mitreid-connect-1.2.0-RC1
  • mitreid-connect-1.2.0-RC2
  • mitreid-connect-1.2.1
  • mitreid-connect-1.2.2
  • mitreid-connect-1.2.3
  • mitreid-connect-1.2.4
  • mitreid-connect-1.2.5
  • mitreid-connect-1.2.6
  • mitreid-connect-1.3.0
  • mitreid-connect-1.3.0-RC1
  • mitreid-connect-1.3.0-RC2
  • mitreid-connect-1.3.1
  • mitreid-connect-1.3.2
  • mitreid-connect-1.3.3
  • v10.0.0
  • v10.0.1
  • v10.0.2
  • v10.0.3
  • v10.1.0
  • v10.1.1
  • v10.1.2
  • v10.1.3
  • v10.2.0
  • v10.2.1
  • v10.3.0
  • v10.3.1
  • v10.3.2
  • v10.3.3
  • v10.3.4
  • v10.4.0
  • v10.5.0
  • v10.5.1
  • v10.5.2
  • v10.5.3
  • v10.5.4
  • v11.0.0
  • v11.0.1
  • v11.0.2
  • v11.0.3
  • v12.0.0
  • v12.0.1
  • v12.0.2
  • v12.0.3
  • v12.0.4
  • v12.0.5
  • v12.1.0
  • v12.1.1
  • v12.1.2
  • v12.1.3
  • v12.1.4
  • v12.1.5
  • v12.1.6
106 results

Target

Select target project
  • perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server
1 result
Select Git revision
  • dBucik/endpoint_auth_methods
  • dBucik/skip_logout_confirm
  • fix_juridiction
  • main
  • revert-d2e009d2
  • token_endpoint_auth_methods
  • mitreid-connect-0.9.0
  • mitreid-connect-0.9.1
  • mitreid-connect-0.9.2
  • mitreid-connect-0.9.3
  • mitreid-connect-1.0.0
  • mitreid-connect-1.0.1
  • mitreid-connect-1.0.10
  • mitreid-connect-1.0.11
  • mitreid-connect-1.0.12
  • mitreid-connect-1.0.13
  • mitreid-connect-1.0.14
  • mitreid-connect-1.0.15
  • mitreid-connect-1.0.16
  • mitreid-connect-1.0.17
  • mitreid-connect-1.0.18
  • mitreid-connect-1.0.19
  • mitreid-connect-1.0.2
  • mitreid-connect-1.0.20
  • mitreid-connect-1.0.21
  • mitreid-connect-1.0.22
  • mitreid-connect-1.0.3
  • mitreid-connect-1.0.4
  • mitreid-connect-1.0.5
  • mitreid-connect-1.0.6
  • mitreid-connect-1.0.7
  • mitreid-connect-1.0.8
  • mitreid-connect-1.0.9
  • mitreid-connect-1.1.0
  • mitreid-connect-1.1.1
  • mitreid-connect-1.1.10
  • mitreid-connect-1.1.11
  • mitreid-connect-1.1.12
  • mitreid-connect-1.1.13
  • mitreid-connect-1.1.14
  • mitreid-connect-1.1.15
  • mitreid-connect-1.1.16
  • mitreid-connect-1.1.17
  • mitreid-connect-1.1.18
  • mitreid-connect-1.1.19
  • mitreid-connect-1.1.2
  • mitreid-connect-1.1.3
  • mitreid-connect-1.1.4
  • mitreid-connect-1.1.5
  • mitreid-connect-1.1.6
  • mitreid-connect-1.1.7
  • mitreid-connect-1.1.8
  • mitreid-connect-1.1.9
  • mitreid-connect-1.2.0
  • mitreid-connect-1.2.0-RC1
  • mitreid-connect-1.2.0-RC2
  • mitreid-connect-1.2.1
  • mitreid-connect-1.2.2
  • mitreid-connect-1.2.3
  • mitreid-connect-1.2.4
  • mitreid-connect-1.2.5
  • mitreid-connect-1.2.6
  • mitreid-connect-1.3.0
  • mitreid-connect-1.3.0-RC1
  • mitreid-connect-1.3.0-RC2
  • mitreid-connect-1.3.1
  • mitreid-connect-1.3.2
  • mitreid-connect-1.3.3
  • v10.0.0
  • v10.0.1
  • v10.0.2
  • v10.0.3
  • v10.1.0
  • v10.1.1
  • v10.1.2
  • v10.1.3
  • v10.2.0
  • v10.2.1
  • v10.3.0
  • v10.3.1
  • v10.3.2
  • v10.3.3
  • v10.3.4
  • v10.4.0
  • v10.5.0
  • v10.5.1
  • v10.5.2
  • v10.5.3
  • v10.5.4
  • v11.0.0
  • v11.0.1
  • v11.0.2
  • v11.0.3
  • v12.0.0
  • v12.0.1
  • v12.0.2
  • v12.0.3
  • v12.0.4
  • v12.0.5
  • v12.1.0
  • v12.1.1
  • v12.1.2
  • v12.1.3
  • v12.1.4
  • v12.1.5
  • v12.1.6
106 results
Show changes
Commits on Source (3)
Showing
with 145 additions and 10 deletions
# [17.2.0](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/compare/v17.1.5...v17.2.0) (2024-02-28)
### Features
* 🎸 Dynreg - delete client ([73c7076](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/commit/73c70764701589155cd68c80d2b07300e7d9f0d7))
## [17.1.5](https://gitlab.ics.muni.cz/perun/perun-proxyidp/v1/OpenID-Connect-Java-Spring-Server/compare/v17.1.4...v17.1.5) (2024-02-28)
......
......@@ -21,7 +21,7 @@
<parent>
<groupId>cz.muni.ics</groupId>
<artifactId>perun-oidc-parent</artifactId>
<version>17.1.5</version>
<version>17.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -22,7 +22,7 @@
<parent>
<groupId>cz.muni.ics</groupId>
<artifactId>perun-oidc-parent</artifactId>
<version>17.1.5</version>
<version>17.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -44,6 +44,7 @@ import lombok.ToString;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.util.StringUtils;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
......@@ -71,6 +72,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static cz.muni.ics.oauth2.model.ClientDetailsEntity.DELETE_BY_CLIENT_ID;
import static cz.muni.ics.oauth2.model.ClientDetailsEntity.PARAM_CLIENT_ID;
import static cz.muni.ics.oauth2.model.ClientDetailsEntity.PARAM_PARENT_CLIENT_ID;
import static cz.muni.ics.oauth2.model.ClientDetailsEntity.PARAM_RESOURCE_ID;
......@@ -107,6 +109,11 @@ import static cz.muni.ics.oauth2.model.ClientDetailsEntity.QUERY_BY_CLIENT_ID;
name = QUERY_ALL_BY_PARENT_CLIENT_ID,
query = "SELECT c FROM ClientDetailsEntity c " +
"WHERE c.parentClientId = :" + PARAM_PARENT_CLIENT_ID),
@NamedQuery(
name = DELETE_BY_CLIENT_ID,
query = "DELETE FROM ClientDetailsEntity c " +
"WHERE c.clientId = :" + PARAM_CLIENT_ID
)
})
public class ClientDetailsEntity implements ClientDetails {
......@@ -114,6 +121,7 @@ public class ClientDetailsEntity implements ClientDetails {
public static final String QUERY_ALL = "ClientDetailsEntity.findAll";
public static final String QUERY_RESOURCE_IDS_BY_CLIENT_ID = "ClientDetailsEntity.getResourceIdsForClientID";
public static final String QUERY_ALL_BY_PARENT_CLIENT_ID = "ClientDetailsEntity.getByParentClientId";
public static final String DELETE_BY_CLIENT_ID = "ClientDetailsEntity.deleteByClientId";
public static final String PARAM_CLIENT_ID = "clientId";
public static final String PARAM_RESOURCE_ID = "resourceId";
......@@ -451,4 +459,13 @@ public class ClientDetailsEntity implements ClientDetails {
this.deviceCodeValiditySeconds = deviceCodeValiditySeconds;
}
public void makeClientDynamicallyRegistered(Long parentClientId) {
if (parentClientId == null) {
throw new IllegalArgumentException("When making client dynamically registered, parentClientId must be provided");
}
this.parentClientId = parentClientId;
this.dynamicallyRegistered = true;
}
}
......@@ -37,4 +37,6 @@ public interface OAuth2ClientRepository {
Collection<String> getResourceIdsForClientID(String resourceId);
void deleteClientByClientId(String cid);
}
......@@ -20,11 +20,13 @@ package cz.muni.ics.oauth2.repository.impl;
import cz.muni.ics.oauth2.model.ClientDetailsEntity;
import cz.muni.ics.oauth2.repository.OAuth2ClientRepository;
import cz.muni.ics.util.jpa.JpaUtil;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.Collection;
......@@ -105,6 +107,13 @@ public class JpaOAuth2ClientRepository implements OAuth2ClientRepository {
return query.getResultList();
}
@Override
public void deleteClientByClientId(String clientId) {
Query query = manager.createNamedQuery(ClientDetailsEntity.DELETE_BY_CLIENT_ID);
query.setParameter(ClientDetailsEntity.PARAM_CLIENT_ID, clientId);
query.executeUpdate();
}
private Collection<ClientDetailsEntity> getClientsByParentClientId(Long parentId) {
TypedQuery<ClientDetailsEntity> query = manager.createNamedQuery(
ClientDetailsEntity.QUERY_ALL_BY_PARENT_CLIENT_ID, ClientDetailsEntity.class
......
......@@ -48,4 +48,6 @@ public interface ClientDetailsEntityService extends ClientDetailsService {
boolean checkResourceIdsAreAllowedForClient(String clientId, Set<String> resourceIds);
Set<String> getMismatchedResourceIds(String clientId, Set<String> resourceIds);
void removeClient(ClientDetailsEntity client);
}
......@@ -7,4 +7,6 @@ public interface DynamicClientRegistrationService {
ClientDetailsEntity saveClient(String tokenClientId, DynamicallyRegisteredRequestBody requestedRegistration);
void removeClient(String tokenClientId, String dynregClientId);
}
......@@ -56,10 +56,12 @@ import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
@Slf4j
......@@ -460,6 +462,34 @@ public class DefaultOAuth2ClientDetailsEntityService implements ClientDetailsEnt
return copy;
}
@Override
public void removeClient(ClientDetailsEntity dynregClient) {
String clientId = dynregClient.getClientId();
Set<String> clientIdsToRemove = new HashSet<>();
clientIdsToRemove.add(clientId);
boolean found = true;
Collection<ClientDetailsEntity> clients = clientRepository.getAllClients();
while (found) {
Set<String> childClients = clients.stream()
.filter(client -> clientIdsToRemove.contains(client.getParentClientId()))
.map(ClientDetailsEntity::getClientId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
for (String cid: clientIdsToRemove) {
clientRepository.deleteClientByClientId(cid);
}
clientIdsToRemove.clear();
clientIdsToRemove.addAll(childClients);
if (childClients.isEmpty()) {
found = false;
}
}
}
/**
* Utility class to load a sector identifier's set of authorized redirect URIs.
*
......
......@@ -70,6 +70,28 @@ public class DynamicClientRegistrationServiceImpl implements DynamicClientRegist
return clientDetailsEntityService.saveNewClient(client);
}
@Override
public void removeClient(String tokenClientId, String dynregClientId) {
ClientDetailsEntity tokenClient = clientDetailsEntityService.loadClientByClientId(tokenClientId);
if (tokenClient == null) {
throw new InvalidClientException("Client for ID " + tokenClientId + " not found");
}
ClientDetailsEntity dynregClient = clientDetailsEntityService.loadClientByClientId(dynregClientId);
if (dynregClient == null) {
throw new InvalidClientException("Client for ID " + dynregClientId + " not found");
} else if (!dynregClient.isDynamicallyRegistered()) {
throw new InvalidClientException("Client "
+ dynregClientId + " has not been registered dynamically, thus cannot be removed");
}
if (!tokenClient.equals(dynregClient)) {
throw new InvalidClientException("Client "
+ dynregClientId + " has not been registered by the caller client, thus cannot be removed");
}
clientDetailsEntityService.removeClient(dynregClient);
}
private void validateWithParentClient(ClientDetailsEntity tokenClient, DynamicallyRegisteredRequestBody requestedRegistration) {
Set<String> requestedScope = requestedRegistration.getScope();
Set<String> authScope = tokenClient.getScope();
......
......@@ -10,10 +10,14 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
......@@ -28,6 +32,18 @@ public class DynamicRegistrationEndpoint {
public static final String URL = "register";
public static final String PARAM_CLIENT_ID = "dynregClientId";
public static final String PATH_PARAM_CLIENT_ID = '{' + PARAM_CLIENT_ID + '}';
public static final String GRANT_TYPE = "grant_type";
public static final String CLIENT_ID = "client_id";
public static final String CLIENT_CREDENTIALS = "client_credentials";
public static final String SCOPE_DYNREG = "client_dynamic_registration";
private final DynamicClientRegistrationService dynamicClientRegistrationService;
@Autowired
......@@ -47,15 +63,10 @@ public class DynamicRegistrationEndpoint {
) {
OAuth2Authentication authentication = (OAuth2Authentication) auth;
Set<String> scope = authentication.getOAuth2Request().getScope();
if (scope == null || !scope.contains("client_dynamic_registration")) {
if (scope == null || !scope.contains(SCOPE_DYNREG)) {
throw new InvalidRequestException("The provided token does not contain the required scope");
}
Map<String, String> tokenRequestParameters = ((OAuth2Authentication) auth).getOAuth2Request().getRequestParameters();
if (tokenRequestParameters == null) {
throw new InvalidRequestException("Could not extract token request parameters from the used token");
} else if (!"client_credentials".equals(tokenRequestParameters.getOrDefault("grant_type", ""))) {
throw new InvalidRequestException("Token has to be issued using client_credentials grant type");
}
validateTokenRequestParams(authentication);
String tokenClientId = authentication.getOAuth2Request().getClientId();
ClientDetailsEntity registeredClient = dynamicClientRegistrationService.saveClient(tokenClientId, client);
......@@ -73,4 +84,37 @@ public class DynamicRegistrationEndpoint {
return DynamicRegistrationEndpointView.VIEWNAME;
}
private void validateTokenRequestParams(OAuth2Authentication auth) {
Map<String, String> tokenRequestParameters = auth.getOAuth2Request().getRequestParameters();
if (tokenRequestParameters == null) {
throw new InvalidRequestException("Could not extract token request parameters from the used token");
} else if (!CLIENT_CREDENTIALS.equals(tokenRequestParameters.getOrDefault(GRANT_TYPE, ""))) {
throw new InvalidRequestException("Token has to be issued using client_credentials grant type");
} else if (!StringUtils.hasText(tokenRequestParameters.get(CLIENT_ID))) {
throw new InvalidRequestException("Token has no associated client identifier");
}
}
@DeleteMapping(
value = "/" + URL + "/" + PATH_PARAM_CLIENT_ID
)
public ResponseEntity<Void> deregisterClient(
@PathVariable(PARAM_CLIENT_ID) String dynregClientId,
Authentication auth
) {
OAuth2Authentication authentication = (OAuth2Authentication) auth;
Set<String> scope = authentication.getOAuth2Request().getScope();
if (scope == null || !scope.contains(SCOPE_DYNREG)) {
throw new InvalidRequestException("The provided token does not contain the required scope");
} else if (!StringUtils.hasText(dynregClientId)) {
throw new InvalidRequestException("Path parameter identifying the client to be deleted not specified");
}
validateTokenRequestParams(authentication);
String tokenClientId = authentication.getOAuth2Request().getClientId();
dynamicClientRegistrationService.removeClient(tokenClientId, dynregClientId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
\ No newline at end of file
......@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>cz.muni.ics</groupId>
<artifactId>perun-oidc-parent</artifactId>
<version>17.1.5</version>
<version>17.2.0</version>
<packaging>pom</packaging>
<modules>
......