Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync Cryptomator Hub CLI client user to Hub #239

Merged
merged 4 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,24 @@ List<User> users(RealmResource realm) {
users.addAll(currentRequestedUsers);
} while (currentRequestedUsers.size() == MAX_COUNT_PER_REQUEST);

var cliUser = cryptomatorCliUser(realm);
cliUser.ifPresent(users::add);

return users;
}

//visible for testing
Optional<User> cryptomatorCliUser(RealmResource realm) {
var clients = realm.clients().findByClientId("cryptomatorhub-cli");
if (clients.isEmpty()) {
return Optional.empty();
}
var clientId = clients.get(0).getId();
var client = realm.clients().get(clientId);
var clientUser = client.getServiceAccountUser();
return Optional.of(mapToUser(clientUser));
}

private Predicate<UserRepresentation> notSyncerUser() {
return user -> !user.getUsername().equals(syncerConfig.getUsername());
}
Expand Down
28 changes: 27 additions & 1 deletion backend/src/main/resources/dev-realm.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"composites": {
"client": {
"realm-management": [
"view-users"
"view-users",
"view-clients"
]
}
}
Expand Down Expand Up @@ -79,6 +80,21 @@
"realmRoles": [
"syncer"
]
},
{
"username": "cli",
"email": "cli@localhost",
"enabled": true,
"serviceAccountClientId": "cryptomatorhub-cli",
"attributes": {
"picture": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJ5ZXMiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICAgIDxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9ImJsYWNrIi8+CiAgICA8cGF0aCBzdHJva2Utd2lkdGg9IjUiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTMwIDM3LjUgbDE1IDEyLjUgbC0xNSAxMi41ICBtMjAgMCBoMjAiIC8+Cjwvc3ZnPgo="
},
"realmRoles": [
"user"
],
"clientRoles" : {
"realm-management" : [ "manage-users", "view-users" ]
}
}
],
"scopeMappings": [
Expand Down Expand Up @@ -156,6 +172,16 @@
"attributes": {
"pkce.code.challenge.method": "S256"
}
},
{
"clientId": "cryptomatorhub-cli",
"name": "Cryptomator Hub CLI",
"clientAuthenticatorType": "client-secret",
"secret": "top-secret",
"standardFlowEnabled": false,
"serviceAccountsEnabled": true,
"publicClient": false,
"enabled": true
}
],
"browserSecurityHeaders": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.GroupResource;
import org.keycloak.admin.client.resource.GroupsResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.mockito.Mockito;
Expand All @@ -27,12 +30,25 @@ class KeycloakRemoteUserProviderTest {
private KeycloakRemoteUserProvider keycloakRemoteUserProvider;
private UserRepresentation user1 = Mockito.mock(UserRepresentation.class);
private UserRepresentation user2 = Mockito.mock(UserRepresentation.class);

private UserRepresentation syncer = Mockito.mock(UserRepresentation.class);

private UserRepresentation hubCliUser = Mockito.mock(UserRepresentation.class);

private ClientsResource hubCliClientsResource = Mockito.mock(ClientsResource.class);

private ClientRepresentation hubCliClientRepresentation = Mockito.mock(ClientRepresentation.class);

private ClientResource hubCliClientResource = Mockito.mock(ClientResource.class);


@BeforeEach
void setUp() {
var synerConfig = Mockito.mock(SyncerConfig.class);

Mockito.when(realm.clients()).thenReturn(hubCliClientsResource);
Mockito.when(realm.clients().findByClientId("cryptomatorhub-cli")).thenReturn(List.of());

Mockito.when(realm.users()).thenReturn(usersResource);

Mockito.when(user1.getId()).thenReturn("id3000");
Expand All @@ -54,7 +70,7 @@ void setUp() {

@Test
@DisplayName("test user listing excludes syncer and returns two users")
public void testListUser() {
void testListUser() {
Mockito.when(usersResource.list(0, KeycloakRemoteUserProvider.MAX_COUNT_PER_REQUEST)).thenReturn(List.of(user1, user2, syncer));

var result = keycloakRemoteUserProvider.users(realm);
Expand All @@ -75,9 +91,51 @@ public void testListUser() {
Assertions.assertNull(resultUser2.pictureUrl);
}

@Test
@DisplayName("test user listing excludes syncer, includes Hub CLI user and returns two users")
void testListUserIncludingHubCliUser() {
Mockito.when(usersResource.list(0, KeycloakRemoteUserProvider.MAX_COUNT_PER_REQUEST)).thenReturn(List.of(user1, user2, syncer));

Mockito.when(realm.clients()).thenReturn(hubCliClientsResource);

List<ClientRepresentation> clientRepresentations = Mockito.mock(List.class);
Mockito.when(realm.clients().findByClientId("cryptomatorhub-cli")).thenReturn(clientRepresentations);
Mockito.when(clientRepresentations.get(0)).thenReturn(hubCliClientRepresentation);

Mockito.when(hubCliClientRepresentation.getId()).thenReturn("cryptomatorHubCliClientId");

Mockito.when(realm.clients().get(Mockito.anyString())).thenReturn(hubCliClientResource);

Mockito.when(hubCliUser.getId()).thenReturn("cryptomatorHubCliUserId");
Mockito.when(hubCliUser.getUsername()).thenReturn("cryptomatorHubCliUserUsername");
Mockito.when(hubCliClientResource.getServiceAccountUser()).thenReturn(hubCliUser);

var result = keycloakRemoteUserProvider.users(realm);

Assertions.assertEquals(3, result.size());

var resultUser1 = result.get(0);
var resultUser2 = result.get(1);
var resultUser3 = result.get(2);

Assertions.assertEquals("id3000", resultUser1.id);
Assertions.assertEquals("username3000", resultUser1.name);
Assertions.assertEquals("email3000", resultUser1.email);
Assertions.assertEquals("picture3000", resultUser1.pictureUrl);

Assertions.assertEquals("id3001", resultUser2.id);
Assertions.assertEquals("username3001", resultUser2.name);
Assertions.assertEquals("email3001", resultUser2.email);
Assertions.assertNull(resultUser2.pictureUrl);

Assertions.assertEquals("cryptomatorHubCliUserId", resultUser3.id);
Assertions.assertEquals("cryptomatorHubCliUserUsername", resultUser3.name);
}


@Nested
@DisplayName("Test groups")
public class Groups {
class Groups {

private GroupsResource groupsResource = Mockito.mock(GroupsResource.class);
private GroupRepresentation group1 = Mockito.mock(GroupRepresentation.class);
Expand Down
Loading