Skip to content

Commit

Permalink
Improve client and api key views
Browse files Browse the repository at this point in the history
  • Loading branch information
Portals committed Jun 9, 2024
1 parent b370192 commit 1ba03b1
Show file tree
Hide file tree
Showing 19 changed files with 198 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import it.chalmers.gamma.app.apikey.ApiKeyFacade;
import it.chalmers.gamma.app.apikey.ApiKeySettingsFacade;
import it.chalmers.gamma.app.supergroup.SuperGroupFacade;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -167,14 +168,24 @@ public ModelAndView createApiKey(
@DeleteMapping("/api-keys/{id}")
public ModelAndView deleteApiKey(
@RequestHeader(value = "HX-Request", required = false) boolean htmxRequest,
@RequestHeader(value = "return-empty-on-delete", required = false)
boolean returnEmptyOnDelete,
HttpServletResponse response,
@PathVariable("id") UUID id) {
try {
this.apiKeyFacade.delete(id);
} catch (ApiKeyFacade.ApiKeyNotFoundException e) {
throw new RuntimeException(e);
}

return new ModelAndView("redirect:/api-keys");
if (returnEmptyOnDelete) {
response.addHeader("HX-Reswap", "delete");
response.addHeader("HX-Retarget", "closest article");
} else {
response.addHeader("HX-Redirect", "/api-keys");
}

return new ModelAndView("common/empty");
}

public static final class ApiKeySettings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import it.chalmers.gamma.app.client.domain.authority.ClientAuthorityRepository;
import it.chalmers.gamma.app.supergroup.SuperGroupFacade;
import it.chalmers.gamma.app.user.UserFacade;
import it.chalmers.gamma.security.authentication.AuthenticationExtractor;
import it.chalmers.gamma.security.authentication.UserAuthentication;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -62,7 +65,7 @@ public ModelAndView getClients(
}

@GetMapping("/clients/{id}")
public ModelAndView getClients(
public ModelAndView getClient(
@RequestHeader(value = "HX-Request", required = false) boolean htmxRequest,
@PathVariable("id") String clientUid) {
if (!isValidUUID(clientUid)) {
Expand Down Expand Up @@ -93,6 +96,17 @@ public ModelAndView getClients(
mv.addObject("clientAuthorities", clientAuthorities);
mv.addObject("userApprovals", userApprovals);

if (client.get().owner() instanceof ClientFacade.ClientDTO.UserOwner userOwner) {
if (AuthenticationExtractor.getAuthentication() instanceof UserAuthentication userPrincipal) {
boolean isOwner = userOwner.user().id().equals(userPrincipal.gammaUser().id().value());
mv.addObject("amIOwner", isOwner);

if (!isOwner) {
mv.addObject("owner", userOwner.user());
}
}
}

return mv;
}

Expand Down Expand Up @@ -237,10 +251,11 @@ public ModelAndView newRestrictionRow(
public ModelAndView createClient(
@RequestHeader(value = "HX-Request", required = true) boolean htmxRequest,
CreateClient form,
BindingResult bindingResult) {
BindingResult bindingResult,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView();

ClientFacade.ClientAndApiKeySecrets secrets =
ClientFacade.CreatedClientDTO result =
this.clientFacade.createOfficialClient(
new ClientFacade.NewClient(
form.redirectUrl,
Expand All @@ -251,11 +266,16 @@ public ModelAndView createClient(
form.emailScope,
new ClientFacade.NewClientRestrictions(form.restrictions)));

mv.setViewName("pages/client-credentials");
mv.addObject("clientUid", secrets.clientUid());
mv.addObject("clientSecret", secrets.clientSecret());
mv.addObject("apiKeyToken", secrets.apiKeyToken());
mv.addObject("name", form.prettyName);
mv.setViewName("pages/client-details");

mv.addObject("clientUid", result.client().clientUid());
mv.addObject("client", result.client());
mv.addObject("clientAuthorities", new ArrayList<>());
mv.addObject("userApprovals", new ArrayList<>());
mv.addObject("clientSecret", result.clientSecret());
mv.addObject("apiKeyToken", result.apiKeyToken());

response.addHeader("HX-Push-Url", "/clients/" + result.client().clientUid().toString());

return mv;
}
Expand Down Expand Up @@ -421,14 +441,19 @@ public ModelAndView createAuthority(
@DeleteMapping("/clients/{id}")
public ModelAndView deleteClient(
@RequestHeader(value = "HX-Request", required = true) boolean htmxRequest,
@RequestHeader(value = "owner", required = false) boolean wasOwner,
@PathVariable("id") UUID clientUid) {
try {
this.clientFacade.delete(clientUid);
} catch (ClientFacade.ClientNotFoundException e) {
throw new RuntimeException(e);
}

return new ModelAndView("redirect:/clients");
if (wasOwner) {
return new ModelAndView("redirect:/my-clients");
} else {
return new ModelAndView("redirect:/clients");
}
}

@DeleteMapping("/clients/{id}/authority/{name}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package it.chalmers.gamma.adapter.primary.web;

import it.chalmers.gamma.app.client.ClientFacade;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -131,10 +133,11 @@ public ModelAndView getCreateUserClient(
public ModelAndView createUserClient(
@RequestHeader(value = "HX-Request", required = true) boolean htmxRequest,
CreateUserClient form,
BindingResult bindingResult) {
BindingResult bindingResult,
HttpServletResponse response) {
ModelAndView mv = new ModelAndView();

ClientFacade.ClientAndApiKeySecrets secrets =
ClientFacade.CreatedClientDTO result =
this.clientFacade.createUserClient(
new ClientFacade.NewClient(
form.redirectUrl,
Expand All @@ -145,11 +148,16 @@ public ModelAndView createUserClient(
form.emailScope,
null));

mv.setViewName("pages/client-credentials");
mv.addObject("clientUid", secrets.clientUid());
mv.addObject("clientSecret", secrets.clientSecret());
mv.addObject("apiKeyToken", secrets.apiKeyToken());
mv.addObject("name", form.prettyName);
mv.setViewName("pages/client-details");

mv.addObject("clientUid", result.client().clientUid());
mv.addObject("client", result.client());
mv.addObject("clientAuthorities", new ArrayList<>());
mv.addObject("userApprovals", new ArrayList<>());
mv.addObject("clientSecret", result.clientSecret());
mv.addObject("apiKeyToken", result.apiKeyToken());

response.addHeader("HX-Push-Url", "/clients/" + result.client().clientUid().toString());

return mv;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import it.chalmers.gamma.app.apikey.domain.ApiKey;
import it.chalmers.gamma.app.apikey.domain.ApiKeyId;
import it.chalmers.gamma.app.apikey.domain.ApiKeyRepository;
import it.chalmers.gamma.app.apikey.domain.ApiKeyToken;
import jakarta.persistence.EntityExistsException;
import jakarta.transaction.Transactional;
import java.util.List;
Expand Down Expand Up @@ -75,14 +74,6 @@ public Optional<ApiKey> getById(ApiKeyId apiKeyId) {
return this.repository.findById(apiKeyId.value()).map(this.apiKeyEntityConverter::toDomain);
}

@Override
public Optional<ApiKey> getByToken(ApiKeyToken apiKeyToken) {
System.out.println(apiKeyToken.value());
return this.repository
.findByToken(apiKeyToken.value())
.map(this.apiKeyEntityConverter::toDomain);
}

private ApiKeyEntity toEntity(ApiKey apiKey) {
ApiKeyEntity apiKeyEntity = new ApiKeyEntity();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import it.chalmers.gamma.adapter.secondary.jpa.user.UserJpaRepository;
import it.chalmers.gamma.adapter.secondary.jpa.util.PersistenceErrorHelper;
import it.chalmers.gamma.adapter.secondary.jpa.util.PersistenceErrorState;
import it.chalmers.gamma.app.apikey.domain.ApiKeyId;
import it.chalmers.gamma.app.apikey.domain.ApiKeyToken;
import it.chalmers.gamma.app.client.domain.Client;
import it.chalmers.gamma.app.client.domain.ClientId;
Expand Down Expand Up @@ -143,4 +144,12 @@ public Optional<Client> getByApiKey(ApiKeyToken apiKeyToken) {
.map(ClientApiKeyEntity::getClient)
.map(this.clientEntityConverter::toDomain);
}

@Override
public Optional<Client> getByApiKey(ApiKeyId apiKeyId) {
return this.clientApiKeyJpaRepository
.findById(apiKeyId.value())
.map(ClientApiKeyEntity::getClient)
.map(this.clientEntityConverter::toDomain);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package it.chalmers.gamma.app.apikey;

import static it.chalmers.gamma.app.authentication.AccessGuard.isAdmin;
import static it.chalmers.gamma.app.authentication.AccessGuard.isLocalRunner;
import static it.chalmers.gamma.app.authentication.AccessGuard.*;

import it.chalmers.gamma.app.Facade;
import it.chalmers.gamma.app.apikey.domain.*;
Expand Down Expand Up @@ -90,9 +89,11 @@ public void delete(UUID apiKeyId) throws ApiKeyNotFoundException {
}

public Optional<ApiKeyDTO> getById(UUID apiKeyId) {
this.accessGuard.require(isAdmin());
ApiKeyId id = new ApiKeyId(apiKeyId);

this.accessGuard.requireEither(isAdmin(), ownerOfClientApi(id));

return this.apiKeyRepository.getById(new ApiKeyId(apiKeyId)).map(ApiKeyDTO::new);
return this.apiKeyRepository.getById(id).map(ApiKeyDTO::new);
}

public List<ApiKeyDTO> getAll() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ public interface ApiKeyRepository {

Optional<ApiKey> getById(ApiKeyId apiKeyId);

Optional<ApiKey> getByToken(ApiKeyToken apiKeyToken);

class ApiKeyNotFoundException extends Exception {}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public record GeneratedApiKeyToken(ApiKeyToken apiKeyToken, String rawToken) {}
public static GeneratedApiKeyToken generate(PasswordEncoder passwordEncoder) {
String value =
TokenUtils.generateToken(
50,
32,
TokenUtils.CharacterTypes.LOWERCASE,
TokenUtils.CharacterTypes.UPPERCASE,
TokenUtils.CharacterTypes.NUMBERS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ public static AccessChecker ownerOfClient(ClientUid clientUid) {
};
}

public static AccessChecker ownerOfClientApi(ApiKeyId apiKeyId) {
return (clientRepository, userRepository) -> {
if (AuthenticationExtractor.getAuthentication() instanceof UserAuthentication userPrincipal) {
Optional<Client> client = clientRepository.getByApiKey(apiKeyId);

if (client.isPresent()) {
ClientOwner clientOwner = client.get().owner();

if (clientOwner instanceof ClientUserOwner(UserId userId)) {
return userId.equals(userPrincipal.gammaUser().id());
}
}
}

return false;
};
}

/** Such as Bootstrap */
public static AccessChecker isLocalRunner() {
return (clientRepository, userRepository) ->
Expand Down
19 changes: 9 additions & 10 deletions app/src/main/java/it/chalmers/gamma/app/client/ClientFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static it.chalmers.gamma.app.authentication.AccessGuard.*;

import it.chalmers.gamma.app.Facade;
import it.chalmers.gamma.app.apikey.ApiKeyFacade;
import it.chalmers.gamma.app.apikey.domain.ApiKey;
import it.chalmers.gamma.app.apikey.domain.ApiKeyId;
import it.chalmers.gamma.app.apikey.domain.ApiKeyToken;
Expand Down Expand Up @@ -51,14 +52,14 @@ public ClientFacade(
}

@Transactional
public ClientAndApiKeySecrets createOfficialClient(NewClient newClient) {
public CreatedClientDTO createOfficialClient(NewClient newClient) {
this.accessGuard.require(isAdmin());

return this.create(newClient, new ClientOwnerOfficial());
}

@Transactional
public ClientAndApiKeySecrets createUserClient(NewClient newClient) {
public CreatedClientDTO createUserClient(NewClient newClient) {
this.accessGuard.require(isSignedIn());

if (newClient.restrictions != null) {
Expand All @@ -73,7 +74,7 @@ public ClientAndApiKeySecrets createUserClient(NewClient newClient) {
throw new IllegalStateException();
}

private ClientAndApiKeySecrets create(NewClient newClient, ClientOwner clientOwner) {
private CreatedClientDTO create(NewClient newClient, ClientOwner clientOwner) {
ClientSecret.GeneratedClientSecret generatedClientSecret =
ClientSecret.generate(passwordEncoder);
ApiKey apiKey = null;
Expand Down Expand Up @@ -131,9 +132,8 @@ private ClientAndApiKeySecrets create(NewClient newClient, ClientOwner clientOwn

this.clientRepository.save(client);

return new ClientAndApiKeySecrets(
clientUid.value(),
clientId.value(),
return new CreatedClientDTO(
createDTO(client),
generatedClientSecret.rawSecret(),
generatedApiKeyToken == null ? null : generatedApiKeyToken.rawToken());
}
Expand Down Expand Up @@ -215,8 +215,7 @@ public record NewClient(
boolean emailScope,
NewClientRestrictions restrictions) {}

public record ClientAndApiKeySecrets(
UUID clientUid, String clientId, String clientSecret, String apiKeyToken) {}
public record CreatedClientDTO(ClientDTO client, String clientSecret, String apiKeyToken) {}

private ClientDTO createDTO(Client client) {
return new ClientDTO(
Expand All @@ -226,7 +225,7 @@ private ClientDTO createDTO(Client client) {
client.prettyName().value(),
client.description().sv().value(),
client.description().en().value(),
client.clientApiKey().isPresent(),
client.clientApiKey().map(ApiKeyFacade.ApiKeyDTO::new),
client
.restrictions()
.map(
Expand Down Expand Up @@ -255,7 +254,7 @@ public record ClientDTO(
String prettyName,
String svDescription,
String enDescription,
boolean hasApiKey,
Optional<ApiKeyFacade.ApiKeyDTO> apiKey,
ClientRestrictionDTO restriction,
Owner owner) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package it.chalmers.gamma.app.client.domain;

import it.chalmers.gamma.app.apikey.domain.ApiKeyId;
import it.chalmers.gamma.app.apikey.domain.ApiKeyToken;
import it.chalmers.gamma.app.user.domain.UserId;
import java.util.List;
Expand Down Expand Up @@ -32,6 +33,8 @@ void save(Client client)

Optional<Client> getByApiKey(ApiKeyToken apiKeyToken);

Optional<Client> getByApiKey(ApiKeyId apiKeyId);

class ClientNotFoundException extends Exception {}

class ClientIdAlreadyExistsRuntimeException extends RuntimeException {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public record GeneratedClientSecret(ClientSecret clientSecret, String rawSecret)
public static GeneratedClientSecret generate(PasswordEncoder passwordEncoder) {
String value =
TokenUtils.generateToken(
128,
32,
TokenUtils.CharacterTypes.LOWERCASE,
TokenUtils.CharacterTypes.UPPERCASE,
TokenUtils.CharacterTypes.NUMBERS);
Expand Down
Loading

0 comments on commit 1ba03b1

Please sign in to comment.