Skip to content

Commit

Permalink
Merge pull request #150 from cryptomator/feature/identicon
Browse files Browse the repository at this point in the history
Generate identicons if no profile picture is set
  • Loading branch information
overheadhunter authored Sep 26, 2022
2 parents 0f3582d + e7f7ccc commit 2f98ff3
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 35 deletions.
21 changes: 1 addition & 20 deletions backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,6 @@

abstract sealed class AuthorityDto permits UserDto, GroupDto {

private static final String DEFAULT_USER_IMAGE_URI = """
data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0iYmxhY2siIGNsYXNzPSJ3LT\
UgaC01Ij4gICA8Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSIxMCIgZmlsbD0id2hpdGUiIC8+ICAgPHBhdGggZD0iTTEwIDhhMyAzIDAgMTAwLTYgMyAzIDAgMDAwIDZ6TTMuND\
Y1IDE0LjQ5M2ExLjIzIDEuMjMgMCAwMC40MSAxLjQxMkE5Ljk1NyA5Ljk1NyAwIDAwMTAgMThjMi4zMSAwIDQuNDM4LS43ODQgNi4xMzEtMi4xLjQzLS4zMzMuNjA0LS45MDMuND\
A4LTEuNDFhNy4wMDIgNy4wMDIgMCAwMC0xMy4wNzQuMDAzeiIgLz4gPC9zdmc+
""";
private static final String DEFAULT_GROUP_IMAGE_URI = """
data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0iYmxhY2siIGNsYXNzPSJ3LT\
UgaC01Ij4gICA8Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSIxMCIgZmlsbD0id2hpdGUiIC8+ICAgPHBhdGggZD0iTTEwIDlhMyAzIDAgMTAwLTYgMyAzIDAgMDAwIDZ6TTYgOG\
EyIDIgMCAxMS00IDAgMiAyIDAgMDE0IDB6TTEuNDkgMTUuMzI2YS43OC43OCAwIDAxLS4zNTgtLjQ0MiAzIDMgMCAwMTQuMzA4LTMuNTE2IDYuNDg0IDYuNDg0IDAgMDAtMS45MD\
UgMy45NTljLS4wMjMuMjIyLS4wMTQuNDQyLjAyNS42NTRhNC45NyA0Ljk3IDAgMDEtMi4wNy0uNjU1ek0xNi40NCAxNS45OGE0Ljk3IDQuOTcgMCAwMDIuMDctLjY1NC43OC43OC\
AwIDAwLjM1Ny0uNDQyIDMgMyAwIDAwLTQuMzA4LTMuNTE3IDYuNDg0IDYuNDg0IDAgMDExLjkwNyAzLjk2IDIuMzIgMi4zMiAwIDAxLS4wMjYuNjU0ek0xOCA4YTIgMiAwIDExLT\
QgMCAyIDIgMCAwMTQgMHpNNS4zMDQgMTYuMTlhLjg0NC44NDQgMCAwMS0uMjc3LS43MSA1IDUgMCAwMTkuOTQ3IDAgLjg0My44NDMgMCAwMS0uMjc3LjcxQTYuOTc1IDYuOTc1ID\
AgMDExMCAxOGE2Ljk3NCA2Ljk3NCAwIDAxLTQuNjk2LTEuODF6IiAvPiA8L3N2Zz4=
""";

public enum Type {
USER, GROUP
}
Expand All @@ -42,10 +26,7 @@ protected AuthorityDto(String id, Type type, String name, String pictureUrl) {
this.id = id;
this.type = type;
this.name = name;
this.pictureUrl = Objects.requireNonNullElseGet(pictureUrl, () -> switch (type) {
case USER -> DEFAULT_USER_IMAGE_URI;
case GROUP -> DEFAULT_GROUP_IMAGE_URI;
});
this.pictureUrl = pictureUrl;
}

}
45 changes: 41 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@heroicons/vue": "^2.0.11",
"axios": "^0.27.2",
"file-saver": "^2.0.5",
"jdenticon": "^3.2.0",
"jszip": "^3.9.1",
"keycloak-js": "^18.0.0",
"miscreant": "^0.3.2",
Expand Down
85 changes: 74 additions & 11 deletions frontend/src/common/backend.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import AxiosStatic, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { JdenticonConfig, toSvg } from 'jdenticon';
import { base64 } from 'rfc4648';
import authPromise from './auth';
import { backendBaseURL } from './config';
import { VaultKeys } from './crypto';
Expand Down Expand Up @@ -56,35 +58,94 @@ enum AuthorityType {
}

abstract class AuthorityDto {
constructor(public id: string, public name: string, public type: AuthorityType, public pictureUrl: string) { }

private _pictureUrl?: string;

constructor(public id: string, public name: string, public type: AuthorityType, pictureUrl?: string) {
this._pictureUrl = pictureUrl;
}

public get pictureUrl() {
if (this._pictureUrl) {
return this._pictureUrl;
} else {
switch (this.type) {
case AuthorityType.User:
}
const svg = toSvg(this.id, 100, this.getIdenticonConfig());
const bytes = new TextEncoder().encode(svg);
return `data:image/svg+xml;base64,${base64.stringify(bytes)}`;
}
}

abstract getIdenticonConfig(): JdenticonConfig;

}

export class UserDto extends AuthorityDto {
constructor(public id: string, public name: string, public type: AuthorityType, public pictureUrl: string, public email: string, public devices: DeviceDto[]) {
constructor(public id: string, public name: string, public type: AuthorityType, public email: string, public devices: DeviceDto[], pictureUrl?: string) {
super(id, name, type, pictureUrl);
}

getIdenticonConfig(): JdenticonConfig {
return {
hues: [6, 28, 48, 121, 283],
saturation: {
color: 0.59,
},
lightness: {
color: [0.32, 0.49],
grayscale: [0.32, 0.49]
},
backColor: '#F7F7F7',
padding: 0
};
}

static typeOf(obj: any): obj is UserDto {
const userDto = obj as UserDto;
return typeof userDto.id === 'string'
&& typeof userDto.name === 'string'
&& typeof userDto.pictureUrl === 'string'
&& (typeof userDto.pictureUrl === 'string' || userDto.pictureUrl === null)
&& userDto.type === AuthorityType.User;
}

static copy(obj: UserDto): UserDto {
return new UserDto(obj.id, obj.name, obj.type, obj.email, obj.devices, obj.pictureUrl);
}
}

export class GroupDto extends AuthorityDto {
constructor(public id: string, public name: string, public type: AuthorityType, public pictureUrl: string) {
constructor(public id: string, public name: string, public type: AuthorityType, pictureUrl: string) {
super(id, name, type, pictureUrl);
}

getIdenticonConfig(): JdenticonConfig {
return {
hues: [190],
saturation: {
color: 0.59
},
lightness: {
color: [0.81, 0.97],
grayscale: [0.81, 0.97]
},
backColor: '#005E71',
padding: 0
};
}

static typeOf(obj: any): obj is GroupDto {
const groupDto = obj as GroupDto;
return typeof groupDto.id === 'string'
&& typeof groupDto.name === 'string'
&& typeof groupDto.pictureUrl === 'string'
&& (typeof groupDto.pictureUrl === 'string' || groupDto.pictureUrl === null)
&& groupDto.type === AuthorityType.Group;
}

static copy(obj: GroupDto): GroupDto {
return new GroupDto(obj.id, obj.name, obj.type, obj.pictureUrl);
}
}

export type BillingDto = {
Expand Down Expand Up @@ -128,9 +189,9 @@ class VaultService {
return axiosAuth.get<(UserDto | GroupDto)[]>(`/vaults/${vaultId}/members`, { headers: { 'Cryptomator-Vault-Admin-Authorization': vaultAdminAuthorizationJWT } }).then(response => {
return response.data.map(authority => {
if (UserDto.typeOf(authority)) {
return new UserDto(authority.id, authority.name, authority.type, authority.pictureUrl, authority.email, authority.devices);
return UserDto.copy(authority);
} else if (GroupDto.typeOf(authority)) {
return new GroupDto(authority.id, authority.name, authority.type, authority.pictureUrl);
return GroupDto.copy(authority);
} else {
throw new Error('Provided data is not of type UserDTO or GroupDTO');
}
Expand Down Expand Up @@ -200,11 +261,13 @@ class UserService {
return axiosAuth.put('/users/me');
}
public async me(withDevices: boolean = false, withAccessibleVaults: boolean = false): Promise<UserDto> {
return axiosAuth.get<UserDto>(`/users/me?withDevices=${withDevices}&withAccessibleVaults=${withAccessibleVaults}`).then(response => response.data);
return axiosAuth.get<UserDto>(`/users/me?withDevices=${withDevices}&withAccessibleVaults=${withAccessibleVaults}`).then(response => UserDto.copy(response.data));
}

public async listAll(): Promise<UserDto[]> {
return axiosAuth.get<UserDto[]>('/users/').then(response => response.data);
return axiosAuth.get<UserDto[]>('/users/').then(response => {
return response.data.map(dto => UserDto.copy(dto));
});
}

}
Expand All @@ -214,9 +277,9 @@ class AuthorityService {
return axiosAuth.get<(UserDto | GroupDto)[]>(`/authorities/search?query=${query}`).then(response => {
return response.data.map(authority => {
if (UserDto.typeOf(authority)) {
return new UserDto(authority.id, authority.name, authority.type, authority.pictureUrl, authority.email, authority.devices);
return UserDto.copy(authority);
} else if (GroupDto.typeOf(authority)) {
return new GroupDto(authority.id, authority.name, authority.type, authority.pictureUrl);
return GroupDto.copy(authority);
} else {
throw new Error('Provided data is not of type UserDTO or GroupDTO');
}
Expand Down

0 comments on commit 2f98ff3

Please sign in to comment.