feat(console): add and remove org domains, show userLoginNames (#235)

* org domains, user login names

* switch user to userview, regen proto
This commit is contained in:
Max Peintner
2020-06-17 17:11:36 +02:00
committed by GitHub
parent e7b139ba2c
commit b5376b2e5a
18 changed files with 613 additions and 339 deletions
@@ -3,6 +3,38 @@
<h1>{{org?.name}}</h1>
<p class="sub">{{'ORG_DETAIL.DESCRIPTION' | translate}}
</p>
<app-card title="{{ 'ORG.DOMAINS.TITLE' | translate }}"
description="{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}">
<div *ngFor="let domain of domains" class="domain">
<span class="title">{{domain.domain}}</span>
<i matTooltip="verified" *ngIf="domain.verified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.primary" class="primary las la-chess-queen"></i>
<span class="fill-space"></span>
<button disabled mat-icon-button
matTooltip="download /.well-known/caos-developer-domain-association.txt and deploy it on your domain. Then verify"><i
class="las la-file-download"></i></button>
<button matTooltip="Remove domain" color="warn" mat-icon-button (click)="removeDomain(domain.domain)"><i
class="las la-trash"></i></button>
</div>
<p class="new-desc">{{'ORG.PAGES.ORGDOMAIN_VERIFICATION' | translate}}</p>
<div class="new-row">
<mat-form-field appearance="outline">
<mat-label>new domain</mat-label>
<input matInput [(ngModel)]="newDomain" />
</mat-form-field>
<button matTooltip="Add domain" mat-icon-button color="accent" (click)="saveNewOrgDomain()">
<mat-icon>check</mat-icon>
</button>
<button disabled mat-icon-button
matTooltip="download /.well-known/caos-developer-domain-association.txt and deploy it on your domain. Then verify"><i
class="las la-file-download"></i></button>
<button disabled mat-icon-button matTooltip="Verify"><i class=" las la-check-circle"></i></button>
</div>
</app-card>
<ng-template appHasRole [appHasRole]="['policy.read']">
<app-policy-grid></app-policy-grid>
</ng-template>
@@ -12,7 +44,8 @@
<div class="details">
<div class="row">
<span class="first">Domains:</span>
<span *ngFor="let domain of domains" class="second">{{domain.domain}}</span>
<span class="second"><span style="display: block;"
*ngFor="let domain of domains">{{domain.domain}}</span></span>
</div>
<div class="row">
<span class="first">State:</span>
@@ -55,6 +55,43 @@ h1 {
}
}
.domain {
display: flex;
align-items: center;
padding: .5rem 0;
flex-wrap: wrap;
.title {
font-size: 16px;
margin-right: 1rem;
}
.verified, .primary{
color: #5282c1;
margin-right: 1rem;
}
.fill-space {
flex: 1;
}
}
.new-desc {
font-size: 14px;
color: #818a8a;
}
.new-row {
display: flex;
flex-wrap: wrap;
align-items: center;
mat-form-field {
flex: 1;
}
}
.side {
.details {
margin-bottom: 1rem;
@@ -96,6 +133,7 @@ h1 {
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
a {
@@ -30,6 +30,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
private subscription: Subscription = new Subscription();
public domains: OrgDomainView.AsObject[] = [];
public newDomain: string = '';
constructor(
public translate: TranslateService,
@@ -76,4 +77,19 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
});
}
}
public saveNewOrgDomain(): void {
this.orgService.AddMyOrgDomain(this.newDomain).then(domain => {
this.domains.push(domain.toObject());
});
}
public removeDomain(domain: string): void {
console.log(domain);
this.orgService.RemoveMyOrgDomain(domain).then(() => {
this.toast.showInfo('Removed');
}).catch(error => {
this.toast.showError(error.message);
});
}
}
@@ -6,8 +6,7 @@ import { MatTable } from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { tap } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { User } from 'src/app/proto/generated/auth_pb';
import { Org, ProjectMember, ProjectType } from 'src/app/proto/generated/management_pb';
import { Org, ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
import { OrgService } from 'src/app/services/org.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -3,7 +3,6 @@ import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { BehaviorSubject, from, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { User } from 'src/app/proto/generated/auth_pb';
import {
ProjectGrantView,
ProjectMemberSearchResponse,
@@ -11,6 +10,7 @@ import {
ProjectState,
ProjectType,
ProjectView,
User,
} from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -3,7 +3,7 @@ import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/fo
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { Gender, User, UserAddress, UserEmail, UserPhone, UserProfile } from 'src/app/proto/generated/auth_pb';
import { Gender, UserAddress, UserEmail, UserPhone, UserProfile } from 'src/app/proto/generated/auth_pb';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/management_pb';
import { AuthUserService } from 'src/app/services/auth-user.service';
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
@@ -33,8 +33,6 @@ function passwordConfirmValidator(c: AbstractControl): any {
styleUrls: ['./auth-user-detail.component.scss'],
})
export class AuthUserDetailComponent implements OnDestroy {
public user!: User.AsObject;
public profile!: UserProfile.AsObject;
public email: UserEmail.AsObject = { email: '' } as any;
public phone: UserPhone.AsObject = { phone: '' } as any;
@@ -267,12 +265,6 @@ export class AuthUserDetailComponent implements OnDestroy {
}
private async getData(): Promise<void> {
// this.mgmtUserService.GetUserByID(id).then(user => {
// console.log(user.toObject());
// this.user = user.toObject();
// }).catch(err => {
// console.error(err);
// });
this.profile = (await this.userService.GetMyUserProfile()).toObject();
this.email = (await this.userService.GetMyUserEmail()).toObject();
this.phone = (await this.userService.GetMyUserPhone()).toObject();
@@ -188,6 +188,14 @@
</div>
<metainfo *ngIf="user" class="side">
<div class="details">
<div class="row" *ngIf="user?.loginNamesList">
<span class="first">Login Names:</span>
<span class="second"><span style="display: block;"
*ngFor="let login of user?.loginNamesList">{{login}}</span></span>
</div>
</div>
<p class="side-section">{{ 'CHANGES.USER.TITLE' | translate }}</p>
<app-changes [changeType]="ChangeType.USER" [id]="user.id"></app-changes>
</metainfo>
@@ -126,6 +126,63 @@
// display: flex;
// flex-direction: column;
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
.row {
display: flex;
margin-bottom: 0.5rem;
align-items: center;
button {
display: none;
visibility: hidden;
}
&:hover {
button {
display: inline-block;
visibility: visible;
mat-icon {
font-size: 1.2rem;
}
}
}
.first {
flex: 1;
font-size: 0.8rem;
margin-right: 0.5rem;
white-space: nowrap;
}
.fill-space {
flex: 1;
}
.second {
font-size: 0.8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
a {
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
.side-section {
color: #81868a;
}
}
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
@@ -10,12 +10,12 @@ import {
Gender,
NotificationType,
PasswordComplexityPolicy,
User,
UserAddress,
UserEmail,
UserPhone,
UserProfile,
UserState,
UserView,
} from 'src/app/proto/generated/management_pb';
import { AuthUserService } from 'src/app/services/auth-user.service';
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
@@ -45,7 +45,7 @@ function passwordConfirmValidator(c: AbstractControl): any {
styleUrls: ['./user-detail.component.scss'],
})
export class UserDetailComponent implements OnInit, OnDestroy {
public user!: User.AsObject;
public user!: UserView.AsObject;
// public email: UserEmail.AsObject = { email: '' } as any;
// public phone: UserPhone.AsObject = { phone: '' } as any;
public address: UserAddress.AsObject = { id: '' } as any;
+13
View File
@@ -30,6 +30,7 @@ import {
UserProfile,
UserProfileView,
UserSessionViews,
UserView,
VerifyMfaOtp,
VerifyMyUserEmailRequest,
VerifyUserPhoneRequest} from './auth_pb';
@@ -67,6 +68,13 @@ export class AuthServiceClient {
response: UserSessionViews) => void
): grpcWeb.ClientReadableStream<UserSessionViews>;
getMyUser(
request: google_protobuf_empty_pb.Empty,
metadata: grpcWeb.Metadata | undefined,
callback: (err: grpcWeb.Error,
response: UserView) => void
): grpcWeb.ClientReadableStream<UserView>;
getMyUserProfile(
request: google_protobuf_empty_pb.Empty,
metadata: grpcWeb.Metadata | undefined,
@@ -234,6 +242,11 @@ export class AuthServicePromiseClient {
metadata?: grpcWeb.Metadata
): Promise<UserSessionViews>;
getMyUser(
request: google_protobuf_empty_pb.Empty,
metadata?: grpcWeb.Metadata
): Promise<UserView>;
getMyUserProfile(
request: google_protobuf_empty_pb.Empty,
metadata?: grpcWeb.Metadata
@@ -404,6 +404,86 @@ proto.caos.zitadel.auth.api.v1.AuthServicePromiseClient.prototype.getMyUserSessi
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.auth.api.v1.UserView>}
*/
const methodDescriptor_AuthService_GetMyUser = new grpc.web.MethodDescriptor(
'/caos.zitadel.auth.api.v1.AuthService/GetMyUser',
grpc.web.MethodType.UNARY,
google_protobuf_empty_pb.Empty,
proto.caos.zitadel.auth.api.v1.UserView,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.auth.api.v1.UserView.deserializeBinary
);
/**
* @const
* @type {!grpc.web.AbstractClientBase.MethodInfo<
* !proto.google.protobuf.Empty,
* !proto.caos.zitadel.auth.api.v1.UserView>}
*/
const methodInfo_AuthService_GetMyUser = new grpc.web.AbstractClientBase.MethodInfo(
proto.caos.zitadel.auth.api.v1.UserView,
/**
* @param {!proto.google.protobuf.Empty} request
* @return {!Uint8Array}
*/
function(request) {
return request.serializeBinary();
},
proto.caos.zitadel.auth.api.v1.UserView.deserializeBinary
);
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @param {function(?grpc.web.Error, ?proto.caos.zitadel.auth.api.v1.UserView)}
* callback The callback function(error, response)
* @return {!grpc.web.ClientReadableStream<!proto.caos.zitadel.auth.api.v1.UserView>|undefined}
* The XHR Node Readable Stream
*/
proto.caos.zitadel.auth.api.v1.AuthServiceClient.prototype.getMyUser =
function(request, metadata, callback) {
return this.client_.rpcCall(this.hostname_ +
'/caos.zitadel.auth.api.v1.AuthService/GetMyUser',
request,
metadata || {},
methodDescriptor_AuthService_GetMyUser,
callback);
};
/**
* @param {!proto.google.protobuf.Empty} request The
* request proto
* @param {?Object<string, string>} metadata User defined
* call metadata
* @return {!Promise<!proto.caos.zitadel.auth.api.v1.UserView>}
* A native promise that resolves to the response
*/
proto.caos.zitadel.auth.api.v1.AuthServicePromiseClient.prototype.getMyUser =
function(request, metadata) {
return this.client_.unaryCall(this.hostname_ +
'/caos.zitadel.auth.api.v1.AuthService/GetMyUser',
request,
metadata || {},
methodDescriptor_AuthService_GetMyUser);
};
/**
* @const
* @type {!grpc.web.MethodDescriptor<
+24 -22
View File
@@ -47,6 +47,12 @@ export class UserSessionView extends jspb.Message {
getSequence(): number;
setSequence(value: number): void;
getLoginName(): string;
setLoginName(value: string): void;
getDisplayName(): string;
setDisplayName(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UserSessionView.AsObject;
static toObject(includeInstance: boolean, msg: UserSessionView): UserSessionView.AsObject;
@@ -63,10 +69,12 @@ export namespace UserSessionView {
userId: string,
userName: string,
sequence: number,
loginName: string,
displayName: string,
}
}
export class User extends jspb.Message {
export class UserView extends jspb.Message {
getId(): string;
setId(value: string): void;
@@ -78,11 +86,6 @@ export class User extends jspb.Message {
hasCreationDate(): boolean;
clearCreationDate(): void;
getActivationDate(): google_protobuf_timestamp_pb.Timestamp | undefined;
setActivationDate(value?: google_protobuf_timestamp_pb.Timestamp): void;
hasActivationDate(): boolean;
clearActivationDate(): void;
getChangeDate(): google_protobuf_timestamp_pb.Timestamp | undefined;
setChangeDate(value?: google_protobuf_timestamp_pb.Timestamp): void;
hasChangeDate(): boolean;
@@ -107,12 +110,12 @@ export class User extends jspb.Message {
getLastName(): string;
setLastName(value: string): void;
getNickName(): string;
setNickName(value: string): void;
getDisplayName(): string;
setDisplayName(value: string): void;
getNickName(): string;
setNickName(value: string): void;
getPreferredLanguage(): string;
setPreferredLanguage(value: string): void;
@@ -146,12 +149,12 @@ export class User extends jspb.Message {
getStreetAddress(): string;
setStreetAddress(value: string): void;
getPasswordChangeRequired(): boolean;
setPasswordChangeRequired(value: boolean): void;
getSequence(): number;
setSequence(value: number): void;
getResourceOwner(): string;
setResourceOwner(value: string): void;
getLoginNamesList(): Array<string>;
setLoginNamesList(value: Array<string>): void;
clearLoginNamesList(): void;
@@ -161,27 +164,26 @@ export class User extends jspb.Message {
setPreferredLoginName(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): User.AsObject;
static toObject(includeInstance: boolean, msg: User): User.AsObject;
static serializeBinaryToWriter(message: User, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): User;
static deserializeBinaryFromReader(message: User, reader: jspb.BinaryReader): User;
toObject(includeInstance?: boolean): UserView.AsObject;
static toObject(includeInstance: boolean, msg: UserView): UserView.AsObject;
static serializeBinaryToWriter(message: UserView, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UserView;
static deserializeBinaryFromReader(message: UserView, reader: jspb.BinaryReader): UserView;
}
export namespace User {
export namespace UserView {
export type AsObject = {
id: string,
state: UserState,
creationDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
activationDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
changeDate?: google_protobuf_timestamp_pb.Timestamp.AsObject,
lastLogin?: google_protobuf_timestamp_pb.Timestamp.AsObject,
passwordChanged?: google_protobuf_timestamp_pb.Timestamp.AsObject,
userName: string,
firstName: string,
lastName: string,
nickName: string,
displayName: string,
nickName: string,
preferredLanguage: string,
gender: Gender,
email: string,
@@ -193,8 +195,8 @@ export namespace User {
postalCode: string,
region: string,
streetAddress: string,
passwordChangeRequired: boolean,
sequence: number,
resourceOwner: string,
loginNamesList: Array<string>,
preferredLoginName: string,
}
@@ -1215,7 +1217,7 @@ export enum OIDCResponseType {
OIDCRESPONSETYPE_ID_TOKEN_TOKEN = 2,
}
export enum UserState {
USERSTATE_UNSPECIEFIED = 0,
USERSTATE_UNSPECIFIED = 0,
USERSTATE_ACTIVE = 1,
USERSTATE_INACTIVE = 2,
USERSTATE_DELETED = 3,
File diff suppressed because it is too large Load Diff
@@ -6054,7 +6054,7 @@ proto.caos.zitadel.management.api.v1.UserView.toObject = function(includeInstanc
sequence: jspb.Message.getFieldWithDefault(msg, 23, 0),
resourceOwner: jspb.Message.getFieldWithDefault(msg, 24, ""),
loginNamesList: jspb.Message.getRepeatedField(msg, 25),
preferredLoginName: jspb.Message.getFieldWithDefault(msg, 27, "")
preferredLoginName: jspb.Message.getFieldWithDefault(msg, 26, "")
};
if (includeInstance) {
@@ -6195,7 +6195,7 @@ proto.caos.zitadel.management.api.v1.UserView.deserializeBinaryFromReader = func
var value = /** @type {string} */ (reader.readString());
msg.addLoginNames(value);
break;
case 27:
case 26:
var value = /** @type {string} */ (reader.readString());
msg.setPreferredLoginName(value);
break;
@@ -6410,7 +6410,7 @@ proto.caos.zitadel.management.api.v1.UserView.serializeBinaryToWriter = function
f = message.getPreferredLoginName();
if (f.length > 0) {
writer.writeString(
27,
26,
f
);
}
@@ -6886,17 +6886,17 @@ proto.caos.zitadel.management.api.v1.UserView.prototype.clearLoginNamesList = fu
/**
* optional string preferred_login_name = 27;
* optional string preferred_login_name = 26;
* @return {string}
*/
proto.caos.zitadel.management.api.v1.UserView.prototype.getPreferredLoginName = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 27, ""));
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 26, ""));
};
/** @param {string} value */
proto.caos.zitadel.management.api.v1.UserView.prototype.setPreferredLoginName = function(value) {
jspb.Message.setProto3StringField(this, 27, value);
jspb.Message.setProto3StringField(this, 26, value);
};
@@ -34,6 +34,7 @@ import {
UserSearchQuery,
UserSearchRequest,
UserSearchResponse,
UserView,
} from '../proto/generated/management_pb';
import { GrpcBackendService } from './grpc-backend.service';
import { GrpcService, RequestFactory, ResponseMapper } from './grpc.service';
@@ -82,7 +83,7 @@ export class MgmtUserService {
);
}
public async GetUserByID(id: string): Promise<User> {
public async GetUserByID(id: string): Promise<UserView> {
const req = new UserID();
req.setId(id);
return await this.request(
+22
View File
@@ -4,6 +4,7 @@ import { Metadata } from 'grpc-web';
import { ManagementServicePromiseClient } from '../proto/generated/management_grpc_web_pb';
import {
AddOrgDomainRequest,
AddOrgMemberRequest,
Iam,
Org,
@@ -29,6 +30,7 @@ import {
PasswordLockoutPolicyUpdate,
ProjectGrant,
ProjectGrantCreate,
RemoveOrgDomainRequest,
RemoveOrgMemberRequest,
} from '../proto/generated/management_pb';
import { GrpcBackendService } from './grpc-backend.service';
@@ -74,6 +76,26 @@ export class OrgService {
);
}
public async AddMyOrgDomain(domain: string): Promise<OrgDomain> {
const req: AddOrgDomainRequest = new AddOrgDomainRequest();
req.setDomain(domain);
return await this.request(
c => c.addMyOrgDomain,
req,
f => f,
);
}
public async RemoveMyOrgDomain(domain: string): Promise<Empty> {
const req: RemoveOrgDomainRequest = new RemoveOrgDomainRequest();
req.setDomain(domain);
return await this.request(
c => c.removeMyOrgDomain,
req,
f => f,
);
}
public async SearchMyOrgDomains(offset: number, limit: number, queryList?: OrgDomainSearchQuery[]):
Promise<OrgDomainSearchResponse> {
const req: OrgDomainSearchRequest = new OrgDomainSearchRequest();