Commit 108ec5b7 authored by Matija Obreza's avatar Matija Obreza

Merge branch 'ui-182-admin-oauth-client-management' into 'master'

Added api v1 endpoints for managing OAuth clients

See merge request genesys-pgr/genesys-server!331
parents 7c000335 4af0800a
/*
* Copyright 2019 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.api.v1;
import io.swagger.annotations.Api;
import org.genesys.blocks.oauth.model.AccessToken;
import org.genesys.blocks.oauth.model.OAuthClient;
import org.genesys.blocks.oauth.model.RefreshToken;
import org.genesys.blocks.oauth.service.OAuthClientDetailsService;
import org.genesys.blocks.oauth.service.OAuthTokenStoreService;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys2.server.api.ApiBaseController;
import org.genesys2.server.api.FilteredPage;
import org.genesys2.server.api.Pagination;
import org.genesys2.server.mvc.BaseController;
import org.genesys2.server.service.filter.ArticleFilter;
import org.genesys2.server.service.filter.OAuthClientFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
/**
* Allow administrators to manage OAuth clients and keys.
*/
@RestController("oauthManagementApi1")
@PreAuthorize("hasRole('ADMINISTRATOR')")
@RequestMapping(OAuthManagementController.CONTROLLER_PATH)
@Api(tags = { "oauthManagement" })
public class OAuthManagementController extends BaseController {
final static String CONTROLLER_PATH = ApiBaseController.APIv1_BASE + "/admin/oauth-clients";
/**
* The short filter service.
*/
@Autowired
protected ShortFilterService shortFilterService;
@Autowired
private OAuthClientDetailsService clientDetailsService;
@Autowired
private OAuthTokenStoreService tokenStoreService;
@GetMapping("/")
public FilteredPage<OAuthClient> listClients(@RequestParam(name = "f", required = false) String filterCode, final Pagination page,
@RequestBody(required = false) OAuthClientFilter filter) throws IOException {
if (filterCode != null) {
filter = shortFilterService.filterByCode(filterCode, OAuthClientFilter.class);
} else {
filterCode = shortFilterService.getCode(filter);
}
ShortFilterService.FilterInfo<OAuthClientFilter> filterInfo = shortFilterService.processFilter(filterCode, filter, OAuthClientFilter.class);
return new FilteredPage<>(filterInfo.filterCode, filterInfo.filter, clientDetailsService.listClientDetails(page.toPageRequest(100, Sort.Direction.ASC, "clientId")));
}
@DeleteMapping("/{clientId}/access-tokens")
public boolean removeAllAccessTokens(@PathVariable("clientId") String clientId) {
final Collection<AccessToken> tokens = tokenStoreService.findAccessTokensByClientId(clientId);
for (final AccessToken token : tokens) {
tokenStoreService.removeAccessToken(token.getTokenId());
}
return true;
}
@DeleteMapping("/{clientId}/refresh-tokens")
public boolean removeAllRefreshTokens(@PathVariable("clientId") String clientId) {
final Collection<RefreshToken> tokens = tokenStoreService.findRefreshTokensByClientId(clientId);
for (final RefreshToken token : tokens) {
tokenStoreService.removeRefreshToken(token.getTokenId());
}
return true;
}
@DeleteMapping("/{clientId}/refresh-tokens/{tokenId}")
public boolean removeRefreshToken(@PathVariable("tokenId") String tokenId, @PathVariable("clientId") String clientId) {
tokenStoreService.removeRefreshToken(tokenId);
return true;
}
@DeleteMapping("/{clientId}/access-tokens/{tokenId}")
public boolean removeAccessToken(@PathVariable("tokenId") String tokenId, @PathVariable("clientId") String clientId) {
tokenStoreService.removeAccessToken(tokenId);
return true;
}
@GetMapping("/user/{uuid}/tokens")
@PreAuthorize("hasRole('ADMINISTRATOR') || principal.uuid == #uuid")
public List<AccessToken> getIssuedTokens(@PathVariable("uuid") String uuid) {
return tokenStoreService.findTokensByUserUuid(uuid);
}
@DeleteMapping("/user/{uuid}/{tokenId}/remove")
@PreAuthorize("hasRole('ADMINISTRATOR') || principal.uuid == #uuid")
public boolean removeUsersAccessToken(@PathVariable("tokenId") String tokenId, @PathVariable("uuid") String uuid) {
tokenStoreService.removeAccessToken(tokenId);
return true;
}
@PostMapping(value = "/save-client")
@PreAuthorize("hasAnyRole('VETTEDUSER','ADMINISTRATOR')")
public @ResponseBody OAuthClient createClientEntry(@RequestBody OAuthClient client) {
OAuthClient oauthClient = clientDetailsService.addClient(client);
LOG.info("Created OAuth client with clientId={}", oauthClient.getClientId());
return oauthClient;
}
@PostMapping(value = "/{clientId}/generate-secret")
@PreAuthorize("hasAnyRole('VETTEDUSER','ADMINISTRATOR')")
public String generateSecret(@PathVariable("clientId") String clientId) {
OAuthClient oauthClient = clientDetailsService.getClient(clientId);
return clientDetailsService.resetSecret(oauthClient);
}
@PostMapping(value = "/{clientId}/remove-secret")
@PreAuthorize("hasAnyRole('VETTEDUSER','ADMINISTRATOR')")
public ClientDetails removeSecret(@PathVariable("clientId") String clientId) {
OAuthClient oauthClient = clientDetailsService.getClient(clientId);
clientDetailsService.removeSecret(oauthClient);
return clientDetailsInfo(clientId);
}
@DeleteMapping(value = "/{clientId}")
@PreAuthorize("hasAnyRole('VETTEDUSER','ADMINISTRATOR')")
public OAuthClient deleteClient(@PathVariable("clientId") String clientId) {
OAuthClient clientDetails = clientDetailsService.getClient(clientId);
LOG.info("Deleting client {}", clientDetails.getClientId());
return clientDetailsService.removeClient(clientDetails);
}
@PostMapping(value = "/save-client", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody OAuthClient saveOAuthClient(@RequestBody OAuthClient updates) {
if (updates.getClientId() == null) {
return clientDetailsService.addClient(updates);
}
return clientDetailsService.updateClient(updates.getId(), updates.getVersion(), updates);
}
@GetMapping(value = "/{clientId}/details")
public ClientDetails clientDetailsInfo(@PathVariable("clientId") String clientId) {
final OAuthClient clientDetails = clientDetailsService.getClient(clientId);
final Collection<AccessToken> tokensByClientId = tokenStoreService.findAccessTokensByClientId(clientId);
final Collection<RefreshToken> refreshTokensByClientId = tokenStoreService.findRefreshTokensByClientId(clientId);
return ClientDetails.from(clientDetails, tokensByClientId, refreshTokensByClientId);
}
static class ClientDetails {
public OAuthClient clientDetails;
public Collection<AccessToken> accessTokens;
public Collection<RefreshToken> refreshTokens;
public static ClientDetails from(OAuthClient clientDetails, Collection<AccessToken> accessTokens, Collection<RefreshToken> refreshTokens) {
ClientDetails details = new ClientDetails();
details.accessTokens = accessTokens;
details.clientDetails = clientDetails;
details.refreshTokens = refreshTokens;
return details;
}
}
}
/*
* Copyright 2018 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.service.filter;
import com.hazelcast.util.CollectionUtil;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import org.genesys.blocks.model.filters.AuditedVersionedModelFilter;
import org.genesys.blocks.model.filters.BasicModelFilter;
import org.genesys.blocks.model.filters.StringFilter;
import org.genesys.blocks.oauth.model.OAuthClient;
import org.genesys.blocks.oauth.model.OAuthRole;
import org.genesys.blocks.oauth.model.QOAuthClient;
import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.impl.QUser;
import org.genesys2.server.model.impl.User;
import java.util.Date;
import java.util.Set;
/**
* Filters for {@link User}.
*
* @author Matija Obreza
*/
public class OAuthClientFilter extends AuditedVersionedModelFilter<OAuthClientFilter, OAuthClient> {
/** The clientId. */
public Set<String> clientId;
/** The title. */
public StringFilter title;
/** The resource ids. */
public StringFilter resource;
/** The redirect uris. */
public StringFilter redirect;
/** The grant types. */
public StringFilter grants;
/** The oAuth roles. */
public Set<OAuthRole> roles;
/** The auto approve. */
public Boolean autoApprove;
/* (non-Javadoc)
* @see org.genesys.blocks.model.filters.BasicModelFilter#buildQuery()
*/
@Override
public Predicate buildQuery() {
return buildQuery(QOAuthClient.oAuthClient);
}
/**
* Builds the query.
*
* @param oAuthClient the OAuthClient
* @return the predicate
*/
public Predicate buildQuery(QOAuthClient oAuthClient) {
final BooleanBuilder and = new BooleanBuilder();
super.buildQuery(oAuthClient, oAuthClient._super._super._super, and);
if (CollectionUtil.isNotEmpty(clientId)) {
and.and(oAuthClient.clientId.in(clientId));
}
if (title != null) {
and.and(title.buildQuery(oAuthClient.title));
}
if (CollectionUtil.isNotEmpty(roles)) {
and.and(oAuthClient.roles.any().in(roles));
}
if (grants != null) {
and.and(grants.buildQuery(oAuthClient.grants));
}
if (resource != null) {
and.and(resource.buildQuery(oAuthClient.resource));
}
if (redirect != null) {
and.and(redirect.buildQuery(oAuthClient.redirect));
}
if (autoApprove != null) {
and.and(oAuthClient.autoApprove.eq(autoApprove.booleanValue()));
}
return and;
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment