mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-04 19:30:51 +01:00
Create rest endpoint to delete api keys
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -42,7 +42,7 @@ public class NotSupportedExceptionMapper implements ExceptionMapper<NotSupported
|
||||
|
||||
@Override
|
||||
public Response toResponse(NotSupportedException exception) {
|
||||
LOG.debug("illegal media type");
|
||||
LOG.debug("illegal media type", exception);
|
||||
ErrorDto error = new ErrorDto();
|
||||
error.setTransactionId(MDC.get("transaction_id"));
|
||||
error.setMessage("illegal media type");
|
||||
|
||||
@@ -38,6 +38,7 @@ import sonia.scm.web.VndMediaType;
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
@@ -129,6 +130,7 @@ public class ApiKeyResource {
|
||||
@POST
|
||||
@Path("")
|
||||
@Consumes(VndMediaType.API_KEY)
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Create new api key for the current user", description = "Creates a new api key for the given user with the role specified in the given key.", tags = "User")
|
||||
@ApiResponse(
|
||||
responseCode = "201",
|
||||
@@ -158,4 +160,14 @@ public class ApiKeyResource {
|
||||
.location(URI.create(resourceLinks.apiKey().self(newKey.getId())))
|
||||
.build();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Operation(summary = "Delete api key", description = "Deletes the api key with the given id for the current user.", tags = "User")
|
||||
@ApiResponse(responseCode = "204", description = "delete success or nothing to delete")
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "500", description = "internal server error")
|
||||
public void delete(@PathParam("id") String id) {
|
||||
apiKeyService.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,17 +31,21 @@ import sonia.scm.security.ApiKey;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static de.otto.edison.hal.Link.link;
|
||||
|
||||
@Mapper
|
||||
public abstract class ApiKeyToApiKeyDtoMapper {
|
||||
|
||||
@Inject
|
||||
private ResourceLinks links;
|
||||
private ResourceLinks resourceLinks;
|
||||
|
||||
abstract ApiKeyDto map(ApiKey key);
|
||||
|
||||
@ObjectFactory
|
||||
ApiKeyDto createDto(ApiKey key) {
|
||||
Links.linkingTo().self(links.apiKey().self(key.getId()));
|
||||
return new ApiKeyDto(Links.linkingTo().self(links.apiKey().self(key.getId())).build());
|
||||
Links.Builder links = Links.linkingTo()
|
||||
.self(resourceLinks.apiKey().self(key.getId()))
|
||||
.single(link("delete", resourceLinks.apiKey().delete(key.getId())));
|
||||
return new ApiKeyDto(links.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public class MeResource {
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@Path("apiKeys")
|
||||
@Path("api-keys")
|
||||
public ApiKeyResource apiKeys() {
|
||||
return apiKeyResource.get();
|
||||
}
|
||||
|
||||
@@ -238,6 +238,10 @@ class ResourceLinks {
|
||||
String self(String id) {
|
||||
return apiKeyLinkBuilder.method("apiKeys").parameters().method("get").parameters(id).href();
|
||||
}
|
||||
|
||||
String delete(String id) {
|
||||
return apiKeyLinkBuilder.method("apiKeys").parameters().method("delete").parameters(id).href();
|
||||
}
|
||||
}
|
||||
|
||||
UserCollectionLinks userCollection() {
|
||||
|
||||
@@ -41,6 +41,7 @@ import java.util.Optional;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
@@ -140,7 +141,12 @@ public class ApiKeyService {
|
||||
}
|
||||
|
||||
public Collection<ApiKey> getKeys() {
|
||||
return store.get(currentUser()).getKeys().stream().map(ApiKey::new).collect(toList());
|
||||
return store.getOptional(currentUser())
|
||||
.map(ApiKeyCollection::getKeys)
|
||||
.map(Collection::stream)
|
||||
.orElse(Stream.empty())
|
||||
.map(ApiKey::new)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
private String currentUser() {
|
||||
|
||||
@@ -64,6 +64,7 @@ import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
@@ -139,7 +140,7 @@ public class MeResourceTest {
|
||||
assertThat(response.getContentAsString()).contains("\"name\":\"trillian\"");
|
||||
assertThat(response.getContentAsString()).contains("\"self\":{\"href\":\"/v2/me/\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"delete\":{\"href\":\"/v2/users/trillian\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"apiKeys\":{\"href\":\"/v2/me/apiKeys\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"apiKeys\":{\"href\":\"/v2/me/api-keys\"}");
|
||||
}
|
||||
|
||||
private void applyUserToSubject(User user) {
|
||||
@@ -230,29 +231,30 @@ public class MeResourceTest {
|
||||
public void shouldGetAllApiKeys() throws URISyntaxException, UnsupportedEncodingException {
|
||||
when(apiKeyService.getKeys()).thenReturn(Arrays.asList(new ApiKey("1", "key 1", "READ"), new ApiKey("2", "key 2", "WRITE")));
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2 + "apiKeys");
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2 + "api-keys");
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
assertThat(response.getContentAsString()).contains("\"displayName\":\"key 1\",\"role\":\"READ\"");
|
||||
assertThat(response.getContentAsString()).contains("\"displayName\":\"key 2\",\"role\":\"WRITE\"");
|
||||
assertThat(response.getContentAsString()).contains("\"self\":{\"href\":\"/v2/me/apiKeys\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"create\":{\"href\":\"/v2/me/apiKeys\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"self\":{\"href\":\"/v2/me/api-keys\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"create\":{\"href\":\"/v2/me/api-keys\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGetSingleApiKey() throws URISyntaxException, UnsupportedEncodingException {
|
||||
when(apiKeyService.getKeys()).thenReturn(Arrays.asList(new ApiKey("1", "key 1", "READ"), new ApiKey("2", "key 2", "WRITE")));
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2 + "apiKeys/1");
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2 + "api-keys/1");
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
assertThat(response.getContentAsString()).contains("\"displayName\":\"key 1\"");
|
||||
assertThat(response.getContentAsString()).contains("\"role\":\"READ\"");
|
||||
assertThat(response.getContentAsString()).contains("\"self\":{\"href\":\"/v2/me/apiKeys/1\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"self\":{\"href\":\"/v2/me/api-keys/1\"}");
|
||||
assertThat(response.getContentAsString()).contains("\"delete\":{\"href\":\"/v2/me/api-keys/1\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -260,7 +262,7 @@ public class MeResourceTest {
|
||||
when(apiKeyService.createNewKey("guide", "READ")).thenReturn(new ApiKeyService.CreationResult("abc", "1"));
|
||||
|
||||
final MockHttpRequest request = MockHttpRequest
|
||||
.post("/" + MeResource.ME_PATH_V2 + "apiKeys/")
|
||||
.post("/" + MeResource.ME_PATH_V2 + "api-keys/")
|
||||
.contentType(VndMediaType.API_KEY)
|
||||
.content("{\"displayName\":\"guide\",\"role\":\"READ\"}".getBytes());
|
||||
|
||||
@@ -268,7 +270,16 @@ public class MeResourceTest {
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(201);
|
||||
assertThat(response.getContentAsString()).isEqualTo("abc");
|
||||
assertThat(response.getOutputHeaders().get("Location")).containsExactly(URI.create("/v2/me/apiKeys/1"));
|
||||
assertThat(response.getOutputHeaders().get("Location")).containsExactly(URI.create("/v2/me/api-keys/1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteExistingApiKey() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.delete("/" + MeResource.ME_PATH_V2 + "api-keys/1");
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(204);
|
||||
verify(apiKeyService).remove("1");
|
||||
}
|
||||
|
||||
private User createDummyUser(String name) {
|
||||
|
||||
@@ -108,6 +108,11 @@ class ApiKeyServiceTest {
|
||||
assertThat(role).contains("READ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNewUser() {
|
||||
assertThat(service.getKeys()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotReturnAnythingWithWrongKey() {
|
||||
service.createNewKey("1", "READ");
|
||||
@@ -135,8 +140,8 @@ class ApiKeyServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldRemoveKey() {
|
||||
String firstKey = service.createNewKey("1", "READ").getToken();
|
||||
String secondKey = service.createNewKey("2", "WRITE").getToken();
|
||||
String firstKey = service.createNewKey("first", "READ").getToken();
|
||||
String secondKey = service.createNewKey("second", "WRITE").getToken();
|
||||
|
||||
service.remove("1");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user