feat: protos refactoring

* start with user

* user first try done in all services

* user, org, idp for discussion

* remove unused stuff

* bla

* dockerbuild

* rename search, get multiple to list...

* add annotation

* update proto dependencies

* update proto dependencies

* change proto imports

* replace all old imports

* fix go out

* remove unused lines

* correct protoc flags

* grpc and openapi flags

* go out source path relative

* -p

* remove dead code

* sourcepath relative

* ls

* is onenapi the problem?

* hobla

* authoption output

* wrong field name

* gopf

* correct option, add correct flags

* small improvments

* SIMPLYFY

* relative path

* gopf bin ich en tubel

* correct path

* default policies in admin

* grpc generation in one file

* remove non ascii

* metadata on manipulations

* correct auth_option import

* fixes

* larry

* idp provider to idp

* fix generate

* admin and auth nearly done

* admin and auth nearly done

* gen

* healthz

* imports

* deleted too much imports

* fix org

* add import

* imports

* import

* naming

* auth_opt

* gopf

* management

* imports

* _TYPE_UNSPECIFIED

* improts

* auth opts

* management policies

* imports

* passwordlessType to MFAType

* auth_opt

* add user grant calls

* add missing messages

* result

* fix option

* improvements

* ids

* fix http

* imports

* fixes

* fields

* body

* add fields

* remove wrong member query

* fix request response

* fixes

* add copy files

* variable versions

* generate all files

* improvements

* add dependencies

* factors

* user session

* oidc information, iam

* remove unused file

* changes

* enums

* dockerfile

* fix build

* remove unused folder

* update readme for build

* move old server impl

* add event type to change

* some changes

* start admin

* remove wrong field

* admin only list calls missing

* fix proto numbers

* surprisingly it compiles

* service ts changes

* admin mgmt

* mgmt

* auth manipulation and gets done, lists missing

* validations and some field changes

* validations

* enum validations

* remove todo

* move proto files to proto/zitadel

* change proto path in dockerfile

* it compiles!

* add validate import

* remove duplicate import

* fix protos

* fix import

* tests

* cleanup

* remove unimplemented methods

* iam member multiple queries

* all auth and admin calls

* add initial password on crate human

* message names

* management user server

* machine done

* fix: todos (#1346)

* fix: pub sub in new eventstore

* fix: todos

* fix: todos

* fix: todos

* fix: todos

* fix: todos

* fix tests

* fix: search method domain

* admin service, user import type typescript

* admin changes

* admin changes

* fix: search method domain

* more user grpc and begin org, fix configs

* fix: return object details

* org grpc

* remove creation date add details

* app

* fix: return object details

* fix: return object details

* mgmt service, project members

* app

* fix: convert policies

* project, members, granted projects, searches

* fix: convert usergrants

* fix: convert usergrants

* auth user detail, user detail, mfa, second factor, auth

* fix: convert usergrants

* mfa, memberships, password, owned proj detail

* fix: convert usergrants

* project grant

* missing details

* changes, userview

* idp table, keys

* org list and user table filter

* unify rest paths (#1381)

* unify rest paths

* post for all searches,
mfa to multi_factor,
secondfactor to second_factor

* remove v1

* fix tests

* rename api client key to app key

* machine keys, age policy

* user list, machine keys, changes

* fix: org states

* add default flag to policy

* second factor to type

* idp id

* app type

* unify ListQuery, ListDetails, ObjectDetails field names

* user grants, apps, memberships

* fix type params

* metadata to detail, linke idps

* api create, membership, app detail, create

* idp, app, policy

* queries, multi -> auth factors and missing fields

* update converters

* provider to user, remove old mgmt refs

* temp remove authfactor dialog, build finish

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Silvan
2021-03-09 10:30:11 +01:00
committed by GitHub
parent 9f417f3957
commit dabd5920dc
372 changed files with 17881 additions and 22036 deletions

7
.gitignore vendored
View File

@@ -46,6 +46,11 @@ zitadelctl
tmp/ tmp/
console/src/app/proto/generated/ console/src/app/proto/generated/
pkg/grpc/*/*.pb.* #generated filed
pkg/grpc/*/*.pb*.*
pkg/grpc/*/*.swagger.json pkg/grpc/*/*.swagger.json
pkg/grpc/*/mock/*.mock.go pkg/grpc/*/mock/*.mock.go
**.pb.go
**.proto.mock.go
**.pb.*.go
**.gen.go

View File

@@ -12,15 +12,15 @@
This command generates the grpc stub for angular into the folder console/src/app/proto/generated for local development This command generates the grpc stub for angular into the folder console/src/app/proto/generated for local development
```Bash ```Bash
DOCKER_BUILDKIT=1 docker build -f build/dockerfile . -t zitadel:local --target npm-copy -o console/src/app/proto/generated DOCKER_BUILDKIT=1 docker build -f build/dockerfile . -t zitadel:local --target npm-copy -o .
``` ```
### Go ### Go
With this command you can generate the stub for golang into the correct dir pkg/ With this command you can generate the stub for golang into the zitadel dir
```Bash ```Bash
DOCKER_BUILDKIT=1 docker build -f build/dockerfile . -t zitadel:local --target go-copy -o pkg DOCKER_BUILDKIT=1 docker build -f build/dockerfile . -t zitadel:local --target go-copy -o .
``` ```
## Run ## Run

View File

@@ -10,33 +10,8 @@ mkdir -p $GEN_PATH
echo "Generate grpc" echo "Generate grpc"
protoc \ protoc \
-I=.tmp/protos/message \ -I=/proto/include \
-I=.tmp/protos/admin/proto \
-I=.tmp/protos/management/proto \
-I=.tmp/protos/auth/proto \
-I=node_modules/google-proto-files \ -I=node_modules/google-proto-files \
-I=.tmp/protos \
--js_out=import_style=commonjs,binary:$GEN_PATH \ --js_out=import_style=commonjs,binary:$GEN_PATH \
--grpc-web_out=import_style=commonjs+dts,mode=grpcweb:$GEN_PATH \ --grpc-web_out=import_style=typescript,mode=grpcweb:$GEN_PATH \
.tmp/protos/message/proto/*.proto \ $(find /proto/include -iname "*.proto")
.tmp/protos/admin/proto/*.proto \
.tmp/protos/auth/proto/*.proto \
.tmp/protos/management/proto/*.proto
echo "Generate annotations js file (compatibility)"
mkdir -p $GEN_PATH/google/api/
touch $GEN_PATH/google/api/annotations_pb.js
echo "export {}" > $GEN_PATH/google/api/annotations_pb.d.ts
mkdir -p $GEN_PATH/validate
touch $GEN_PATH/validate/validate_pb.js
echo "export {}" > $GEN_PATH/validate/validate_pb.d.ts
mkdir -p $GEN_PATH/protoc-gen-swagger/options
touch $GEN_PATH/protoc-gen-swagger/options/annotations_pb.js
echo "export {}" > $GEN_PATH/protoc-gen-swagger/options/annotations_pb.d.ts
mkdir -p $GEN_PATH/authoption
touch $GEN_PATH/authoption/options_pb.js
echo "export {}" > $GEN_PATH/authoption/options_pb.d.ts

View File

@@ -1,105 +1,137 @@
####################### ARG GO_VERSION=1.15.8
## By default we build the prod enviroment ARG NODE_VERSION=15.8.0
ARG ENV=prod ARG ENV=prod
####################### #######################
## This step downloads the protofiles, protoc and protoc-gen-grpc-web for later use ## This step sets up the folder structure,
## initalices go mods,
## downloads the protofiles,
## protoc and protoc-gen-grpc-web for later use
####################### #######################
FROM alpine as base FROM alpine AS base
RUN apk add tar curl ARG PROTOC_VERSION=3.14.0
WORKDIR /.tmp ARG PROTOC_ZIP=protoc-${PROTOC_VERSION}-linux-x86_64.zip
RUN wget -O protoc https://github.com/protocolbuffers/protobuf/releases/download/v3.13.0/protoc-3.13.0-linux-x86_64.zip \ ARG GRPC_WEB_VERSION=1.2.1
&& unzip protoc \ ARG GRPC_WEB=protoc-gen-grpc-web-${GRPC_WEB_VERSION}-linux-x86_64
&& wget -O bin/protoc-gen-grpc-web https://github.com/grpc/grpc-web/releases/download/1.2.0/protoc-gen-grpc-web-1.2.0-linux-x86_64 \
&& chmod +x bin/protoc-gen-grpc-web
RUN curl https://raw.githubusercontent.com/envoyproxy/protoc-gen-validate/v0.4.1/validate/validate.proto --create-dirs -o validate/validate.proto \ RUN apk add tar curl
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v1.14.6/protoc-gen-swagger/options/annotations.proto --create-dirs -o protoc-gen-swagger/options/annotations.proto \ WORKDIR /proto
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v1.14.6/protoc-gen-swagger/options/openapiv2.proto --create-dirs -o protoc-gen-swagger/options/openapiv2.proto \
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto --create-dirs -o google/api/annotations.proto \ #protoc
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto --create-dirs -o google/api/http.proto \ RUN curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/$PROTOC_ZIP \
&& curl https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/empty.proto --create-dirs -o google/protobuf/empty.proto \ && unzip -o $PROTOC_ZIP -d /usr/local bin/protoc \
&& curl https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/timestamp.proto --create-dirs -o google/protobuf/timestamp.proto \ && unzip -o $PROTOC_ZIP -d /proto include/* \
&& curl https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/descriptor.proto --create-dirs -o google/protobuf/descriptor.proto \ && rm -f $PROTOC_ZIP
&& curl https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/duration.proto --create-dirs -o google/protobuf/duration.proto \
&& curl https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/any.proto --create-dirs -o google/protobuf/any.proto \ #grpc web
&& curl https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/struct.proto --create-dirs -o google/protobuf/struct.proto RUN curl -OL https://github.com/grpc/grpc-web/releases/download/${GRPC_WEB_VERSION}/${GRPC_WEB} \
&& mv ${GRPC_WEB} /usr/local/bin/protoc-gen-grpc-web \
&& chmod +x /usr/local/bin/protoc-gen-grpc-web
#proto dependencies
RUN curl https://raw.githubusercontent.com/envoyproxy/protoc-gen-validate/v0.4.1/validate/validate.proto --create-dirs -o include/validate/validate.proto \
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v2.2.0/protoc-gen-openapiv2/options/annotations.proto --create-dirs -o include/protoc-gen-openapiv2/options/annotations.proto \
&& curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/v2.2.0/protoc-gen-openapiv2/options/openapiv2.proto --create-dirs -o include/protoc-gen-openapiv2/options/openapiv2.proto \
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto --create-dirs -o include/google/api/annotations.proto \
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto --create-dirs -o include/google/api/http.proto \
&& curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/field_behavior.proto --create-dirs -o include/google/api/field_behavior.proto
#zitadel protos
COPY /proto/ include/.
COPY pkg/grpc/admin/proto/admin.proto admin/proto/admin.proto
COPY pkg/grpc/auth/proto/auth.proto auth/proto/auth.proto
COPY pkg/grpc/management/proto/management.proto management/proto/management.proto
COPY pkg/grpc/message/proto/message.proto message/proto/message.proto
COPY internal/protoc/protoc-gen-authoption/authoption/options.proto authoption/options.proto
####################### #######################
## With this step we prepare all node_modules, this helps caching the build ## With this step we prepare all node_modules, this helps caching the build
## Speed up this step by mounting your local node_modules directory ## Speed up this step by mounting your local node_modules directory
####################### #######################
FROM node:15 as npm-base FROM node:${NODE_VERSION} as npm-base
WORKDIR console WORKDIR /console
COPY console/package.json console/package-lock.json ./ COPY console/package.json console/package-lock.json ./
RUN npm install \ RUN npm install
&& mkdir .tmp
COPY console . COPY console .
COPY --from=base /.tmp/bin /usr/local/bin/ COPY --from=base /proto /proto
COPY --from=base /.tmp .tmp/protos/ COPY --from=base /usr/local/bin /usr/local/bin/.
COPY build/console build/console/ COPY build/console build/console/
RUN build/console/generate-grpc.sh RUN build/console/generate-grpc.sh
FROM scratch as npm-copy
COPY --from=npm-base /console/src/app/proto/generated .
#######################
## copy for local dev
#######################
FROM scratch as npm-copy
COPY --from=npm-base /console/src/app/proto/generated ./console/src/app/proto/generated
#######################
## anular dev build ## anular dev build
#######################
FROM npm-base as dev-angular-build FROM npm-base as dev-angular-build
RUN npm install -g @angular/cli RUN npm install -g @angular/cli
#######################
## anular prod build ## anular prod build
#######################
FROM npm-base as prod-angular-build FROM npm-base as prod-angular-build
RUN npm run prodbuild RUN npm run prodbuild
####################### #######################
## Go base build ## Go dependencies
## Speed up this step by mounting your local go mod pkg directory ## Speed up this step by mounting your local go mod pkg directory
####################### #######################
FROM golang:1.15 as go-base FROM golang:${GO_VERSION} as go-dep
WORKDIR src/github.com/caos/zitadel/ RUN mkdir -p src/github.com/caos/zitadel
COPY go.mod go.sum ./ COPY . src/github.com/caos/zitadel/
WORKDIR /go/src/github.com/caos/zitadel/
RUN go mod download RUN go mod download
COPY --from=base /.tmp .tmp/protos/ RUN ./tools/install.sh
COPY --from=base /.tmp/bin /usr/local/bin/
COPY internal/protoc/protoc-base internal/protoc/protoc-base/
COPY internal/protoc/protoc-gen-authoption internal/protoc/protoc-gen-authoption/
RUN go install \ #######################
github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \ ## Go base build
github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \ #######################
github.com/golang/protobuf/protoc-gen-go \ FROM go-dep as go-base
github.com/envoyproxy/protoc-gen-validate COPY --from=base /proto /proto
COPY --from=base /usr/local/bin /usr/local/bin/.
RUN go get -u github.com/go-bindata/go-bindata/...
RUN go-bindata ./internal/protoc/protoc-gen-authoption/templates \
&& go install ./internal/protoc/protoc-gen-authoption
COPY build/zitadel build/zitadel/
RUN build/zitadel/generate-grpc.sh RUN build/zitadel/generate-grpc.sh
FROM scratch as go-copy
COPY --from=go-base /go/src/github.com/caos/zitadel/pkg/ .
#######################
## copy for local dev
#######################
FROM scratch as go-copy
COPY --from=go-base /go/src/github.com/caos/zitadel/pkg/grpc ./pkg/grpc
COPY --from=go-base /go/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption/templates.gen.go ./internal/protoc/protoc-gen-authoption/templates.gen.go
COPY --from=go-base /go/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption/authoption/options.pb.go ./internal/protoc/protoc-gen-authoption/authoption/options.pb.go
#######################
## Go test ## Go test
#######################
FROM go-base as go-test FROM go-base as go-test
COPY . . COPY . .
#Migrations for cockroach-secure
# Migrations for cockroach-secure
RUN go install github.com/rakyll/statik RUN go install github.com/rakyll/statik
RUN ./build/operator/prebuild.sh ./migrations RUN ./build/operator/prebuild.sh ./migrations
RUN go test -race -v -coverprofile=profile.cov $(go list ./... | grep -v /operator/) RUN go test -race -v -coverprofile=profile.cov $(go list ./... | grep -v /operator/)
## Go test
#######################
## Go test results
#######################
FROM scratch as go-codecov FROM scratch as go-codecov
COPY --from=go-test /go/src/github.com/caos/zitadel/profile.cov profile.cov COPY --from=go-test /go/src/github.com/caos/zitadel/profile.cov profile.cov
#######################
## Go prod build ## Go prod build
#######################
FROM go-test as prod-go-build FROM go-test as prod-go-build
COPY --from=prod-angular-build console/dist/console console/dist/console/ COPY --from=prod-angular-build console/dist/console console/dist/console/
RUN go get github.com/rakyll/statik \ RUN go get github.com/rakyll/statik \
@@ -109,10 +141,14 @@ RUN go get github.com/rakyll/statik \
&& ./build/zitadel/generate-static.sh && ./build/zitadel/generate-static.sh
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o zitadel-linux-amd64 cmd/zitadel/main.go RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o zitadel-linux-amd64 cmd/zitadel/main.go
#######################
## Go dev build ## Go dev build
#######################
FROM go-base as dev-go-build FROM go-base as dev-go-build
RUN go get github.com/go-delve/delve/cmd/dlv RUN go get github.com/go-delve/delve/cmd/dlv
####################### #######################
## Final Production Image ## Final Production Image
####################### #######################
@@ -123,11 +159,14 @@ COPY --from=prod-go-build /go/src/github.com/caos/zitadel/zitadel-linux-amd64 /a
RUN chmod a+x /app/zitadel RUN chmod a+x /app/zitadel
RUN ls -la / RUN ls -la /
#######################
## Scratch Image ## Scratch Image
#######################
FROM scratch as final FROM scratch as final
COPY --from=artifact /etc/passwd /etc/passwd COPY --from=artifact /etc/passwd /etc/passwd
COPY --from=artifact /etc/ssl/certs /etc/ssl/certs COPY --from=artifact /etc/ssl/certs /etc/ssl/certs
COPY --from=artifact /app / COPY --from=artifact /app /
USER zitadel USER zitadel
HEALTHCHECK NONE HEALTHCHECK NONE
ENTRYPOINT ["/zitadel"] ENTRYPOINT ["/zitadel"]

View File

@@ -4,62 +4,70 @@ set -eux
echo "Generate grpc" echo "Generate grpc"
protoc \ OPENAPI_PATH=${GOPATH}/src/github.com/caos/zitadel/openapi/v2
-I=.tmp/protos/message \ ZITADEL_PATH=${GOPATH}/src/github.com/caos/zitadel
-I=.tmp/protos/admin/proto \ GRPC_PATH=${ZITADEL_PATH}/pkg/grpc
-I=.tmp/protos/management/proto \ PROTO_PATH=/proto/include/zitadel
-I=.tmp/protos/auth/proto \
-I=.tmp/protos \
-I=${GOPATH}/src \
--go_out=plugins=grpc:$GOPATH/src \
.tmp/protos/message/proto/message.proto
protoc \ protoc \
-I=.tmp/protos/message \ -I=/proto/include/ \
-I=.tmp/protos/admin/proto \ --go_out $GOPATH/src \
-I=.tmp/protos/management/proto \ --go-grpc_out $GOPATH/src \
-I=.tmp/protos/auth/proto \ $(find ${PROTO_PATH} -iname *.proto | grep -v "management|admin|auth")
-I=.tmp/protos \
-I=${GOPATH}/src \ go-bindata \
--go_out=plugins=grpc:$GOPATH/src \ -pkg main \
--grpc-gateway_out=logtostderr=true:$GOPATH/src \ -prefix internal/protoc/protoc-gen-authoption \
--swagger_out=logtostderr=true:. \ -o ${ZITADEL_PATH}/internal/protoc/protoc-gen-authoption/templates.gen.go \
--authoption_out=. \ ${ZITADEL_PATH}/internal/protoc/protoc-gen-authoption/templates
go install ${ZITADEL_PATH}/internal/protoc/protoc-gen-authoption
# output folder for openapi v2
mkdir -p ${OPENAPI_PATH}
protoc \
-I=/proto/include \
--go_out ${GOPATH}/src \
--go-grpc_out ${GOPATH}/src \
--grpc-gateway_out ${GOPATH}/src \
--grpc-gateway_opt logtostderr=true \
--openapiv2_out ${OPENAPI_PATH} \
--openapiv2_opt logtostderr=true \
--authoption_out ${GRPC_PATH}/admin \
--validate_out=lang=go:${GOPATH}/src \ --validate_out=lang=go:${GOPATH}/src \
.tmp/protos/admin/proto/admin.proto ${PROTO_PATH}/admin.proto
mv ${ZITADEL_PATH}/pkg/grpc/admin/zitadel/* ${ZITADEL_PATH}/pkg/grpc/admin
mv admin* $GOPATH/src/github.com/caos/zitadel/pkg/grpc/admin/ rm -r ${ZITADEL_PATH}/pkg/grpc/admin/zitadel
protoc \ protoc \
-I=.tmp/protos/message \ -I=/proto/include \
-I=.tmp/protos/admin/proto \ --go_out $GOPATH/src \
-I=.tmp/protos/management/proto \ --go-grpc_out $GOPATH/src \
-I=.tmp/protos/auth/proto \ --grpc-gateway_out ${GOPATH}/src \
-I=.tmp/protos \ --grpc-gateway_opt logtostderr=true \
-I=${GOPATH}/src \ --grpc-gateway_opt allow_delete_body=true \
--go_out=plugins=grpc:$GOPATH/src \ --openapiv2_out ${OPENAPI_PATH} \
--grpc-gateway_out=logtostderr=true,allow_delete_body=true:${GOPATH}/src \ --openapiv2_opt logtostderr=true \
--swagger_out=logtostderr=true,allow_delete_body=true:. \ --openapiv2_opt allow_delete_body=true \
--authoption_out=. \ --authoption_out ${GRPC_PATH}/management \
--validate_out=lang=go:${GOPATH}/src \ --validate_out=lang=go:${GOPATH}/src \
.tmp/protos/management/proto/management.proto ${PROTO_PATH}/management.proto
mv ${ZITADEL_PATH}/pkg/grpc/management/zitadel/* ${ZITADEL_PATH}/pkg/grpc/management
mv management* $GOPATH/src/github.com/caos/zitadel/pkg/grpc/management/ rm -r ${ZITADEL_PATH}/pkg/grpc/management/zitadel
protoc \ protoc \
-I=.tmp/protos/message \ -I=/proto/include \
-I=.tmp/protos/admin/proto \ --go_out $GOPATH/src \
-I=.tmp/protos/management/proto \ --go-grpc_out $GOPATH/src \
-I=.tmp/protos/auth/proto \ --grpc-gateway_out ${GOPATH}/src \
-I=.tmp/protos \ --grpc-gateway_opt logtostderr=true \
-I=${GOPATH}/src \ --openapiv2_out ${OPENAPI_PATH} \
--go_out=plugins=grpc:$GOPATH/src \ --openapiv2_opt logtostderr=true \
--grpc-gateway_out=logtostderr=true:$GOPATH/src \ --authoption_out=${GRPC_PATH}/auth \
--swagger_out=logtostderr=true:. \
--authoption_out=. \
--validate_out=lang=go:${GOPATH}/src \ --validate_out=lang=go:${GOPATH}/src \
.tmp/protos/auth/proto/auth.proto ${PROTO_PATH}/auth.proto
mv ${ZITADEL_PATH}/pkg/grpc/auth/zitadel/* ${ZITADEL_PATH}/pkg/grpc/auth
rm -r ${ZITADEL_PATH}/pkg/grpc/auth/zitadel
mv auth* $GOPATH/src/github.com/caos/zitadel/pkg/grpc/auth/ echo "done generating grpc"
echo "done generating"

View File

@@ -4,19 +4,16 @@ Log:
Format: text Format: text
Eventstore: Eventstore:
ServiceName: 'Admin' Host: $ZITADEL_EVENTSTORE_HOST
Repository: Port: $ZITADEL_EVENTSTORE_PORT
SQL: User: 'eventstore'
Host: $ZITADEL_EVENTSTORE_HOST Database: 'eventstore'
Port: $ZITADEL_EVENTSTORE_PORT Password: $CR_EVENTSTORE_PASSWORD
User: 'eventstore' SSL:
Database: 'eventstore' Mode: $CR_SSL_MODE
Password: $CR_EVENTSTORE_PASSWORD RootCert: $CR_ROOT_CERT
SSL: Cert: $CR_EVENTSTORE_CERT
Mode: $CR_SSL_MODE Key: $CR_EVENTSTORE_KEY
RootCert: $CR_ROOT_CERT
Cert: $CR_EVENTSTORE_CERT
Key: $CR_EVENTSTORE_KEY
SetUp: SetUp:
Step1: Step1:

View File

@@ -36,9 +36,9 @@ Queries:
Eventstore: Eventstore:
User: 'queries' User: 'queries'
Password: $CR_QUERIES_PASSWORD Password: $CR_QUERIES_PASSWORD
SSL: SSL:
Cert: $CR_QUERIES_CERT Cert: $CR_QUERIES_CERT
Key: $CR_QUERIES_KEY Key: $CR_QUERIES_KEY
AuthZ: AuthZ:
Repository: Repository:

View File

@@ -18,8 +18,8 @@
<path d="M16.88 3.549L7.12 20.451"></path> <path d="M16.88 3.549L7.12 20.451"></path>
</svg> </svg>
<button class="org-button" (click)="loadOrgs()" *ngIf="profile?.id && org" mat-button <button class="org-button" (click)="loadOrgs()" *ngIf="user && org" mat-button [matMenuTriggerFor]="menu"
[matMenuTriggerFor]="menu" (menuOpened)="focusFilter()">{{org?.name ? org.name : 'NO NAME'}} (menuOpened)="focusFilter()">{{org?.name ? org.name : 'NO NAME'}}
<mat-icon> <mat-icon>
arrow_drop_down</mat-icon> arrow_drop_down</mat-icon>
</button> </button>
@@ -63,7 +63,7 @@
[name]="user.displayName ? user.displayName : (user.firstName + ' '+ user.lastName)" [size]="38"> [name]="user.displayName ? user.displayName : (user.firstName + ' '+ user.lastName)" [size]="38">
</app-avatar> </app-avatar>
<app-accounts-card @accounts class="a_card mat-elevation-z1" *ngIf="showAccount" <app-accounts-card @accounts class="a_card mat-elevation-z1" *ngIf="showAccount"
(close)="showAccount = false" [profile]="profile" [iamuser]="iamuser$ | async"> (close)="showAccount = false" [user]="user" [iamuser]="iamuser$ | async">
</app-accounts-card> </app-accounts-card>
</div> </div>
</mat-toolbar> </mat-toolbar>

View File

@@ -12,13 +12,9 @@ import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
import { catchError, debounceTime, finalize, map, take } from 'rxjs/operators'; import { catchError, debounceTime, finalize, map, take } from 'rxjs/operators';
import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations'; import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolbarAnimation } from './animations';
import { import { TextQueryMethod } from './proto/generated/zitadel/object_pb';
MyProjectOrgSearchKey, import { Org, OrgNameQuery, OrgQuery } from './proto/generated/zitadel/org_pb';
MyProjectOrgSearchQuery, import { User } from './proto/generated/zitadel/user_pb';
Org,
SearchMethod,
UserProfileView,
} from './proto/generated/auth_pb';
import { AuthenticationService } from './services/authentication.service'; import { AuthenticationService } from './services/authentication.service';
import { GrpcAuthService } from './services/grpc-auth.service'; import { GrpcAuthService } from './services/grpc-auth.service';
import { ManagementService } from './services/mgmt.service'; import { ManagementService } from './services/mgmt.service';
@@ -50,7 +46,7 @@ export class AppComponent implements OnDestroy {
public showAccount: boolean = false; public showAccount: boolean = false;
public org!: Org.AsObject; public org!: Org.AsObject;
public orgs$: Observable<Org.AsObject[]> = of([]); public orgs$: Observable<Org.AsObject[]> = of([]);
public profile!: UserProfileView.AsObject; public user!: User.AsObject;
public isDarkTheme: Observable<boolean> = of(true); public isDarkTheme: Observable<boolean> = of(true);
public orgLoading$: BehaviorSubject<any> = new BehaviorSubject(false); public orgLoading$: BehaviorSubject<any> = new BehaviorSubject(false);
@@ -183,7 +179,7 @@ export class AppComponent implements OnDestroy {
this.authSub = this.authenticationService.authenticationChanged.subscribe((authenticated) => { this.authSub = this.authenticationService.authenticationChanged.subscribe((authenticated) => {
if (authenticated) { if (authenticated) {
this.authService.GetActiveOrg().then(org => { this.authService.getActiveOrg().then(org => {
this.org = org; this.org = org;
}); });
} }
@@ -224,16 +220,17 @@ export class AppComponent implements OnDestroy {
public loadOrgs(filter?: string): void { public loadOrgs(filter?: string): void {
let query; let query;
if (filter) { if (filter) {
query = new MyProjectOrgSearchQuery(); query = new OrgQuery();
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE); const orgNameQuery = new OrgNameQuery();
query.setKey(MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME); orgNameQuery.setName(filter);
query.setValue(filter); orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setNameQuery(orgNameQuery);
} }
this.orgLoading$.next(true); this.orgLoading$.next(true);
this.orgs$ = from(this.authService.SearchMyProjectOrgs(10, 0, query ? [query] : undefined)).pipe( this.orgs$ = from(this.authService.listMyProjectOrgs(10, 0, query ? [query] : undefined)).pipe(
map(resp => { map(resp => {
return resp.toObject().resultList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => { finalize(() => {
@@ -264,12 +261,15 @@ export class AppComponent implements OnDestroy {
this.translate.setDefaultLang('en'); this.translate.setDefaultLang('en');
this.authService.user.subscribe(userprofile => { this.authService.user.subscribe(userprofile => {
this.profile = userprofile; if (userprofile) {
const cropped = navigator.language.split('-')[0] ?? 'en'; this.user = userprofile;
const fallbackLang = cropped.match(/en|de/) ? cropped : 'en'; const cropped = navigator.language.split('-')[0] ?? 'en';
const lang = userprofile.preferredLanguage.match(/en|de/) ? userprofile.preferredLanguage : fallbackLang; const fallbackLang = cropped.match(/en|de/) ? cropped : 'en';
this.translate.use(lang);
this.document.documentElement.lang = lang; const lang = userprofile?.human?.profile?.preferredLanguage.match(/en|de/) ? userprofile.human.profile?.preferredLanguage : fallbackLang;
this.translate.use(lang);
this.document.documentElement.lang = lang;
}
}); });
} }
@@ -284,9 +284,8 @@ export class AppComponent implements OnDestroy {
private getProjectCount(): void { private getProjectCount(): void {
this.authService.isAllowed(['project.read']).subscribe((allowed) => { this.authService.isAllowed(['project.read']).subscribe((allowed) => {
if (allowed) { if (allowed) {
this.mgmtService.SearchProjects(0, 0); this.mgmtService.listProjects(0, 0);
this.mgmtService.listGrantedProjects(0, 0);
this.mgmtService.SearchGrantedProjects(0, 0);
} }
}); });
} }

View File

@@ -17,7 +17,7 @@ export class UserGuard implements CanActivate {
state: RouterStateSnapshot, state: RouterStateSnapshot,
): Observable<boolean> | Promise<boolean> | boolean { ): Observable<boolean> | Promise<boolean> | boolean {
return this.authService.user.pipe( return this.authService.user.pipe(
map(user => user.id !== route.params.id), map(user => user?.id !== route.params.id),
tap((isNotMe) => { tap((isNotMe) => {
if (!isNotMe) { if (!isNotMe) {
this.router.navigate(['/users', 'me']); this.router.navigate(['/users', 'me']);

View File

@@ -1,24 +1,27 @@
<div class="card" appOutsideClick (clickOutside)="closeCard($event)"> <div class="card" appOutsideClick (clickOutside)="closeCard($event)">
<app-avatar *ngIf="profile && (profile.displayName || (profile.firstName && profile.lastName))" class="avatar" <app-avatar
[name]="profile.displayName ? profile.displayName : (profile.firstName + ' '+ profile.lastName)" [size]="80"> *ngIf="user.human?.profile && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
class="avatar"
[name]="user.human?.profile?.displayName ? user.human?.profile?.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
[size]="80">
</app-avatar> </app-avatar>
<span class="u-name">{{profile.displayName ? profile.displayName : profile.preferredLoginName}}</span> <span class="u-name">{{user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A'}}</span>
<span class="u-email">{{profile?.preferredLoginName}}</span> <span class="u-email">{{user.human?.profile?.preferredLoginName}}</span>
<span class="iamuser" *ngIf="iamuser">IAM USER</span> <span class="iamuser" *ngIf="iamuser">IAM USER</span>
<button color="primary" (click)="editUserProfile()" mat-stroked-button>{{'USER.EDITACCOUNT' | translate}}</button> <button color="primary" (click)="editUserProfile()" mat-stroked-button>{{'USER.EDITACCOUNT' | translate}}</button>
<div class="l-accounts"> <div class="l-accounts">
<mat-progress-bar *ngIf="loadingUsers" color="primary" mode="indeterminate"></mat-progress-bar> <mat-progress-bar *ngIf="loadingUsers" color="primary" mode="indeterminate"></mat-progress-bar>
<a class="row" *ngFor="let user of users" (click)="selectAccount(user.loginName)"> <a class="row" *ngFor="let session of sessions" (click)="selectAccount(session.loginName)">
<app-avatar *ngIf="user && user.displayName" class="small-avatar" <app-avatar *ngIf="session && session.displayName" class="small-avatar"
[name]="user.displayName ? user.displayName : ''" [size]="32"> [name]="session.displayName ? session.displayName : ''" [size]="32">
</app-avatar> </app-avatar>
<div class="col"> <div class="col">
<span class="user-title">{{user.displayName ? user.displayName : user.userName}} </span> <span class="user-title">{{session.displayName ? session.displayName : session.userName}} </span>
<span class="loginname">{{user.loginName}}</span> <span class="loginname">{{session.loginName}}</span>
<span class="email">{{'USER.STATE.'+user.authState | translate}}</span> <span class="email">{{'USER.STATE.'+session.authState | translate}}</span>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<mat-icon>keyboard_arrow_right</mat-icon> <mat-icon>keyboard_arrow_right</mat-icon>

View File

@@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { AuthConfig } from 'angular-oauth2-oidc'; import { AuthConfig } from 'angular-oauth2-oidc';
import { UserProfileView, UserSessionView } from 'src/app/proto/generated/auth_pb'; import { Session, User } from 'src/app/proto/generated/zitadel/user_pb';
import { AuthenticationService } from 'src/app/services/authentication.service'; import { AuthenticationService } from 'src/app/services/authentication.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
@@ -11,18 +11,18 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
styleUrls: ['./accounts-card.component.scss'], styleUrls: ['./accounts-card.component.scss'],
}) })
export class AccountsCardComponent implements OnInit { export class AccountsCardComponent implements OnInit {
@Input() public profile!: UserProfileView.AsObject; @Input() public user!: User.AsObject;
@Input() public iamuser: boolean = false; @Input() public iamuser: boolean = false;
@Output() public close: EventEmitter<void> = new EventEmitter(); @Output() public close: EventEmitter<void> = new EventEmitter();
public users: UserSessionView.AsObject[] = []; public sessions: Session.AsObject[] = [];
public loadingUsers: boolean = false; public loadingUsers: boolean = false;
constructor(public authService: AuthenticationService, private router: Router, private userService: GrpcAuthService) { constructor(public authService: AuthenticationService, private router: Router, private userService: GrpcAuthService) {
this.userService.getMyUserSessions().then(sessions => { this.userService.listMyUserSessions().then(sessions => {
this.users = sessions.toObject().userSessionsList; this.sessions = sessions.resultList;
const index = this.users.findIndex(user => user.loginName === this.profile.preferredLoginName); const index = this.sessions.findIndex(user => user.loginName === this.user.preferredLoginName);
if (index > -1) { if (index > -1) {
this.users.splice(index, 1); this.sessions.splice(index, 1);
} }
this.loadingUsers = false; this.loadingUsers = false;

View File

@@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AuthNKeyType, MachineKeyType } from 'src/app/proto/generated/management_pb'; import { KeyType } from 'src/app/proto/generated/zitadel/auth_n_key_pb';
export enum AddKeyDialogType { export enum AddKeyDialogType {
MACHINE = "MACHINE", MACHINE = "MACHINE",
@@ -15,21 +15,16 @@ export enum AddKeyDialogType {
}) })
export class AddKeyDialogComponent { export class AddKeyDialogComponent {
public startDate: Date = new Date(); public startDate: Date = new Date();
types: MachineKeyType[] | AuthNKeyType[] = []; types: KeyType[] = [];
public type!: MachineKeyType | AuthNKeyType; public type!: KeyType;
public dateControl: FormControl = new FormControl('', []); public dateControl: FormControl = new FormControl('', []);
constructor( constructor(
public dialogRef: MatDialogRef<AddKeyDialogComponent>, public dialogRef: MatDialogRef<AddKeyDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: any,
) { ) {
if (data.type = AddKeyDialogType.MACHINE) { this.types = [KeyType.KEY_TYPE_JSON];
this.types = [MachineKeyType.MACHINEKEY_JSON]; this.type = KeyType.KEY_TYPE_JSON;
this.type = MachineKeyType.MACHINEKEY_JSON;
} else if (data.type = AddKeyDialogType.AUTHNKEY) {
this.types = [AuthNKeyType.AUTHNKEY_JSON];
this.type = AuthNKeyType.AUTHNKEY_JSON;
}
const today = new Date(); const today = new Date();
this.startDate.setDate(today.getDate() + 1); this.startDate.setDate(today.getDate() + 1);
} }

View File

@@ -1,7 +1,8 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ProjectGrantView, ProjectRole, ProjectView, UserView } from 'src/app/proto/generated/management_pb'; import { GrantedProject, Project, Role } from 'src/app/proto/generated/zitadel/project_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -23,7 +24,7 @@ export enum CreationType {
export class MemberCreateDialogComponent { export class MemberCreateDialogComponent {
private projectId: string = ''; private projectId: string = '';
private grantId: string = ''; private grantId: string = '';
public preselectedUsers: Array<UserView.AsObject> = []; public preselectedUsers: Array<User.AsObject> = [];
public creationType!: CreationType; public creationType!: CreationType;
@@ -38,8 +39,8 @@ export class MemberCreateDialogComponent {
{ type: CreationType.PROJECT_OWNED, disabled$: this.authService.isAllowed(['project.member.write']) }, { type: CreationType.PROJECT_OWNED, disabled$: this.authService.isAllowed(['project.member.write']) },
{ type: CreationType.PROJECT_GRANTED, disabled$: this.authService.isAllowed(['project.grant.member.write']) }, { type: CreationType.PROJECT_GRANTED, disabled$: this.authService.isAllowed(['project.grant.member.write']) },
]; ];
public users: Array<UserView.AsObject> = []; public users: Array<User.AsObject> = [];
public roles: Array<ProjectRole.AsObject> | string[] = []; public roles: Array<Role.AsObject> | string[] = [];
public CreationType: any = CreationType; public CreationType: any = CreationType;
public ProjectAutocompleteType: any = ProjectAutocompleteType; public ProjectAutocompleteType: any = ProjectAutocompleteType;
public memberRoleOptions: string[] = []; public memberRoleOptions: string[] = [];
@@ -72,22 +73,22 @@ export class MemberCreateDialogComponent {
public loadRoles(): void { public loadRoles(): void {
switch (this.creationType) { switch (this.creationType) {
case CreationType.PROJECT_GRANTED: case CreationType.PROJECT_GRANTED:
this.mgmtService.GetProjectGrantMemberRoles().then(resp => { this.mgmtService.listProjectGrantMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.resultList;
}).catch(error => { }).catch(error => {
this.toastService.showError(error); this.toastService.showError(error);
}); });
break; break;
case CreationType.PROJECT_OWNED: case CreationType.PROJECT_OWNED:
this.mgmtService.GetProjectMemberRoles().then(resp => { this.mgmtService.listProjectMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.resultList;
}).catch(error => { }).catch(error => {
this.toastService.showError(error); this.toastService.showError(error);
}); });
break; break;
case CreationType.IAM: case CreationType.IAM:
this.adminService.GetIamMemberRoles().then(resp => { this.adminService.listIAMMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.rolesList;
}).catch(error => { }).catch(error => {
this.toastService.showError(error); this.toastService.showError(error);
}); });
@@ -95,7 +96,7 @@ export class MemberCreateDialogComponent {
} }
} }
public selectProject(project: ProjectView.AsObject | ProjectGrantView.AsObject | any): void { public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
this.projectId = project.projectId; this.projectId = project.projectId;
if (project.id) { if (project.id) {
this.grantId = project.id; this.grantId = project.id;

View File

@@ -1,5 +1,5 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { OIDCApplicationType } from 'src/app/proto/generated/management_pb'; import { OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
@Component({ @Component({
selector: 'cnsl-app-card', selector: 'cnsl-app-card',
@@ -8,7 +8,7 @@ import { OIDCApplicationType } from 'src/app/proto/generated/management_pb';
}) })
export class AppCardComponent { export class AppCardComponent {
@Input() public outline: boolean = false; @Input() public outline: boolean = false;
@Input() public type!: OIDCApplicationType; @Input() public type!: OIDCAppType;
@Input() public isApiApp: boolean = false; @Input() public isApiApp: boolean = false;
public OIDCApplicationType: any = OIDCApplicationType; public OIDCApplicationType: any = OIDCAppType;
} }

View File

@@ -1,5 +1,10 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { APIAuthMethodType, OIDCAuthMethodType, OIDCGrantType, OIDCResponseType } from 'src/app/proto/generated/management_pb'; import {
APIAuthMethodType,
OIDCAuthMethodType,
OIDCGrantType,
OIDCResponseType,
} from 'src/app/proto/generated/zitadel/app_pb';
export interface RadioItemAuthType { export interface RadioItemAuthType {
key: string; key: string;

View File

@@ -11,8 +11,9 @@
hist.values[0]?.dates[0]| timestampToDate | localizedDate: 'dd. MMMM YYYY' }}</span> hist.values[0]?.dates[0]| timestampToDate | localizedDate: 'dd. MMMM YYYY' }}</span>
<div class="item" *ngFor="let dayelement of hist.values; index as i"> <div class="item" *ngFor="let dayelement of hist.values; index as i">
<div class="row"> <div class="row">
<app-avatar matTooltip="{{ dayelement.editorName }}" *ngIf="dayelement.editorName; else spacer" <app-avatar matTooltip="{{ dayelement.editorDisplayName }}"
class="avatar" [name]="dayelement.editorName" [size]="32"> *ngIf="dayelement.editorDisplayName; else spacer" class="avatar"
[name]="dayelement.editorDisplayName" [size]="32">
</app-avatar> </app-avatar>
<ng-template #spacer> <ng-template #spacer>
<div class="spacer"></div> <div class="spacer"></div>

View File

@@ -1,11 +1,18 @@
import { KeyValue } from '@angular/common';
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of, Subject } from 'rxjs'; import { BehaviorSubject, from, Observable, of, Subject } from 'rxjs';
import { catchError, debounceTime, scan, take, takeUntil, tap } from 'rxjs/operators'; import { catchError, debounceTime, scan, take, takeUntil, tap } from 'rxjs/operators';
import { Change, Changes } from 'src/app/proto/generated/management_pb'; import { ListMyUserChangesResponse } from 'src/app/proto/generated/zitadel/auth_pb';
import { Change } from 'src/app/proto/generated/zitadel/change_pb';
import {
ListAppChangesResponse,
ListOrgChangesResponse,
ListProjectChangesResponse,
ListUserChangesResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { KeyValue } from '@angular/common';
export enum ChangeType { export enum ChangeType {
MYUSER = 'myuser', MYUSER = 'myuser',
@@ -27,6 +34,8 @@ export interface MappedChange {
}>; }>;
} }
type ListChanges = ListMyUserChangesResponse.AsObject | ListUserChangesResponse.AsObject | ListProjectChangesResponse.AsObject | ListOrgChangesResponse.AsObject | ListAppChangesResponse.AsObject;
@Component({ @Component({
selector: 'app-changes', selector: 'app-changes',
templateUrl: './changes.component.html', templateUrl: './changes.component.html',
@@ -46,7 +55,7 @@ export class ChangesComponent implements OnInit, OnDestroy {
loading: Observable<boolean> = this._loading.asObservable(); loading: Observable<boolean> = this._loading.asObservable();
public data!: Observable<MappedChange[]>; public data!: Observable<MappedChange[]>;
public changes!: Changes.AsObject; public changes!: ListChanges;
private destroyed$: Subject<void> = new Subject(); private destroyed$: Subject<void> = new Subject();
constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) { constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) {
@@ -73,17 +82,17 @@ export class ChangesComponent implements OnInit, OnDestroy {
} }
public init(): void { public init(): void {
let first: Promise<Changes>; let first: Promise<ListChanges>;
switch (this.changeType) { switch (this.changeType) {
case ChangeType.MYUSER: first = this.authUserService.GetMyUserChanges(20, 0); case ChangeType.MYUSER: first = this.authUserService.listMyUserChanges(20, 0);
break; break;
case ChangeType.USER: first = this.mgmtUserService.UserChanges(this.id, 20, 0); case ChangeType.USER: first = this.mgmtUserService.listUserChanges(this.id, 20, 0);
break; break;
case ChangeType.PROJECT: first = this.mgmtUserService.ProjectChanges(this.id, 20, 0); case ChangeType.PROJECT: first = this.mgmtUserService.listProjectChanges(this.id, 20, 0);
break; break;
case ChangeType.ORG: first = this.mgmtUserService.OrgChanges(this.id, 20, 0); case ChangeType.ORG: first = this.mgmtUserService.listOrgChanges(20, 0);
break; break;
case ChangeType.APP: first = this.mgmtUserService.ApplicationChanges(this.id, this.secId, 20, 0); case ChangeType.APP: first = this.mgmtUserService.listAppChanges(this.id, this.secId, 20, 0);
break; break;
} }
@@ -100,18 +109,18 @@ export class ChangesComponent implements OnInit, OnDestroy {
const cursor = this.getCursor(); const cursor = this.getCursor();
console.log('cursor' + cursor); console.log('cursor' + cursor);
let more: Promise<Changes>; let more: Promise<ListChanges>;
switch (this.changeType) { switch (this.changeType) {
case ChangeType.MYUSER: more = this.authUserService.GetMyUserChanges(20, cursor); case ChangeType.MYUSER: more = this.authUserService.listMyUserChanges(20, cursor);
break; break;
case ChangeType.USER: more = this.mgmtUserService.UserChanges(this.id, 20, cursor); case ChangeType.USER: more = this.mgmtUserService.listUserChanges(this.id, 20, cursor);
break; break;
case ChangeType.PROJECT: more = this.mgmtUserService.ProjectChanges(this.id, 20, cursor); case ChangeType.PROJECT: more = this.mgmtUserService.listProjectChanges(this.id, 20, cursor);
break; break;
case ChangeType.ORG: more = this.mgmtUserService.OrgChanges(this.id, 20, cursor); case ChangeType.ORG: more = this.mgmtUserService.listOrgChanges(20, cursor);
break; break;
case ChangeType.APP: more = this.mgmtUserService.ApplicationChanges(this.id, this.secId, 20, cursor); case ChangeType.APP: more = this.mgmtUserService.listAppChanges(this.id, this.secId, 20, cursor);
break; break;
} }
@@ -131,7 +140,7 @@ export class ChangesComponent implements OnInit, OnDestroy {
} }
// Maps the snapshot to usable format the updates source // Maps the snapshot to usable format the updates source
private mapAndUpdate(col: Promise<Changes>): any { private mapAndUpdate(col: Promise<ListChanges>): any {
if (this._done.value || this._loading.value) { return; } if (this._done.value || this._loading.value) { return; }
// Map snapshot with doc ref (needed for cursor) // Map snapshot with doc ref (needed for cursor)
@@ -141,8 +150,8 @@ export class ChangesComponent implements OnInit, OnDestroy {
return from(col).pipe( return from(col).pipe(
take(1), take(1),
tap((res: Changes) => { tap((res: ListChanges) => {
const values = res.toObject().changesList; const values = res.resultList;
const mapped = this.mapChanges(values); const mapped = this.mapChanges(values);
// update source with new values, done loading // update source with new values, done loading
// this._data.next(values); // this._data.next(values);
@@ -173,19 +182,19 @@ export class ChangesComponent implements OnInit, OnDestroy {
if (index) { if (index) {
if (splitted[index]) { if (splitted[index]) {
const userData: any = { const userData: any = {
editor: change.editor, editor: change.editorDisplayName,
editorId: change.editorId, editorId: change.editorId,
editorName: change.editor, editorName: change.editorDisplayName,
dates: [change.changeDate], dates: [change.changeDate],
data: [change.data], // data: [change.data],
eventTypes: [change.eventType], eventTypes: [change.eventType],
sequences: [change.sequence], sequences: [change.sequence],
}; };
const lastIndex = splitted[index].length - 1; const lastIndex = splitted[index].length - 1;
if (lastIndex > -1 && splitted[index][lastIndex].editor === change.editor) { if (lastIndex > -1 && splitted[index][lastIndex].editor === change.editorDisplayName) {
splitted[index][lastIndex].dates.push(change.changeDate); splitted[index][lastIndex].dates.push(change.changeDate);
splitted[index][lastIndex].data.push(change.data); // splitted[index][lastIndex].data.push(change.data);
splitted[index][lastIndex].eventTypes.push(change.eventType); splitted[index][lastIndex].eventTypes.push(change.eventType);
splitted[index][lastIndex].sequences.push(change.sequence); splitted[index][lastIndex].sequences.push(change.sequence);
} else { } else {
@@ -194,12 +203,12 @@ export class ChangesComponent implements OnInit, OnDestroy {
} else { } else {
splitted[index] = [ splitted[index] = [
{ {
editor: change.editor, editor: change.editorDisplayName,
editorId: change.editorId, editorId: change.editorId,
editorName: change.editor, editorName: change.editorDisplayName,
dates: [change.changeDate], dates: [change.changeDate],
data: [change.data], // data: [change.data],
eventTypes: [change.eventType], eventTypes: [change.eventType],
sequences: [change.sequence], sequences: [change.sequence],
} }

View File

@@ -1,5 +1,5 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length" <app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[timestamp]="keyResult?.viewTimestamp" [selection]="selection"> [timestamp]="keyResult?.details?.viewTimestamp" [selection]="selection">
<div actions> <div actions>
<button color="warn" <button color="warn"
[disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false" [disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false"
@@ -60,7 +60,7 @@
</tr> </tr>
</table> </table>
<mat-paginator #paginator class="paginator" [length]="keyResult?.totalResult || 0" [pageSize]="10" <mat-paginator #paginator class="paginator" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@@ -7,12 +7,12 @@ import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { Moment } from 'moment'; import { Moment } from 'moment';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { AuthNKeyType, ClientKeySearchResponse, ClientKeyView, MachineKeySearchResponse, MachineKeyType, MachineKeyView } from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import { AddKeyDialogComponent, AddKeyDialogType } from 'src/app/modules/add-key-dialog/add-key-dialog.component'; import { AddKeyDialogComponent, AddKeyDialogType } from 'src/app/modules/add-key-dialog/add-key-dialog.component';
import { ShowKeyDialogComponent } from 'src/app/modules/show-key-dialog/show-key-dialog.component'; import { ShowKeyDialogComponent } from 'src/app/modules/show-key-dialog/show-key-dialog.component';
import { Key, KeyType } from 'src/app/proto/generated/zitadel/auth_n_key_pb';
import { ListAppKeysResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@Component({ @Component({
selector: 'app-client-keys', selector: 'app-client-keys',
@@ -24,14 +24,14 @@ export class ClientKeysComponent implements OnInit {
@Input() appId!: string; @Input() appId!: string;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public dataSource: MatTableDataSource<ClientKeyView.AsObject> = new MatTableDataSource<ClientKeyView.AsObject>(); public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
public selection: SelectionModel<ClientKeyView.AsObject> = new SelectionModel<ClientKeyView.AsObject>(true, []); public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
public keyResult!: MachineKeySearchResponse.AsObject | ClientKeySearchResponse.AsObject; public keyResult!: ListAppKeysResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate']; @Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate'];
@Output() public changedSelection: EventEmitter<Array<ClientKeyView.AsObject>> = new EventEmitter(); @Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog, constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog,
private toast: ToastService) { private toast: ToastService) {
@@ -64,7 +64,7 @@ export class ClientKeysComponent implements OnInit {
public deleteSelectedKeys(): void { public deleteSelectedKeys(): void {
const mappedDeletions = this.selection.selected.map(value => { const mappedDeletions = this.selection.selected.map(value => {
return this.mgmtService.DeleteClientKey(value.id, this.projectId, this.appId); return this.mgmtService.removeAppKey(this.projectId, this.appId, value.id);
}); });
Promise.all(mappedDeletions).then(() => { Promise.all(mappedDeletions).then(() => {
this.selection.clear(); this.selection.clear();
@@ -83,7 +83,7 @@ export class ClientKeysComponent implements OnInit {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const type: AuthNKeyType = resp.type; const type: KeyType = resp.type;
let date: Timestamp | undefined; let date: Timestamp | undefined;
@@ -99,7 +99,7 @@ export class ClientKeysComponent implements OnInit {
} }
if (type) { if (type) {
return this.mgmtService.addClientKey(this.projectId, this.appId, type, date).then((response) => { return this.mgmtService.addAppKey(this.projectId, this.appId, type, date ? date : undefined).then((response) => {
if (response) { if (response) {
setTimeout(() => { setTimeout(() => {
this.refreshPage(); this.refreshPage();
@@ -107,7 +107,7 @@ export class ClientKeysComponent implements OnInit {
this.dialog.open(ShowKeyDialogComponent, { this.dialog.open(ShowKeyDialogComponent, {
data: { data: {
key: response.toObject(), key: response,
type: AddKeyDialogType.AUTHNKEY type: AddKeyDialogType.AUTHNKEY
}, },
width: '400px', width: '400px',
@@ -124,8 +124,8 @@ export class ClientKeysComponent implements OnInit {
private async getData(limit: number, offset: number): Promise<void> { private async getData(limit: number, offset: number): Promise<void> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
if (this.projectId && this.appId) { if (this.projectId && this.appId) {
this.mgmtService.SearchClientKeys(this.projectId, this.appId, limit, offset).then(resp => { this.mgmtService.listAppKeys(this.projectId, this.appId, limit, offset).then(resp => {
this.keyResult = resp.toObject(); this.keyResult = resp;
this.dataSource.data = this.keyResult.resultList; this.dataSource.data = this.keyResult.resultList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch((error: any) => { }).catch((error: any) => {

View File

@@ -6,18 +6,13 @@ import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { import { AddOIDCIDPRequest } from 'src/app/proto/generated/zitadel/admin_pb';
OidcIdpConfigCreate as AdminOidcIdpConfigCreate, import { OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb';
OIDCMappingField as authMappingFields, import { AddOrgOIDCIDPRequest } from 'src/app/proto/generated/zitadel/management_pb';
} from 'src/app/proto/generated/admin_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import {
OidcIdpConfigCreate as MgmtOidcIdpConfigCreate,
OIDCMappingField as mgmtMappingFields,
} from '../../proto/generated/management_pb';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
@Component({ @Component({
@@ -29,7 +24,7 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
private service!: ManagementService | AdminService; private service!: ManagementService | AdminService;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public mappingFields: mgmtMappingFields[] | authMappingFields[] = []; public mappingFields: OIDCMappingField[] = [];
private subscription?: Subscription; private subscription?: Subscription;
public projectId: string = ''; public projectId: string = '';
@@ -61,14 +56,14 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>); this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.mappingFields = [ this.mappingFields = [
mgmtMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME, OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
mgmtMappingFields.OIDCMAPPINGFIELD_EMAIL]; OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL];
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>); this.service = this.injector.get(AdminService as Type<AdminService>);
this.mappingFields = [ this.mappingFields = [
authMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME, OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
authMappingFields.OIDCMAPPINGFIELD_EMAIL]; OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL];
break; break;
} }
}); });
@@ -87,36 +82,50 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
} }
public addIdp(): void { public addIdp(): void {
let req: AdminOidcIdpConfigCreate | MgmtOidcIdpConfigCreate; if (this.serviceType == PolicyComponentServiceType.MGMT) {
const req = new AddOrgOIDCIDPRequest();
switch (this.serviceType) { req.setName(this.name?.value);
case PolicyComponentServiceType.MGMT: req.setClientId(this.clientId?.value);
req = new MgmtOidcIdpConfigCreate(); req.setClientSecret(this.clientSecret?.value);
break; req.setIssuer(this.issuer?.value);
case PolicyComponentServiceType.ADMIN: req.setScopesList(this.scopesList?.value);
req = new AdminOidcIdpConfigCreate(); req.setDisplayNameMapping(this.idpDisplayNameMapping?.value);
break; req.setUsernameMapping(this.usernameMapping?.value);
this.loading = true;
(this.service as ManagementService).addOrgOIDCIDP(req).then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate([
(this.serviceType === PolicyComponentServiceType.MGMT ? 'org' :
this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : ''),
'policy', 'login']);
}, 2000);
}).catch(error => {
this.toast.showError(error);
});
} else if (PolicyComponentServiceType.ADMIN) {
const req = new AddOIDCIDPRequest();
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setClientSecret(this.clientSecret?.value);
req.setIssuer(this.issuer?.value);
req.setScopesList(this.scopesList?.value);
req.setDisplayNameMapping(this.idpDisplayNameMapping?.value);
req.setUsernameMapping(this.usernameMapping?.value);
this.loading = true;
(this.service as AdminService).addOIDCIDP(req).then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate([
(this.serviceType === PolicyComponentServiceType.MGMT ? 'org' :
this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : ''),
'policy', 'login']);
}, 2000);
}).catch(error => {
this.toast.showError(error);
});
} }
req.setName(this.name?.value);
req.setClientId(this.clientId?.value);
req.setClientSecret(this.clientSecret?.value);
req.setIssuer(this.issuer?.value);
req.setScopesList(this.scopesList?.value);
req.setIdpDisplayNameMapping(this.idpDisplayNameMapping?.value);
req.setUsernameMapping(this.usernameMapping?.value);
this.loading = true;
this.service.CreateOidcIdp(req).then((idp) => {
setTimeout(() => {
this.loading = false;
this.router.navigate([
(this.serviceType === PolicyComponentServiceType.MGMT ? 'org' :
this.serviceType === PolicyComponentServiceType.ADMIN ? 'iam' : ''),
'policy', 'login']);
}, 2000);
}).catch(error => {
this.toast.showError(error);
});
} }
public close(): void { public close(): void {

View File

@@ -1,5 +1,6 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length" <app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[emitRefreshOnPreviousRoutes]="['/iam/idp/create']" [timestamp]="idpResult?.viewTimestamp" [selection]="selection"> [emitRefreshOnPreviousRoutes]="['/iam/idp/create']" [timestamp]="idpResult?.details?.viewTimestamp"
[selection]="selection">
<div actions> <div actions>
<button (click)="deactivateSelectedIdps()" matTooltip="{{'IDP.DEACTIVATE' | translate}}" class="icon-button" <button (click)="deactivateSelectedIdps()" matTooltip="{{'IDP.DEACTIVATE' | translate}}" class="icon-button"
mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled"> mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
@@ -30,7 +31,7 @@
</th> </th>
<td mat-cell *matCellDef="let idp"> <td mat-cell *matCellDef="let idp">
<mat-checkbox color="primary" (click)="$event.stopPropagation()" class="chbox" <mat-checkbox color="primary" (click)="$event.stopPropagation()" class="chbox"
[disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IdpProviderType.IDPPROVIDERTYPE_SYSTEM" [disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IDPOwnerType.IDPPROVIDERTYPE_SYSTEM"
(change)="$event ? selection.toggle(idp) : null" [checked]="selection.isSelected(idp)"> (change)="$event ? selection.toggle(idp) : null" [checked]="selection.isSelected(idp)">
<img src="../../../assets/images/google.png" <img src="../../../assets/images/google.png"
*ngIf="idp.stylingType == IdpStylingType.IDPSTYLINGTYPE_GOOGLE" alt="google" /> *ngIf="idp.stylingType == IdpStylingType.IDPSTYLINGTYPE_GOOGLE" alt="google" />
@@ -58,7 +59,7 @@
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.STATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'IDP.STATE' | translate }} </th>
<td [routerLink]="routerLinkForRow(idp)" mat-cell *matCellDef="let idp"> <td [routerLink]="routerLinkForRow(idp)" mat-cell *matCellDef="let idp">
<span class="state" <span class="state"
[ngClass]="{'active': idp.state === IdpState.IDPCONFIGSTATE_ACTIVE, 'inactive': idp.state === IdpState.IDPCONFIGSTATE_INACTIVE}">{{ [ngClass]="{'active': idp.state === IDPState.IDP_STATE_ACTIVE, 'inactive': idp.state === IDPState.IDP_STATE_INACTIVE}">{{
'IDP.STATES.'+idp.state | translate }}</span> 'IDP.STATES.'+idp.state | translate }}</span>
</td> </td>
</ng-container> </ng-container>
@@ -87,7 +88,7 @@
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let idp"> <td mat-cell *matCellDef="let idp">
<button <button
[disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IdpProviderType.IDPPROVIDERTYPE_SYSTEM" [disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IDPOwnerType.IDP_OWNER_TYPE_ORG"
mat-icon-button color="warn" matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}" mat-icon-button color="warn" matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}"
(click)="removeIdp(idp)"> (click)="removeIdp(idp)">
<i class="las la-trash"></i> <i class="las la-trash"></i>
@@ -97,11 +98,11 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" <tr class="highlight"
[ngClass]="{'disabled': serviceType==PolicyComponentServiceType.MGMT && row?.providerType == IdpProviderType.IDPPROVIDERTYPE_SYSTEM}" [ngClass]="{'disabled': serviceType==PolicyComponentServiceType.MGMT && row?.providerType == IDPOwnerType.IDP_OWNER_TYPE_SYSTEM}"
mat-row *matRowDef="let row; columns: displayedColumns;"> mat-row *matRowDef="let row; columns: displayedColumns;">
</tr> </tr>
</table> </table>
</div> </div>
<mat-paginator #paginator class="paginator" [length]="idpResult?.totalResult || 0" [pageSize]="10" <mat-paginator #paginator class="paginator" [length]="idpResult?.details?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</app-refresh-table> </app-refresh-table>

View File

@@ -5,10 +5,10 @@ import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { IdpSearchResponse as AdminIdpSearchResponse, IdpState, IdpStylingType, IdpView as AdminIdpView } from 'src/app/proto/generated/admin_pb'; import { ListIDPsResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { IdpProviderType, IdpView as MgmtIdpView } from 'src/app/proto/generated/management_pb'; import { IDP, IDPOwnerType, IDPState, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb';
import { ListOrgIDPsResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -26,20 +26,20 @@ export class IdpTableComponent implements OnInit {
@Input() service!: AdminService | ManagementService; @Input() service!: AdminService | ManagementService;
@Input() disabled: boolean = false; @Input() disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public dataSource: MatTableDataSource<AdminIdpView.AsObject | MgmtIdpView.AsObject> public dataSource: MatTableDataSource<IDP.AsObject>
= new MatTableDataSource<AdminIdpView.AsObject | MgmtIdpView.AsObject>(); = new MatTableDataSource<IDP.AsObject>();
public selection: SelectionModel<AdminIdpView.AsObject | MgmtIdpView.AsObject> public selection: SelectionModel<IDP.AsObject>
= new SelectionModel<AdminIdpView.AsObject | MgmtIdpView.AsObject>(true, []); = new SelectionModel<IDP.AsObject>(true, []);
public idpResult!: AdminIdpSearchResponse.AsObject; public idpResult!: ListIDPsResponse.AsObject | ListOrgIDPsResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public IdpProviderType: any = IdpProviderType; public IDPOwnerType: any = IDPOwnerType;
public IdpState: any = IdpState; public IDPState: any = IDPState;
public IdpStylingType: any = IdpStylingType; public IdpStylingType: any = IDPStylingType;
@Input() public displayedColumns: string[] = ['select', 'name', 'config', 'dates', 'state']; @Input() public displayedColumns: string[] = ['select', 'name', 'config', 'dates', 'state'];
@Output() public changedSelection: EventEmitter<Array<AdminIdpView.AsObject | MgmtIdpView.AsObject>> @Output() public changedSelection: EventEmitter<Array<IDP.AsObject>>
= new EventEmitter(); = new EventEmitter();
constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) { constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) {
@@ -77,8 +77,12 @@ export class IdpTableComponent implements OnInit {
} }
public deactivateSelectedIdps(): void { public deactivateSelectedIdps(): void {
const map: Promise<Empty>[] = this.selection.selected.map(value => { const map: Promise<any>[] = this.selection.selected.map(value => {
return this.service.DeactivateIdpConfig(value.id); if (this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.service as ManagementService).deactivateOrgIDP(value.id);
} else {
return (this.service as AdminService).deactivateIDP(value.id);
}
}); });
Promise.all(map).then(() => { Promise.all(map).then(() => {
this.selection.clear(); this.selection.clear();
@@ -90,8 +94,12 @@ export class IdpTableComponent implements OnInit {
} }
public reactivateSelectedIdps(): void { public reactivateSelectedIdps(): void {
const map: Promise<Empty>[] = this.selection.selected.map(value => { const map: Promise<any>[] = this.selection.selected.map(value => {
return this.service.ReactivateIdpConfig(value.id); if (this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.service as ManagementService).reactivateOrgIDP(value.id);
} else {
return (this.service as AdminService).reactivateIDP(value.id);
}
}); });
Promise.all(map).then(() => { Promise.all(map).then(() => {
this.selection.clear(); this.selection.clear();
@@ -116,9 +124,12 @@ export class IdpTableComponent implements OnInit {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.selection.clear(); this.selection.clear();
Promise.all(this.selection.selected.map(value => { Promise.all(this.selection.selected.map(value => {
return this.service.RemoveIdpConfig(value.id); if (this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.service as ManagementService).removeOrgIDP(value.id);
} else {
return (this.service as AdminService).removeIDP(value.id);
}
})).then(() => { })).then(() => {
this.toast.showInfo('IDP.TOAST.SELECTEDDEACTIVATED', true); this.toast.showInfo('IDP.TOAST.SELECTEDDEACTIVATED', true);
this.refreshPage(); this.refreshPage();
@@ -127,7 +138,7 @@ export class IdpTableComponent implements OnInit {
}); });
} }
public removeIdp(idp: AdminIdpView.AsObject | MgmtIdpView.AsObject): void { public removeIdp(idp: IDP.AsObject): void {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
confirmKey: 'ACTIONS.DELETE', confirmKey: 'ACTIONS.DELETE',
@@ -140,12 +151,21 @@ export class IdpTableComponent implements OnInit {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.service.RemoveIdpConfig(idp.id).then(() => { if (this.serviceType === PolicyComponentServiceType.MGMT) {
this.toast.showInfo('IDP.TOAST.REMOVED', true); (this.service as ManagementService).removeOrgIDP(idp.id).then(() => {
setTimeout(() => { this.toast.showInfo('IDP.TOAST.REMOVED', true);
this.refreshPage(); setTimeout(() => {
}, 1000); this.refreshPage();
}); }, 1000);
});
} else {
(this.service as AdminService).removeIDP(idp.id).then(() => {
this.toast.showInfo('IDP.TOAST.REMOVED', true);
setTimeout(() => {
this.refreshPage();
}, 1000);
});
}
} }
}); });
} }
@@ -153,14 +173,26 @@ export class IdpTableComponent implements OnInit {
private async getData(limit: number, offset: number): Promise<void> { private async getData(limit: number, offset: number): Promise<void> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
this.service.SearchIdps(limit, offset).then(resp => { if (this.serviceType === PolicyComponentServiceType.MGMT) {
this.idpResult = resp.toObject(); (this.service as ManagementService).listOrgIDPs(limit, offset).then(resp => {
this.dataSource.data = this.idpResult.resultList; this.idpResult = resp;
this.loadingSubject.next(false); this.dataSource.data = resp.resultList;
}).catch(error => { this.loadingSubject.next(false);
this.toast.showError(error); }).catch(error => {
this.loadingSubject.next(false); this.toast.showError(error);
}); this.loadingSubject.next(false);
});
} else {
(this.service as AdminService).listIDPs(limit, offset).then(resp => {
this.idpResult = resp;
this.dataSource.data = resp.resultList;
this.loadingSubject.next(false);
}).catch(error => {
this.toast.showError(error);
this.loadingSubject.next(false);
});
}
} }
public refreshPage(): void { public refreshPage(): void {
@@ -175,14 +207,14 @@ export class IdpTableComponent implements OnInit {
} }
} }
public routerLinkForRow(row: MgmtIdpView.AsObject | AdminIdpView.AsObject): any { public routerLinkForRow(row: IDP.AsObject): any {
if (row.id) { if (row.id) {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
switch ((row as MgmtIdpView.AsObject).providerType) { switch (row.owner) {
case IdpProviderType.IDPPROVIDERTYPE_SYSTEM: case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM:
return ['/iam', 'idp', row.id]; return ['/iam', 'idp', row.id];
case IdpProviderType.IDPPROVIDERTYPE_ORG: case IDPOwnerType.IDP_OWNER_TYPE_ORG:
return ['/org', 'idp', row.id]; return ['/org', 'idp', row.id];
} }
break; break;

View File

@@ -6,18 +6,9 @@ import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap, take } from 'rxjs/operators'; import { switchMap, take } from 'rxjs/operators';
import { import { UpdateIDPOIDCConfigRequest, UpdateIDPRequest } from 'src/app/proto/generated/zitadel/admin_pb';
IdpStylingType as adminIdpStylingType, import { IDPStylingType, OIDCMappingField } from 'src/app/proto/generated/zitadel/idp_pb';
IdpUpdate as AdminIdpConfigUpdate, import { UpdateOrgIDPOIDCConfigRequest, UpdateOrgIDPRequest } from 'src/app/proto/generated/zitadel/management_pb';
OidcIdpConfigUpdate as AdminOidcIdpConfigUpdate,
OIDCMappingField as adminMappingFields,
} from 'src/app/proto/generated/admin_pb';
import {
IdpStylingType as mgmtIdpStylingType,
IdpUpdate as MgmtIdpConfigUpdate,
OidcIdpConfigUpdate as MgmtOidcIdpConfigUpdate,
OIDCMappingField as mgmtMappingFields,
} from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -30,8 +21,8 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e
styleUrls: ['./idp.component.scss'], styleUrls: ['./idp.component.scss'],
}) })
export class IdpComponent implements OnInit, OnDestroy { export class IdpComponent implements OnInit, OnDestroy {
public mappingFields: mgmtMappingFields[] | adminMappingFields[] = []; public mappingFields: OIDCMappingField[] = [];
public styleFields: mgmtIdpStylingType[] | adminIdpStylingType[] = []; public styleFields: IDPStylingType[] = [];
public showIdSecretSection: boolean = false; public showIdSecretSection: boolean = false;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
@@ -70,35 +61,46 @@ export class IdpComponent implements OnInit, OnDestroy {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>); this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.mappingFields = [
mgmtMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME,
mgmtMappingFields.OIDCMAPPINGFIELD_EMAIL];
this.styleFields = [
mgmtIdpStylingType.IDPSTYLINGTYPE_UNSPECIFIED,
mgmtIdpStylingType.IDPSTYLINGTYPE_GOOGLE];
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>); this.service = this.injector.get(AdminService as Type<AdminService>);
this.mappingFields = [
adminMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME,
adminMappingFields.OIDCMAPPINGFIELD_EMAIL];
this.styleFields = [
adminIdpStylingType.IDPSTYLINGTYPE_UNSPECIFIED,
adminIdpStylingType.IDPSTYLINGTYPE_GOOGLE];
break; break;
} }
this.mappingFields = [
OIDCMappingField.OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
OIDCMappingField.OIDC_MAPPING_FIELD_EMAIL];
this.styleFields = [
IDPStylingType.STYLING_TYPE_UNSPECIFIED,
IDPStylingType.STYLING_TYPE_GOOGLE];
return this.route.params.pipe(take(1)); return this.route.params.pipe(take(1));
})).subscribe((params) => { })).subscribe((params) => {
const { id } = params; const { id } = params;
if (id) { if (id) {
this.service.IdpByID(id).then(idp => { if (this.serviceType == PolicyComponentServiceType.MGMT) {
const idpObject = idp.toObject(); (this.service as ManagementService).getOrgIDPByID(id).then(resp => {
this.idpForm.patchValue(idpObject); if (resp.idp) {
if (idpObject.oidcConfig) { const idpObject = resp.idp;
this.oidcConfigForm.patchValue(idpObject.oidcConfig); this.idpForm.patchValue(idpObject);
} if (idpObject.oidcConfig) {
}); this.oidcConfigForm.patchValue(idpObject.oidcConfig);
}
}
});
} else if (this.serviceType == PolicyComponentServiceType.ADMIN) {
(this.service as AdminService).getIDPByID(id).then(resp => {
if (resp.idp) {
const idpObject = resp.idp;
this.idpForm.patchValue(idpObject);
if (idpObject.oidcConfig) {
this.oidcConfigForm.patchValue(idpObject.oidcConfig);
}
}
});
}
} }
}); });
} }
@@ -116,55 +118,71 @@ export class IdpComponent implements OnInit, OnDestroy {
} }
public updateIdp(): void { public updateIdp(): void {
let req: AdminIdpConfigUpdate | MgmtIdpConfigUpdate; if (this.serviceType == PolicyComponentServiceType.MGMT) {
const req = new UpdateOrgIDPRequest();
switch (this.serviceType) { req.setIdpId(this.id?.value);
case PolicyComponentServiceType.MGMT: req.setName(this.name?.value);
req = new MgmtIdpConfigUpdate(); req.setStylingType(this.stylingType?.value);
break;
case PolicyComponentServiceType.ADMIN: (this.service as ManagementService).updateOrgIDP(req).then(() => {
req = new AdminIdpConfigUpdate(); this.toast.showInfo('IDP.TOAST.SAVED', true);
break; // this.router.navigate(['idp', ]);
}).catch(error => {
this.toast.showError(error);
});
} else if (this.serviceType == PolicyComponentServiceType.ADMIN) {
const req = new UpdateIDPRequest();
req.setIdpId(this.id?.value);
req.setName(this.name?.value);
req.setStylingType(this.stylingType?.value);
(this.service as AdminService).updateIDP(req).then(() => {
this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]);
}).catch(error => {
this.toast.showError(error);
});
} }
req.setId(this.id?.value);
req.setName(this.name?.value);
req.setStylingType(this.stylingType?.value);
this.service.UpdateIdp(req).then((idp) => {
this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]);
}).catch(error => {
this.toast.showError(error);
});
} }
public updateOidcConfig(): void { public updateOidcConfig(): void {
let req: AdminOidcIdpConfigUpdate | MgmtOidcIdpConfigUpdate; if (this.serviceType == PolicyComponentServiceType.MGMT) {
const req = new UpdateOrgIDPOIDCConfigRequest();
switch (this.serviceType) { req.setIdpId(this.id?.value);
case PolicyComponentServiceType.MGMT: req.setClientId(this.clientId?.value);
req = new MgmtOidcIdpConfigUpdate(); req.setClientSecret(this.clientSecret?.value);
break; req.setIssuer(this.issuer?.value);
case PolicyComponentServiceType.ADMIN: req.setScopesList(this.scopesList?.value);
req = new AdminOidcIdpConfigUpdate(); req.setUsernameMapping(this.usernameMapping?.value);
break; req.setDisplayNameMapping(this.idpDisplayNameMapping?.value);
(this.service as ManagementService).updateOrgIDPOIDCConfig(req).then((oidcConfig) => {
this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]);
}).catch(error => {
this.toast.showError(error);
});
} else if (this.serviceType == PolicyComponentServiceType.ADMIN) {
const req = new UpdateIDPOIDCConfigRequest();
req.setIdpId(this.id?.value);
req.setClientId(this.clientId?.value);
req.setClientSecret(this.clientSecret?.value);
req.setIssuer(this.issuer?.value);
req.setScopesList(this.scopesList?.value);
req.setUsernameMapping(this.usernameMapping?.value);
req.setDisplayNameMapping(this.idpDisplayNameMapping?.value);
(this.service as AdminService).updateIDPOIDCConfig(req).then((oidcConfig) => {
this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]);
}).catch(error => {
this.toast.showError(error);
});
} }
req.setIdpId(this.id?.value);
req.setClientId(this.clientId?.value);
req.setClientSecret(this.clientSecret?.value);
req.setIssuer(this.issuer?.value);
req.setScopesList(this.scopesList?.value);
req.setUsernameMapping(this.usernameMapping?.value);
req.setIdpDisplayNameMapping(this.idpDisplayNameMapping?.value);
this.service.UpdateOidcIdpConfig(req).then((oidcConfig) => {
this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]);
}).catch(error => {
this.toast.showError(error);
});
} }
public close(): void { public close(): void {

View File

@@ -1,5 +1,5 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length" <app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[timestamp]="keyResult?.viewTimestamp" [selection]="selection"> [timestamp]="keyResult?.details?.viewTimestamp" [selection]="selection">
<div actions> <div actions>
<button color="warn" [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false" <button color="warn" [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false"
(click)="deleteSelectedKeys()" matTooltip="{{'ACTIONS.DELETE' | translate}}" class="icon-button" (click)="deleteSelectedKeys()" matTooltip="{{'ACTIONS.DELETE' | translate}}" class="icon-button"
@@ -58,7 +58,7 @@
</tr> </tr>
</table> </table>
<mat-paginator #paginator class="paginator" [length]="keyResult?.totalResult || 0" [pageSize]="10" <mat-paginator #paginator class="paginator" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@@ -7,12 +7,12 @@ import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { Moment } from 'moment'; import { Moment } from 'moment';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { ClientKeySearchResponse, MachineKeySearchResponse, MachineKeyType, MachineKeyView } from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import { AddKeyDialogComponent, AddKeyDialogType } from 'src/app/modules/add-key-dialog/add-key-dialog.component'; import { AddKeyDialogComponent, AddKeyDialogType } from 'src/app/modules/add-key-dialog/add-key-dialog.component';
import { ShowKeyDialogComponent } from 'src/app/modules/show-key-dialog/show-key-dialog.component'; import { ShowKeyDialogComponent } from 'src/app/modules/show-key-dialog/show-key-dialog.component';
import { Key, KeyType } from 'src/app/proto/generated/zitadel/auth_n_key_pb';
import { ListMachineKeysResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@Component({ @Component({
selector: 'app-machine-keys', selector: 'app-machine-keys',
@@ -23,14 +23,14 @@ export class MachineKeysComponent implements OnInit {
@Input() userId!: string; @Input() userId!: string;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public dataSource: MatTableDataSource<MachineKeyView.AsObject> = new MatTableDataSource<MachineKeyView.AsObject>(); public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
public selection: SelectionModel<MachineKeyView.AsObject> = new SelectionModel<MachineKeyView.AsObject>(true, []); public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
public keyResult!: MachineKeySearchResponse.AsObject | ClientKeySearchResponse.AsObject; public keyResult!: ListMachineKeysResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate']; @Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate'];
@Output() public changedSelection: EventEmitter<Array<MachineKeyView.AsObject>> = new EventEmitter(); @Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog, constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog,
private toast: ToastService) { private toast: ToastService) {
@@ -63,7 +63,7 @@ export class MachineKeysComponent implements OnInit {
public deleteSelectedKeys(): void { public deleteSelectedKeys(): void {
const mappedDeletions = this.selection.selected.map(value => { const mappedDeletions = this.selection.selected.map(value => {
return this.mgmtService.DeleteMachineKey(value.id, this.userId); return this.mgmtService.removeMachineKey(value.id, this.userId);
}); });
Promise.all(mappedDeletions).then(() => { Promise.all(mappedDeletions).then(() => {
this.selection.clear(); this.selection.clear();
@@ -82,7 +82,7 @@ export class MachineKeysComponent implements OnInit {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const type: MachineKeyType = resp.type; const type: KeyType = resp.type;
let date: Timestamp | undefined; let date: Timestamp | undefined;
@@ -98,7 +98,7 @@ export class MachineKeysComponent implements OnInit {
} }
if (type) { if (type) {
return this.mgmtService.AddMachineKey(this.userId, type, date).then((response) => { return this.mgmtService.addMachineKey(this.userId, type, date).then((response) => {
if (response) { if (response) {
setTimeout(() => { setTimeout(() => {
this.refreshPage(); this.refreshPage();
@@ -106,7 +106,7 @@ export class MachineKeysComponent implements OnInit {
this.dialog.open(ShowKeyDialogComponent, { this.dialog.open(ShowKeyDialogComponent, {
data: { data: {
key: response.toObject(), key: response,
type: AddKeyDialogType.MACHINE type: AddKeyDialogType.MACHINE
}, },
width: '400px', width: '400px',
@@ -124,9 +124,11 @@ export class MachineKeysComponent implements OnInit {
this.loadingSubject.next(true); this.loadingSubject.next(true);
if (this.userId) { if (this.userId) {
this.mgmtService.SearchMachineKeys(this.userId, limit, offset).then(resp => { this.mgmtService.listMachineKeys(this.userId, limit, offset).then(resp => {
this.keyResult = resp.toObject(); this.keyResult = resp;
this.dataSource.data = this.keyResult.resultList; if (resp.resultList) {
this.dataSource.data = resp.resultList;
}
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch((error: any) => { }).catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -7,12 +7,10 @@ import { Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { IamMembersDataSource } from 'src/app/pages/iam/iam-members/iam-members-datasource'; import { IamMembersDataSource } from 'src/app/pages/iam/iam-members/iam-members-datasource';
import { OrgMembersDataSource } from 'src/app/pages/orgs/org-members/org-members-datasource'; import { OrgMembersDataSource } from 'src/app/pages/orgs/org-members/org-members-datasource';
import { IamMemberView } from 'src/app/proto/generated/admin_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { OrgMemberView, ProjectMemberView } from 'src/app/proto/generated/management_pb';
import { ProjectMembersDataSource } from '../project-members/project-members-datasource'; import { ProjectMembersDataSource } from '../project-members/project-members-datasource';
type View = OrgMemberView.AsObject | ProjectMemberView.AsObject | IamMemberView.AsObject;
type MemberDatasource = OrgMembersDataSource | ProjectMembersDataSource | IamMembersDataSource; type MemberDatasource = OrgMembersDataSource | ProjectMembersDataSource | IamMembersDataSource;
@Component({ @Component({
@@ -25,15 +23,15 @@ export class MembersTableComponent implements OnInit, OnDestroy {
@Input() public canDelete: boolean = false; @Input() public canDelete: boolean = false;
@Input() public canWrite: boolean = false; @Input() public canWrite: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<View>; @ViewChild(MatTable) public table!: MatTable<Member>;
@Input() public dataSource!: MemberDatasource; @Input() public dataSource!: MemberDatasource;
public selection: SelectionModel<any> = new SelectionModel<any>(true, []); public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
@Input() public memberRoleOptions: string[] = []; @Input() public memberRoleOptions: string[] = [];
@Input() public factoryLoadFunc!: Function; @Input() public factoryLoadFunc!: Function;
@Input() public refreshTrigger!: Observable<void>; @Input() public refreshTrigger!: Observable<void>;
@Output() public updateRoles: EventEmitter<{ member: View, change: MatSelectChange; }> = new EventEmitter(); @Output() public updateRoles: EventEmitter<{ member: Member, change: MatSelectChange; }> = new EventEmitter();
@Output() public changedSelection: EventEmitter<any[]> = new EventEmitter(); @Output() public changedSelection: EventEmitter<any[]> = new EventEmitter();
@Output() public deleteMember: EventEmitter<View> = new EventEmitter(); @Output() public deleteMember: EventEmitter<Member> = new EventEmitter();
private destroyed: Subject<void> = new Subject(); private destroyed: Subject<void> = new Subject();

View File

@@ -2,18 +2,18 @@
<div mat-dialog-content> <div mat-dialog-content>
<p class="desc">{{data.desc | translate}}</p> <p class="desc">{{data.desc | translate}}</p>
<cnsl-form-field class="form-field" label="Access Code" required="true"> <!-- <cnsl-form-field class="form-field" label="Access Code" required="true">
<cnsl-label>{{'MFA.TYPE' | translate}}</cnsl-label> <cnsl-label>{{'MFA.TYPE' | translate}}</cnsl-label>
<mat-select [(ngModel)]="newMfaType"> <mat-select [(ngModel)]="newMfaType">
<mat-option *ngFor="let mfa of availableMfaTypes" [value]="mfa"> <mat-option *ngFor="let mfa of []" [value]="mfa">
{{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}} {{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
</mat-option> </mat-option>
</mat-select> </mat-select>
</cnsl-form-field> </cnsl-form-field> -->
</div> </div>
<div mat-dialog-actions class="action"> <div mat-dialog-actions class="action">
<button mat-button (click)="closeDialog()"><span>{{'ACTIONS.CLOSE' | translate}}</span></button> <button mat-button (click)="closeDialog()"><span>{{'ACTIONS.CLOSE' | translate}}</span></button>
<button [disabled]="newMfaType == undefined" mat-raised-button class="ok-button" color="primary" <button [disabled]="false" mat-raised-button class="ok-button" color="primary"
(click)="closeDialogWithCode()"><span>{{'ACTIONS.OK' | translate}}</span> (click)="closeDialogWithCode()"><span>{{'ACTIONS.OK' | translate}}</span>
</button> </button>
</div> </div>

View File

@@ -1,9 +1,6 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MultiFactorType as AdminMultiFactorType } from 'src/app/proto/generated/admin_pb';
import { MultiFactorType as MgmtMultiFactorType } from 'src/app/proto/generated/management_pb';
enum LoginMethodComponentType { enum LoginMethodComponentType {
MultiFactor = 1, MultiFactor = 1,
SecondFactor = 2, SecondFactor = 2,
@@ -16,11 +13,10 @@ enum LoginMethodComponentType {
}) })
export class DialogAddTypeComponent { export class DialogAddTypeComponent {
public LoginMethodComponentType: any = LoginMethodComponentType; public LoginMethodComponentType: any = LoginMethodComponentType;
public newMfaType!: AdminMultiFactorType | MgmtMultiFactorType; // public availableMfaTypes: Array<AdminMultiFactorType | MgmtMultiFactorType> = [];
public availableMfaTypes: Array<AdminMultiFactorType | MgmtMultiFactorType> = [];
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>, constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) { @Inject(MAT_DIALOG_DATA) public data: any) {
this.availableMfaTypes = data.types; // this.availableMfaTypes = data.types;
} }
public closeDialog(): void { public closeDialog(): void {
@@ -28,6 +24,6 @@ export class DialogAddTypeComponent {
} }
public closeDialogWithCode(): void { public closeDialogWithCode(): void {
this.dialogRef.close(this.newMfaType); // this.dialogRef.close(this.newMfaType);
} }
} }

View File

@@ -4,24 +4,20 @@ import { MatPaginator } from '@angular/material/paginator';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { import {
MultiFactor as AdminMultiFactor, RemoveMultiFactorFromLoginPolicyRequest as AdminRemoveMultiFactorFromLoginPolicyRequest,
MultiFactorType as AdminMultiFactorType, RemoveSecondFactorFromLoginPolicyRequest as AdminRemoveSecondFactorFromLoginPolicyRequest,
SecondFactor as AdminSecondFactor, } from 'src/app/proto/generated/zitadel/admin_pb';
SecondFactorType as AdminSecondFactorType,
} from 'src/app/proto/generated/admin_pb';
import { import {
MultiFactor as MgmtMultiFactor, RemoveMultiFactorFromLoginPolicyRequest as MgmtRemoveMultiFactorFromLoginPolicyRequest,
MultiFactorType as MgmtMultiFactorType, RemoveSecondFactorFromLoginPolicyRequest as MgmtRemoveSecondFactorFromLoginPolicyRequest,
SecondFactor as MgmtSecondFactor, } from 'src/app/proto/generated/zitadel/management_pb';
SecondFactorType as MgmtSecondFactorType, import { MultiFactorType, SecondFactorType } from 'src/app/proto/generated/zitadel/policy_pb';
} from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
import { DialogAddTypeComponent } from './dialog-add-type/dialog-add-type.component';
export enum LoginMethodComponentType { export enum LoginMethodComponentType {
MultiFactor = 1, MultiFactor = 1,
@@ -40,7 +36,7 @@ export class MfaTableComponent implements OnInit {
@Input() service!: AdminService | ManagementService; @Input() service!: AdminService | ManagementService;
@Input() disabled: boolean = false; @Input() disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public mfas: Array<AdminMultiFactorType | MgmtMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType> = []; public mfas: Array<MultiFactorType | SecondFactorType> = [];
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -53,7 +49,7 @@ export class MfaTableComponent implements OnInit {
this.getData(); this.getData();
} }
public removeMfa(type: MgmtMultiFactorType | AdminMultiFactorType | MgmtSecondFactorType | AdminSecondFactorType): void { public removeMfa(type: MultiFactorType | SecondFactorType): void {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
confirmKey: 'ACTIONS.DELETE', confirmKey: 'ACTIONS.DELETE',
@@ -68,32 +64,32 @@ export class MfaTableComponent implements OnInit {
if (resp) { if (resp) {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
if (this.componentType === LoginMethodComponentType.MultiFactor) { if (this.componentType === LoginMethodComponentType.MultiFactor) {
const req = new MgmtMultiFactor(); const req = new MgmtRemoveMultiFactorFromLoginPolicyRequest();
req.setMultiFactor(type as MgmtMultiFactorType); req.setType(type as MultiFactorType);
(this.service as ManagementService).RemoveMultiFactorFromLoginPolicy(req).then(() => { (this.service as ManagementService).removeMultiFactorFromLoginPolicy(req).then(() => {
this.toast.showInfo('MFA.TOAST.DELETED', true); this.toast.showInfo('MFA.TOAST.DELETED', true);
this.refreshPageAfterTimout(2000); this.refreshPageAfterTimout(2000);
}); });
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
const req = new MgmtSecondFactor(); const req = new MgmtRemoveSecondFactorFromLoginPolicyRequest();
req.setSecondFactor(type as MgmtSecondFactorType); req.setType(type as SecondFactorType);
(this.service as ManagementService).RemoveSecondFactorFromLoginPolicy(req).then(() => { (this.service as ManagementService).removeSecondFactorFromLoginPolicy(req).then(() => {
this.toast.showInfo('MFA.TOAST.DELETED', true); this.toast.showInfo('MFA.TOAST.DELETED', true);
this.refreshPageAfterTimout(2000); this.refreshPageAfterTimout(2000);
}); });
} }
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
if (this.componentType === LoginMethodComponentType.MultiFactor) { if (this.componentType === LoginMethodComponentType.MultiFactor) {
const req = new AdminMultiFactor(); const req = new AdminRemoveMultiFactorFromLoginPolicyRequest();
req.setMultiFactor(type as AdminMultiFactorType); req.setType(type as MultiFactorType);
(this.service as AdminService).RemoveMultiFactorFromDefaultLoginPolicy(req).then(() => { (this.service as AdminService).removeMultiFactorFromLoginPolicy(req).then(() => {
this.toast.showInfo('MFA.TOAST.DELETED', true); this.toast.showInfo('MFA.TOAST.DELETED', true);
this.refreshPageAfterTimout(2000); this.refreshPageAfterTimout(2000);
}); });
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
const req = new AdminSecondFactor(); const req = new AdminRemoveSecondFactorFromLoginPolicyRequest();
req.setSecondFactor(type as AdminSecondFactorType); req.setType(type as SecondFactorType);
(this.service as AdminService).RemoveSecondFactorFromDefaultLoginPolicy(req).then(() => { (this.service as AdminService).removeSecondFactorFromLoginPolicy(req).then(() => {
this.toast.showInfo('MFA.TOAST.DELETED', true); this.toast.showInfo('MFA.TOAST.DELETED', true);
this.refreshPageAfterTimout(2000); this.refreshPageAfterTimout(2000);
}); });
@@ -108,17 +104,9 @@ export class MfaTableComponent implements OnInit {
let selection: any[] = []; let selection: any[] = [];
if (this.componentType === LoginMethodComponentType.MultiFactor) { if (this.componentType === LoginMethodComponentType.MultiFactor) {
selection = this.serviceType === PolicyComponentServiceType.MGMT ? selection = [MultiFactorType.MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION];
[MgmtMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] :
this.serviceType === PolicyComponentServiceType.ADMIN ?
[AdminMultiFactorType.MULTIFACTORTYPE_U2F_WITH_PIN] :
[];
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
selection = this.serviceType === PolicyComponentServiceType.MGMT ? selection = [SecondFactorType.SECOND_FACTOR_TYPE_U2F, SecondFactorType.SECOND_FACTOR_TYPE_OTP];
[MgmtSecondFactorType.SECONDFACTORTYPE_U2F, MgmtSecondFactorType.SECONDFACTORTYPE_OTP] :
this.serviceType === PolicyComponentServiceType.ADMIN ?
[AdminSecondFactorType.SECONDFACTORTYPE_OTP, AdminSecondFactorType.SECONDFACTORTYPE_U2F] :
[];
} }
this.mfas.forEach(mfa => { this.mfas.forEach(mfa => {
@@ -128,58 +116,57 @@ export class MfaTableComponent implements OnInit {
} }
}); });
const dialogRef = this.dialog.open(DialogAddTypeComponent, { // const dialogRef = this.dialog.open(DialogAddTypeComponent, {
data: { // data: {
title: 'MFA.CREATE.TITLE', // title: 'MFA.CREATE.TITLE',
desc: 'MFA.CREATE.DESCRIPTION', // desc: 'MFA.CREATE.DESCRIPTION',
componentType: this.componentType, // componentType: this.componentType,
types: selection, // types: selection,
}, // },
width: '400px', // width: '400px',
}); // });
dialogRef.afterClosed().subscribe((mfaType: AdminMultiFactorType | MgmtMultiFactorType | // dialogRef.afterClosed().subscribe((mfaType: ) => {
AdminSecondFactorType | MgmtSecondFactorType) => { // if (mfaType) {
if (mfaType) { // if (this.serviceType === PolicyComponentServiceType.MGMT) {
if (this.serviceType === PolicyComponentServiceType.MGMT) { // if (this.componentType === LoginMethodComponentType.MultiFactor) {
if (this.componentType === LoginMethodComponentType.MultiFactor) { // const req = new MgmtAddMultiFactorToLoginPolicyRequest();
const req = new MgmtMultiFactor(); // req.setType(mfaType as MultiFactorType);
req.setMultiFactor(mfaType as MgmtMultiFactorType); // (this.service as ManagementService).addMultiFactorToLoginPolicy(req).then(() => {
(this.service as ManagementService).AddMultiFactorToLoginPolicy(req).then(() => { // this.refreshPageAfterTimout(2000);
this.refreshPageAfterTimout(2000); // }).catch(error => {
}).catch(error => { // this.toast.showError(error);
this.toast.showError(error); // });
}); // } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { // const req = new MgmtAddSecondFactorToLoginPolicyRequest();
const req = new MgmtSecondFactor(); // req.setType(mfaType as SecondFactorType);
req.setSecondFactor(mfaType as MgmtSecondFactorType); // (this.service as ManagementService).addSecondFactorToLoginPolicy(req).then(() => {
(this.service as ManagementService).AddSecondFactorToLoginPolicy(req).then(() => { // this.refreshPageAfterTimout(2000);
this.refreshPageAfterTimout(2000); // }).catch(error => {
}).catch(error => { // this.toast.showError(error);
this.toast.showError(error); // });
}); // }
} // } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { // if (this.componentType === LoginMethodComponentType.MultiFactor) {
if (this.componentType === LoginMethodComponentType.MultiFactor) { // const req = new AdminAddMultiFactorToLoginPolicyRequest();
const req = new AdminMultiFactor(); // req.setType(mfaType as MultiFactorType);
req.setMultiFactor(mfaType as AdminMultiFactorType); // (this.service as AdminService).addMultiFactorToLoginPolicy(req).then(() => {
(this.service as AdminService).addMultiFactorToDefaultLoginPolicy(req).then(() => { // this.refreshPageAfterTimout(2000);
this.refreshPageAfterTimout(2000); // }).catch(error => {
}).catch(error => { // this.toast.showError(error);
this.toast.showError(error); // });
}); // } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { // const req = new AdminAddSecondFactorToLoginPolicyRequest();
const req = new AdminSecondFactor(); // req.setType(mfaType as SecondFactorType);
req.setSecondFactor(mfaType as AdminSecondFactorType); // (this.service as AdminService).addSecondFactorToLoginPolicy(req).then(() => {
(this.service as AdminService).AddSecondFactorToDefaultLoginPolicy(req).then(() => { // this.refreshPageAfterTimout(2000);
this.refreshPageAfterTimout(2000); // }).catch(error => {
}).catch(error => { // this.toast.showError(error);
this.toast.showError(error); // });
}); // }
} // }
} // }
} // });
});
} }
private async getData(): Promise<void> { private async getData(): Promise<void> {
@@ -187,16 +174,16 @@ export class MfaTableComponent implements OnInit {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
if (this.componentType === LoginMethodComponentType.MultiFactor) { if (this.componentType === LoginMethodComponentType.MultiFactor) {
(this.service as ManagementService).GetLoginPolicyMultiFactors().then(resp => { (this.service as ManagementService).listLoginPolicyMultiFactors().then(resp => {
this.mfas = resp.toObject().multiFactorsList; this.mfas = resp.resultList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
this.loadingSubject.next(false); this.loadingSubject.next(false);
}); });
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
(this.service as ManagementService).GetLoginPolicySecondFactors().then(resp => { (this.service as ManagementService).listLoginPolicySecondFactors().then(resp => {
this.mfas = resp.toObject().secondFactorsList; this.mfas = resp.resultList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -205,16 +192,16 @@ export class MfaTableComponent implements OnInit {
} }
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
if (this.componentType === LoginMethodComponentType.MultiFactor) { if (this.componentType === LoginMethodComponentType.MultiFactor) {
(this.service as AdminService).getDefaultLoginPolicyMultiFactors().then(resp => { (this.service as AdminService).listLoginPolicyMultiFactors().then(resp => {
this.mfas = resp.toObject().multiFactorsList; this.mfas = resp.resultList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
this.loadingSubject.next(false); this.loadingSubject.next(false);
}); });
} else if (this.componentType === LoginMethodComponentType.SecondFactor) { } else if (this.componentType === LoginMethodComponentType.SecondFactor) {
(this.service as AdminService).GetDefaultLoginPolicySecondFactors().then(resp => { (this.service as AdminService).listLoginPolicySecondFactors().then(resp => {
this.mfas = resp.toObject().secondFactorsList; this.mfas = resp.resultList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/management_pb'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
@Component({ @Component({
selector: 'app-password-complexity-view', selector: 'app-password-complexity-view',

View File

@@ -1,12 +1,13 @@
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { DefaultLabelPolicyUpdate, DefaultLabelPolicyView } from 'src/app/proto/generated/admin_pb'; import { GetLabelPolicyResponse, UpdateLabelPolicyRequest } from 'src/app/proto/generated/zitadel/admin_pb';
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
import { IAM_COMPLEXITY_LINK, IAM_LABEL_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK } from '../../policy-grid/policy-links';
import { CnslLinks } from '../../links/links.component';
import { IAM_COMPLEXITY_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK } from '../../policy-grid/policy-links';
import { PolicyComponentServiceType } from '../policy-component-types.enum'; import { PolicyComponentServiceType } from '../policy-component-types.enum';
@@ -16,7 +17,7 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum';
styleUrls: ['./label-policy.component.scss'], styleUrls: ['./label-policy.component.scss'],
}) })
export class LabelPolicyComponent implements OnDestroy { export class LabelPolicyComponent implements OnDestroy {
public labelData!: DefaultLabelPolicyView.AsObject; public labelData!: LabelPolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
@@ -33,8 +34,8 @@ export class LabelPolicyComponent implements OnDestroy {
) { ) {
this.route.params.subscribe(() => { this.route.params.subscribe(() => {
this.getData().then(data => { this.getData().then(data => {
if (data) { if (data?.policy) {
this.labelData = data.toObject(); this.labelData = data.policy;
} }
}); });
}); });
@@ -44,15 +45,15 @@ export class LabelPolicyComponent implements OnDestroy {
this.sub.unsubscribe(); this.sub.unsubscribe();
} }
private async getData(): Promise<DefaultLabelPolicyView> { private async getData(): Promise<GetLabelPolicyResponse.AsObject> {
return this.adminService.GetDefaultLabelPolicy(); return this.adminService.getLabelPolicy();
} }
public savePolicy(): void { public savePolicy(): void {
const req = new DefaultLabelPolicyUpdate(); const req = new UpdateLabelPolicyRequest();
req.setPrimaryColor(this.labelData.primaryColor); req.setPrimaryColor(this.labelData.primaryColor);
req.setSecondaryColor(this.labelData.secondaryColor); req.setSecondaryColor(this.labelData.secondaryColor);
this.adminService.UpdateDefaultLabelPolicy(req).then(() => { this.adminService.updateLabelPolicy(req).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true); this.toast.showInfo('POLICY.TOAST.SET', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -1,14 +1,7 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { IdpView as AdminIdpView } from 'src/app/proto/generated/admin_pb'; import { IDP, IDPOwnerType, IDPOwnerTypeQuery } from 'src/app/proto/generated/zitadel/idp_pb';
import { import { IDPQuery } from 'src/app/proto/generated/zitadel/management_pb';
Idp,
IdpProviderType,
IdpSearchKey,
IdpSearchQuery,
IdpView as MgmtIdpView,
SearchMethod,
} from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -23,15 +16,15 @@ export class AddIdpDialogComponent {
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public idpType!: IdpProviderType; public idpType!: IDPOwnerType;
public idpTypes: IdpProviderType[] = [ public idpTypes: IDPOwnerType[] = [
IdpProviderType.IDPPROVIDERTYPE_SYSTEM, IDPOwnerType.IDP_OWNER_TYPE_SYSTEM,
IdpProviderType.IDPPROVIDERTYPE_ORG, IDPOwnerType.IDP_OWNER_TYPE_ORG,
]; ];
public idp: Idp.AsObject | undefined = undefined; public idp: IDP.AsObject | undefined = undefined;
public availableIdps: Array<AdminIdpView.AsObject | MgmtIdpView.AsObject> | string[] = []; public availableIdps: Array<IDP.AsObject[] | IDP.AsObject> | string[] = [];
public IdpProviderType: any = IdpProviderType; public IdpProviderType: any = IDPOwnerType;
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,
@@ -43,10 +36,10 @@ export class AddIdpDialogComponent {
this.serviceType = data.serviceType; this.serviceType = data.serviceType;
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
this.idpType = IdpProviderType.IDPPROVIDERTYPE_ORG; this.idpType = IDPOwnerType.IDP_OWNER_TYPE_ORG;
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
this.idpType = IdpProviderType.IDPPROVIDERTYPE_SYSTEM; this.idpType = IDPOwnerType.IDP_OWNER_TYPE_SYSTEM;
break; break;
} }
} }
@@ -57,17 +50,17 @@ export class AddIdpDialogComponent {
public loadIdps(): void { public loadIdps(): void {
this.idp = undefined; this.idp = undefined;
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
const query: IdpSearchQuery = new IdpSearchQuery(); const query: IDPQuery = new IDPQuery();
query.setKey(IdpSearchKey.IDPSEARCHKEY_PROVIDER_TYPE); const idpOTQ: IDPOwnerTypeQuery = new IDPOwnerTypeQuery();
query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); idpOTQ.setOwnerType(this.idpType);
query.setValue(this.idpType.toString()); query.setOwnerTypeQuery(idpOTQ);
this.mgmtService.SearchIdps(undefined, undefined, [query]).then(idps => { this.mgmtService.listOrgIDPs(undefined, undefined, [query]).then(resp => {
this.availableIdps = idps.toObject().resultList; this.availableIdps = resp.resultList;
}); });
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
this.adminService.SearchIdps().then(idps => { this.adminService.listIDPs().then(resp => {
this.availableIdps = idps.toObject().resultList; this.availableIdps = resp.resultList;
}); });
} }
} }

View File

@@ -102,7 +102,7 @@
</button> </button>
<div class="line"> <div class="line">
<img src="../../../assets/images/google.png" <img src="../../../assets/images/google.png"
*ngIf="idp.stylingType == IdpStylingType.IDPSTYLINGTYPE_GOOGLE" alt="google" /> *ngIf="idp.stylingType == IDPStylingType.STYLING_TYPE_GOOGLE" alt="google" />
<div> <div>
<span class="name">{{idp.name}}</span> <span class="name">{{idp.name}}</span>
<span class="meta-info">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.type | translate }}</span> <span class="meta-info">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.type | translate }}</span>

View File

@@ -5,29 +5,28 @@ import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { LoginMethodComponentType } from 'src/app/modules/mfa-table/mfa-table.component'; import { LoginMethodComponentType } from 'src/app/modules/mfa-table/mfa-table.component';
import { import {
DefaultLoginPolicy, GetLoginPolicyResponse as AdminGetLoginPolicyResponse,
DefaultLoginPolicyRequest, UpdateLoginPolicyRequest,
DefaultLoginPolicyView, UpdateLoginPolicyResponse,
IdpProviderView as AdminIdpProviderView, } from 'src/app/proto/generated/zitadel/admin_pb';
IdpStylingType, import { IDP, IDPLoginPolicyLink, IDPStylingType } from 'src/app/proto/generated/zitadel/idp_pb';
IdpView as AdminIdpView,
PasswordlessType as AdminPasswordlessType,
} from 'src/app/proto/generated/admin_pb';
import { import {
IdpProviderType, AddCustomLoginPolicyRequest,
IdpProviderView as MgmtIdpProviderView, GetLoginPolicyResponse as MgmtGetLoginPolicyResponse,
IdpView as MgmtIdpView, } from 'src/app/proto/generated/zitadel/management_pb';
LoginPolicy, import { LoginPolicy, PasswordlessType } from 'src/app/proto/generated/zitadel/policy_pb';
LoginPolicyRequest,
LoginPolicyView,
PasswordlessType as MgmtPasswordlessType,
} from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
import { IAM_COMPLEXITY_LINK, IAM_LABEL_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK, ORG_COMPLEXITY_LINK, ORG_IAM_POLICY_LINK } from '../../policy-grid/policy-links';
import { CnslLinks } from '../../links/links.component';
import {
IAM_COMPLEXITY_LINK,
IAM_LABEL_LINK,
IAM_POLICY_LINK,
ORG_COMPLEXITY_LINK,
ORG_IAM_POLICY_LINK,
} from '../../policy-grid/policy-links';
import { PolicyComponentServiceType } from '../policy-component-types.enum'; import { PolicyComponentServiceType } from '../policy-component-types.enum';
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component'; import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
@@ -38,19 +37,19 @@ import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component
}) })
export class LoginPolicyComponent implements OnDestroy { export class LoginPolicyComponent implements OnDestroy {
public LoginMethodComponentType: any = LoginMethodComponentType; public LoginMethodComponentType: any = LoginMethodComponentType;
public passwordlessTypes: Array<AdminPasswordlessType | MgmtPasswordlessType> = []; public passwordlessTypes: Array<PasswordlessType> = [];
public loginData!: LoginPolicyView.AsObject | DefaultLoginPolicyView.AsObject; public loginData!: LoginPolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
public service!: ManagementService | AdminService; public service!: ManagementService | AdminService;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public idps: MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[] = []; public idps: IDPLoginPolicyLink.AsObject[] = [];
public loading: boolean = false; public loading: boolean = false;
public disabled: boolean = true; public disabled: boolean = true;
public IdpStylingType: any = IdpStylingType; public IDPStylingType: any = IDPStylingType;
public nextLinks: CnslLinks[] = []; public nextLinks: CnslLinks[] = [];
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@@ -63,8 +62,10 @@ export class LoginPolicyComponent implements OnDestroy {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>); this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.passwordlessTypes = [MgmtPasswordlessType.PASSWORDLESSTYPE_ALLOWED, this.passwordlessTypes = [
MgmtPasswordlessType.PASSWORDLESSTYPE_NOT_ALLOWED]; PasswordlessType.PASSWORDLESS_TYPE_ALLOWED,
PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED,
];
this.nextLinks = [ this.nextLinks = [
ORG_COMPLEXITY_LINK, ORG_COMPLEXITY_LINK,
ORG_IAM_POLICY_LINK, ORG_IAM_POLICY_LINK,
@@ -72,8 +73,10 @@ export class LoginPolicyComponent implements OnDestroy {
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>); this.service = this.injector.get(AdminService as Type<AdminService>);
this.passwordlessTypes = [AdminPasswordlessType.PASSWORDLESSTYPE_ALLOWED, this.passwordlessTypes = [
AdminPasswordlessType.PASSWORDLESSTYPE_NOT_ALLOWED]; PasswordlessType.PASSWORDLESS_TYPE_ALLOWED,
PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED,
];
this.nextLinks = [ this.nextLinks = [
IAM_COMPLEXITY_LINK, IAM_COMPLEXITY_LINK,
IAM_POLICY_LINK, IAM_POLICY_LINK,
@@ -89,15 +92,15 @@ export class LoginPolicyComponent implements OnDestroy {
} }
private fetchData(): void { private fetchData(): void {
this.getData().then(data => { this.getData().then(resp => {
if (data) { if (resp.policy) {
this.loginData = data.toObject(); this.loginData = resp.policy;
this.loading = false; this.loading = false;
this.disabled = ((this.loginData as LoginPolicyView.AsObject)?.pb_default) ?? false; this.disabled = ((this.loginData as LoginPolicy.AsObject)?.isDefault) ?? false;
} }
}); });
this.getIdps().then(idps => { this.getIdps().then(resp => {
this.idps = idps; this.idps = resp;
}); });
} }
@@ -106,48 +109,48 @@ export class LoginPolicyComponent implements OnDestroy {
} }
private async getData(): private async getData():
Promise<LoginPolicyView | DefaultLoginPolicyView> { Promise<AdminGetLoginPolicyResponse.AsObject | MgmtGetLoginPolicyResponse.AsObject> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).GetLoginPolicy(); return (this.service as ManagementService).getLoginPolicy();
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).GetDefaultLoginPolicy(); return (this.service as AdminService).getLoginPolicy();
} }
} }
private async getIdps(): Promise<MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[]> { private async getIdps(): Promise<IDPLoginPolicyLink.AsObject[]> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).GetLoginPolicyIdpProviders() return (this.service as ManagementService).listLoginPolicyIDPs()
.then((providers) => { .then((resp) => {
return providers.toObject().resultList; return resp.resultList;
}); });
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).GetDefaultLoginPolicyIdpProviders() return (this.service as AdminService).listLoginPolicyIDPs()
.then((providers) => { .then((providers) => {
return providers.toObject().resultList; return providers.resultList;
}); });
} }
} }
private async updateData(): private async updateData():
Promise<LoginPolicy | DefaultLoginPolicy> { Promise<UpdateLoginPolicyResponse.AsObject> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
const mgmtreq = new LoginPolicyRequest(); const mgmtreq = new AddCustomLoginPolicyRequest();
mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp); mgmtreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
mgmtreq.setAllowRegister(this.loginData.allowRegister); mgmtreq.setAllowRegister(this.loginData.allowRegister);
mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
mgmtreq.setForceMfa(this.loginData.forceMfa); mgmtreq.setForceMfa(this.loginData.forceMfa);
mgmtreq.setPasswordlessType(this.loginData.passwordlessType); mgmtreq.setPasswordlessType(this.loginData.passwordlessType);
// console.log(mgmtreq.toObject()); // console.log(mgmtreq.toObject());
if ((this.loginData as LoginPolicyView.AsObject).pb_default) { if ((this.loginData as LoginPolicy.AsObject).isDefault) {
return (this.service as ManagementService).CreateLoginPolicy(mgmtreq); return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq);
} else { } else {
return (this.service as ManagementService).UpdateLoginPolicy(mgmtreq); return (this.service as ManagementService).updateCustomLoginPolicy(mgmtreq);
} }
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
const adminreq = new DefaultLoginPolicyRequest(); const adminreq = new UpdateLoginPolicyRequest();
adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp); adminreq.setAllowExternalIdp(this.loginData.allowExternalIdp);
adminreq.setAllowRegister(this.loginData.allowRegister); adminreq.setAllowRegister(this.loginData.allowRegister);
adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); adminreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword);
@@ -156,7 +159,7 @@ export class LoginPolicyComponent implements OnDestroy {
// console.log(adminreq.toObject()); // console.log(adminreq.toObject());
return (this.service as AdminService).UpdateDefaultLoginPolicy(adminreq); return (this.service as AdminService).updateLoginPolicy(adminreq);
} }
} }
@@ -174,7 +177,7 @@ export class LoginPolicyComponent implements OnDestroy {
public removePolicy(): void { public removePolicy(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService).RemoveLoginPolicy().then(() => { (this.service as ManagementService).resetLoginPolicyToDefault().then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true); this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
this.loading = true; this.loading = true;
setTimeout(() => { setTimeout(() => {
@@ -195,8 +198,8 @@ export class LoginPolicyComponent implements OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp && resp.idp && resp.type) { if (resp && resp.idp) {
this.addIdp(resp.idp, resp.type).then(() => { this.addIdp(resp.idp).then(() => {
this.loading = true; this.loading = true;
setTimeout(() => { setTimeout(() => {
this.fetchData(); this.fetchData();
@@ -208,29 +211,28 @@ export class LoginPolicyComponent implements OnDestroy {
}); });
} }
private addIdp(idp: AdminIdpView.AsObject | MgmtIdpView.AsObject, private addIdp(idp: IDP.AsObject | IDP.AsObject): Promise<any> {
type: IdpProviderType = IdpProviderType.IDPPROVIDERTYPE_SYSTEM): Promise<any> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).addIdpProviderToLoginPolicy(idp.id, type); return (this.service as ManagementService).addIDPToLoginPolicy(idp.id);
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).AddIdpProviderToDefaultLoginPolicy(idp.id); return (this.service as AdminService).addIDPToLoginPolicy(idp.id);
} }
} }
public removeIdp(idp: AdminIdpProviderView.AsObject | MgmtIdpProviderView.AsObject): void { public removeIdp(idp: IDPLoginPolicyLink.AsObject): void {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
(this.service as ManagementService).RemoveIdpProviderFromLoginPolicy(idp.idpConfigId).then(() => { (this.service as ManagementService).removeIDPFromLoginPolicy(idp.idpId).then(() => {
const index = (this.idps as MgmtIdpProviderView.AsObject[]).findIndex(temp => temp === idp); const index = this.idps.findIndex(temp => temp === idp);
if (index > -1) { if (index > -1) {
this.idps.splice(index, 1); this.idps.splice(index, 1);
} }
}); });
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
(this.service as AdminService).RemoveIdpProviderFromDefaultLoginPolicy(idp.idpConfigId).then(() => { (this.service as AdminService).removeIDPFromLoginPolicy(idp.idpId).then(() => {
const index = (this.idps as AdminIdpProviderView.AsObject[]).findIndex(temp => temp === idp); const index = this.idps.findIndex(temp => temp === idp);
if (index > -1) { if (index > -1) {
this.idps.splice(index, 1); this.idps.splice(index, 1);
} }
@@ -241,7 +243,7 @@ export class LoginPolicyComponent implements OnDestroy {
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.loginData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.loginData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.loginData as LoginPolicyView.AsObject).pb_default; return (this.loginData as LoginPolicy.AsObject).isDefault;
} else { } else {
return false; return false;
} }

View File

@@ -2,16 +2,23 @@ import { Component, Injector, Input, OnDestroy, Type } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { OrgIamPolicyView as AdminOrgIamPolicyView } from 'src/app/proto/generated/admin_pb'; import { GetCustomOrgIAMPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { Org } from 'src/app/proto/generated/auth_pb'; import { GetOrgIAMPolicyResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { OrgIamPolicyView as MgmtOrgIamPolicyView } from 'src/app/proto/generated/management_pb'; import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { OrgIAMPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { StorageService } from 'src/app/services/storage.service'; import { StorageService } from 'src/app/services/storage.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
import { IAM_COMPLEXITY_LINK, IAM_LABEL_LINK, IAM_LOGIN_POLICY_LINK, ORG_LOGIN_POLICY_LINK, ORG_COMPLEXITY_LINK } from '../../policy-grid/policy-links';
import { CnslLinks } from '../../links/links.component';
import {
IAM_COMPLEXITY_LINK,
IAM_LABEL_LINK,
IAM_LOGIN_POLICY_LINK,
ORG_COMPLEXITY_LINK,
ORG_LOGIN_POLICY_LINK,
} from '../../policy-grid/policy-links';
import { PolicyComponentServiceType } from '../policy-component-types.enum'; import { PolicyComponentServiceType } from '../policy-component-types.enum';
@Component({ @Component({
@@ -24,7 +31,7 @@ export class OrgIamPolicyComponent implements OnDestroy {
private managementService!: ManagementService; private managementService!: ManagementService;
public serviceType!: PolicyComponentServiceType; public serviceType!: PolicyComponentServiceType;
public iamData!: AdminOrgIamPolicyView.AsObject | MgmtOrgIamPolicyView.AsObject; public iamData!: OrgIAMPolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
private org!: Org.AsObject; private org!: Org.AsObject;
@@ -68,20 +75,20 @@ export class OrgIamPolicyComponent implements OnDestroy {
} }
public fetchData(): void { public fetchData(): void {
this.getData().then(data => { this.getData().then(resp => {
if (data) { if (resp?.policy) {
this.iamData = data.toObject(); this.iamData = resp.policy;
} }
}); });
} }
private async getData(): Promise<AdminOrgIamPolicyView | MgmtOrgIamPolicyView | undefined> { private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | undefined> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return this.managementService.GetMyOrgIamPolicy(); return this.managementService.getOrgIAMPolicy();
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
if (this.org?.id) { if (this.org?.id) {
return this.adminService.GetOrgIamPolicy(this.org.id); return this.adminService.getCustomOrgIAMPolicy(this.org.id);
} }
break; break;
} }
@@ -90,8 +97,8 @@ export class OrgIamPolicyComponent implements OnDestroy {
public savePolicy(): void { public savePolicy(): void {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
if ((this.iamData as MgmtOrgIamPolicyView.AsObject).pb_default) { if ((this.iamData as OrgIAMPolicy.AsObject).isDefault) {
this.adminService.CreateOrgIamPolicy( this.adminService.addCustomOrgIAMPolicy(
this.org.id, this.org.id,
this.iamData.userLoginMustBeDomain, this.iamData.userLoginMustBeDomain,
).then(() => { ).then(() => {
@@ -101,7 +108,7 @@ export class OrgIamPolicyComponent implements OnDestroy {
}); });
break; break;
} else { } else {
this.adminService.UpdateOrgIamPolicy( this.adminService.updateCustomOrgIAMPolicy(
this.org.id, this.org.id,
this.iamData.userLoginMustBeDomain, this.iamData.userLoginMustBeDomain,
).then(() => { ).then(() => {
@@ -113,8 +120,7 @@ export class OrgIamPolicyComponent implements OnDestroy {
} }
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
// update Default org iam policy? // update Default org iam policy?
this.adminService.UpdateOrgIamPolicy( this.adminService.updateOrgIAMPolicy(
this.org.id,
this.iamData.userLoginMustBeDomain, this.iamData.userLoginMustBeDomain,
).then(() => { ).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true); this.toast.showInfo('POLICY.TOAST.SET', true);
@@ -127,7 +133,7 @@ export class OrgIamPolicyComponent implements OnDestroy {
public removePolicy(): void { public removePolicy(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
this.adminService.RemoveOrgIamPolicy(this.org.id).then(() => { this.adminService.resetCustomOrgIAMPolicyToDefault(this.org.id).then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true); this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
setTimeout(() => { setTimeout(() => {
this.fetchData(); this.fetchData();
@@ -140,7 +146,7 @@ export class OrgIamPolicyComponent implements OnDestroy {
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.iamData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.iamData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.iamData as MgmtOrgIamPolicyView.AsObject).pb_default; return (this.iamData as OrgIAMPolicy.AsObject).isDefault;
} else { } else {
return false; return false;
} }

View File

@@ -2,8 +2,11 @@ import { Component, Injector, OnDestroy, Type } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { DefaultPasswordAgePolicyView } from 'src/app/proto/generated/admin_pb'; import { GetPasswordAgePolicyResponse as AdminGetPasswordAgePolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { PasswordAgePolicyView } from 'src/app/proto/generated/management_pb'; import {
GetPasswordAgePolicyResponse as MgmtGetPasswordAgePolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { PasswordAgePolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -20,7 +23,7 @@ export class PasswordAgePolicyComponent implements OnDestroy {
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public service!: AdminService | ManagementService; public service!: AdminService | ManagementService;
public ageData!: PasswordAgePolicyView.AsObject | DefaultPasswordAgePolicyView.AsObject; public ageData!: PasswordAgePolicy.AsObject | PasswordAgePolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
@@ -43,9 +46,9 @@ export class PasswordAgePolicyComponent implements OnDestroy {
return this.route.params; return this.route.params;
})).subscribe(() => { })).subscribe(() => {
this.getData().then(data => { this.getData().then(resp => {
if (data) { if (resp.policy) {
this.ageData = data.toObject(); this.ageData = resp.policy;
} }
}); });
}); });
@@ -56,19 +59,19 @@ export class PasswordAgePolicyComponent implements OnDestroy {
} }
private async getData(): private async getData():
Promise<PasswordAgePolicyView | DefaultPasswordAgePolicyView> { Promise<MgmtGetPasswordAgePolicyResponse.AsObject | AdminGetPasswordAgePolicyResponse.AsObject> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).GetPasswordAgePolicy(); return (this.service as ManagementService).getPasswordAgePolicy();
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).GetDefaultPasswordAgePolicy(); return (this.service as AdminService).getPasswordAgePolicy();
} }
} }
public removePolicy(): void { public removePolicy(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService).RemovePasswordAgePolicy().then(() => { (this.service as ManagementService).resetPasswordAgePolicyToDefault().then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true); this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
setTimeout(() => { setTimeout(() => {
this.getData(); this.getData();
@@ -106,8 +109,8 @@ export class PasswordAgePolicyComponent implements OnDestroy {
public savePolicy(): void { public savePolicy(): void {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
if ((this.ageData as PasswordAgePolicyView.AsObject).pb_default) { if (this.ageData.isDefault) {
(this.service as ManagementService).CreatePasswordAgePolicy( (this.service as ManagementService).addCustomPasswordAgePolicy(
this.ageData.maxAgeDays, this.ageData.maxAgeDays,
this.ageData.expireWarnDays, this.ageData.expireWarnDays,
).then(() => { ).then(() => {
@@ -116,7 +119,7 @@ export class PasswordAgePolicyComponent implements OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} else { } else {
(this.service as ManagementService).UpdatePasswordAgePolicy( (this.service as ManagementService).updateCustomPasswordAgePolicy(
this.ageData.maxAgeDays, this.ageData.maxAgeDays,
this.ageData.expireWarnDays, this.ageData.expireWarnDays,
).then(() => { ).then(() => {
@@ -127,7 +130,7 @@ export class PasswordAgePolicyComponent implements OnDestroy {
} }
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
(this.service as AdminService).UpdateDefaultPasswordAgePolicy( (this.service as AdminService).updatePasswordAgePolicy(
this.ageData.maxAgeDays, this.ageData.maxAgeDays,
this.ageData.expireWarnDays, this.ageData.expireWarnDays,
).then(() => { ).then(() => {
@@ -141,7 +144,7 @@ export class PasswordAgePolicyComponent implements OnDestroy {
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.ageData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.ageData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.ageData as PasswordAgePolicyView.AsObject).pb_default; return (this.ageData as PasswordAgePolicy.AsObject).isDefault;
} else { } else {
return false; return false;
} }

View File

@@ -2,14 +2,25 @@ import { Component, Injector, OnDestroy, Type } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { DefaultPasswordComplexityPolicy } from 'src/app/proto/generated/admin_pb'; import {
import { PasswordComplexityPolicyView } from 'src/app/proto/generated/management_pb'; GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
import { IAM_LABEL_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK, ORG_IAM_POLICY_LINK, ORG_LOGIN_POLICY_LINK } from '../../policy-grid/policy-links';
import { CnslLinks } from '../../links/links.component';
import {
IAM_LABEL_LINK,
IAM_LOGIN_POLICY_LINK,
IAM_POLICY_LINK,
ORG_IAM_POLICY_LINK,
ORG_LOGIN_POLICY_LINK,
} from '../../policy-grid/policy-links';
import { PolicyComponentServiceType } from '../policy-component-types.enum'; import { PolicyComponentServiceType } from '../policy-component-types.enum';
@Component({ @Component({
@@ -21,7 +32,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public service!: ManagementService | AdminService; public service!: ManagementService | AdminService;
public complexityData!: PasswordComplexityPolicyView.AsObject | DefaultPasswordComplexityPolicy.AsObject; public complexityData!: PasswordComplexityPolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
@@ -64,8 +75,8 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
this.loading = true; this.loading = true;
this.getData().then(data => { this.getData().then(data => {
if (data) { if (data.policy) {
this.complexityData = data.toObject(); this.complexityData = data.policy;
this.loading = false; this.loading = false;
} }
}); });
@@ -76,18 +87,18 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
} }
private async getData(): private async getData():
Promise<PasswordComplexityPolicyView | DefaultPasswordComplexityPolicy> { Promise<MgmtGetPasswordComplexityPolicyResponse.AsObject | AdminGetPasswordComplexityPolicyResponse.AsObject> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).GetPasswordComplexityPolicy(); return (this.service as ManagementService).getPasswordComplexityPolicy();
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).GetDefaultPasswordComplexityPolicy(); return (this.service as AdminService).getPasswordComplexityPolicy();
} }
} }
public removePolicy(): void { public removePolicy(): void {
if (this.service instanceof ManagementService) { if (this.service instanceof ManagementService) {
this.service.removePasswordComplexityPolicy().then(() => { this.service.resetPasswordComplexityPolicyToDefault().then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true); this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
setTimeout(() => { setTimeout(() => {
this.fetchData(); this.fetchData();
@@ -113,8 +124,8 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
public savePolicy(): void { public savePolicy(): void {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
if ((this.complexityData as PasswordComplexityPolicyView.AsObject).pb_default) { if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) {
(this.service as ManagementService).CreatePasswordComplexityPolicy( (this.service as ManagementService).addCustomPasswordComplexityPolicy(
this.complexityData.hasLowercase, this.complexityData.hasLowercase,
this.complexityData.hasUppercase, this.complexityData.hasUppercase,
@@ -127,7 +138,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} else { } else {
(this.service as ManagementService).UpdatePasswordComplexityPolicy( (this.service as ManagementService).updateCustomPasswordComplexityPolicy(
this.complexityData.hasLowercase, this.complexityData.hasLowercase,
this.complexityData.hasUppercase, this.complexityData.hasUppercase,
this.complexityData.hasNumber, this.complexityData.hasNumber,
@@ -141,7 +152,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
} }
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
(this.service as AdminService).UpdateDefaultPasswordComplexityPolicy( (this.service as AdminService).updatePasswordComplexityPolicy(
this.complexityData.hasLowercase, this.complexityData.hasLowercase,
this.complexityData.hasUppercase, this.complexityData.hasUppercase,
this.complexityData.hasNumber, this.complexityData.hasNumber,
@@ -158,7 +169,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.complexityData as PasswordComplexityPolicyView.AsObject).pb_default; return (this.complexityData as PasswordComplexityPolicy.AsObject).isDefault;
} else { } else {
return false; return false;
} }

View File

@@ -3,8 +3,13 @@ import { FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { DefaultPasswordLockoutPolicyView } from 'src/app/proto/generated/admin_pb'; import {
import { PasswordLockoutPolicyView } from 'src/app/proto/generated/management_pb'; GetPasswordLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetPasswordLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { PasswordLockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -22,7 +27,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
public lockoutForm!: FormGroup; public lockoutForm!: FormGroup;
public lockoutData!: PasswordLockoutPolicyView.AsObject; public lockoutData!: PasswordLockoutPolicy.AsObject;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
@@ -54,25 +59,25 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
} }
private fetchData(): void { private fetchData(): void {
this.getData().then(data => { this.getData().then(resp => {
if (data) { if (resp.policy) {
this.lockoutData = data.toObject() as PasswordLockoutPolicyView.AsObject; this.lockoutData = resp.policy;
} }
}); });
} }
private getData(): Promise<PasswordLockoutPolicyView | DefaultPasswordLockoutPolicyView> { private getData(): Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).GetPasswordLockoutPolicy(); return (this.service as ManagementService).getPasswordLockoutPolicy();
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).GetDefaultPasswordLockoutPolicy(); return (this.service as AdminService).getPasswordLockoutPolicy();
} }
} }
public removePolicy(): void { public removePolicy(): void {
if (this.service instanceof ManagementService) { if (this.service instanceof ManagementService) {
this.service.RemovePasswordLockoutPolicy().then(() => { this.service.resetPasswordLockoutPolicyToDefault().then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true); this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
this.fetchData(); this.fetchData();
}).catch(error => { }).catch(error => {
@@ -96,7 +101,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
public savePolicy(): void { public savePolicy(): void {
let promise: Promise<any>; let promise: Promise<any>;
if (this.service instanceof AdminService) { if (this.service instanceof AdminService) {
promise = this.service.UpdateDefaultPasswordLockoutPolicy( promise = this.service.updatePasswordLockoutPolicy(
this.lockoutData.maxAttempts, this.lockoutData.maxAttempts,
this.lockoutData.showLockoutFailure, this.lockoutData.showLockoutFailure,
).then(() => { ).then(() => {
@@ -105,8 +110,8 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} else { } else {
if ((this.lockoutData as PasswordLockoutPolicyView.AsObject).pb_default) { if ((this.lockoutData as PasswordLockoutPolicy.AsObject).isDefault) {
promise = this.service.CreatePasswordLockoutPolicy( promise = this.service.addCustomPasswordLockoutPolicy(
this.lockoutData.maxAttempts, this.lockoutData.maxAttempts,
this.lockoutData.showLockoutFailure, this.lockoutData.showLockoutFailure,
).then(() => { ).then(() => {
@@ -115,7 +120,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} else { } else {
promise = this.service.UpdatePasswordLockoutPolicy( promise = this.service.updateCustomPasswordLockoutPolicy(
this.lockoutData.maxAttempts, this.lockoutData.maxAttempts,
this.lockoutData.showLockoutFailure, this.lockoutData.showLockoutFailure,
).then(() => { ).then(() => {
@@ -129,7 +134,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) {
return (this.lockoutData as PasswordLockoutPolicyView.AsObject).pb_default; return (this.lockoutData as PasswordLockoutPolicy.AsObject).isDefault;
} else { } else {
return false; return false;
} }

View File

@@ -1,9 +1,8 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
import { PasswordComplexityPolicyView as MgmtPasswordComplexityPolicyView } from 'src/app/proto/generated/management_pb'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { DefaultPasswordComplexityPolicyView as AdminPasswordComplexityPolicyView } from 'src/app/proto/generated/admin_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service';
export enum PolicyGridType { export enum PolicyGridType {
ORG, ORG,
@@ -20,18 +19,22 @@ export class PolicyGridComponent implements OnInit {
public PolicyComponentType: any = PolicyComponentType; public PolicyComponentType: any = PolicyComponentType;
public PolicyGridType: any = PolicyGridType; public PolicyGridType: any = PolicyGridType;
public complexityPolicy!: MgmtPasswordComplexityPolicyView.AsObject | AdminPasswordComplexityPolicyView.AsObject | any; public complexityPolicy!: PasswordComplexityPolicy.AsObject;
constructor(private mgmtService: ManagementService, private adminService: AdminService) { } constructor(private mgmtService: ManagementService, private adminService: AdminService) { }
public ngOnInit(): void { public ngOnInit(): void {
if (this.type == PolicyGridType.ORG) { if (this.type == PolicyGridType.ORG) {
this.mgmtService.GetDefaultPasswordComplexityPolicy().then((policy) => { this.mgmtService.getPasswordComplexityPolicy().then((resp) => {
this.complexityPolicy = policy.toObject(); if (resp.policy) {
this.complexityPolicy = resp.policy;
}
}); });
} else if (this.type == PolicyGridType.IAM) { } else if (this.type == PolicyGridType.IAM) {
this.adminService.GetDefaultPasswordComplexityPolicy().then((policy) => { this.adminService.getPasswordComplexityPolicy().then((resp) => {
this.complexityPolicy = policy.toObject(); if (resp.policy) {
this.complexityPolicy = resp.policy;
}
}); });
} }
} }

View File

@@ -2,19 +2,22 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { ProjectMember, ProjectMemberSearchResponse, ProjectType } from 'src/app/proto/generated/management_pb'; import { ListProjectGrantMembersResponse, ListProjectMembersResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ProjectType } from './project-members.component';
/** /**
* Data source for the ProjectMembers view. This class should * Data source for the ProjectMembers view. This class should
* encapsulate all logic for fetching and manipulating the displayed data * encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering). * (including sorting, pagination, and filtering).
*/ */
export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject> { export class ProjectMembersDataSource extends DataSource<Member.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]); public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -29,21 +32,22 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
this.loadingSubject.next(true); this.loadingSubject.next(true);
const promise: Promise<ProjectMemberSearchResponse> | undefined = const promise: Promise<ListProjectMembersResponse.AsObject> | Promise<ListProjectGrantMembersResponse.AsObject> | undefined =
projectType === ProjectType.PROJECTTYPE_OWNED ? projectType === ProjectType.PROJECTTYPE_OWNED ?
this.mgmtService.SearchProjectMembers(projectId, pageSize, offset) : this.mgmtService.listProjectMembers(projectId, pageSize, offset) :
projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ? projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ?
this.mgmtService.SearchProjectGrantMembers(projectId, this.mgmtService.listProjectGrantMembers(projectId,
grantId, pageSize, offset) : undefined; grantId, pageSize, offset) : undefined;
if (promise) { if (promise) {
from(promise).pipe( from(promise).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); if (resp.details?.totalResult) {
this.totalResult = response.totalResult; this.totalResult = resp.details?.totalResult;
if (response.viewTimestamp) {
this.viewTimestamp = response.viewTimestamp;
} }
return response.resultList; if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details.viewTimestamp;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -59,7 +63,7 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<ProjectMember.AsObject[]> { public connect(): Observable<Member.AsObject[]> {
return this.membersSubject.asObservable(); return this.membersSubject.asObservable();
} }

View File

@@ -1,14 +1,14 @@
<app-detail-layout *ngIf="project" [backRouterLink]="[ '/projects', project?.projectId]" <app-detail-layout *ngIf="project" [backRouterLink]="[ '/projects', (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '']"
title="{{projectName}} {{ 'PROJECT.MEMBER.TITLE' | translate }}" title="{{projectName}} {{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}"> description="{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}">
<app-members-table *ngIf="project" [dataSource]="dataSource" [memberRoleOptions]="memberRoleOptions" <app-members-table *ngIf="project" [dataSource]="dataSource" [memberRoleOptions]="memberRoleOptions"
(updateRoles)="updateRoles($event.member, $event.change)" [factoryLoadFunc]="changePageFactory" (updateRoles)="updateRoles($event.member, $event.change)" [factoryLoadFunc]="changePageFactory"
(changedSelection)="selection = $event" [refreshTrigger]="changePage" (changedSelection)="selection = $event" [refreshTrigger]="changePage"
[canWrite]="['project.member.write$', 'project.member.write:'+ project.projectId] | hasRole | async" [canWrite]="['project.member.write$', 'project.member.write:'+ (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: ''] | hasRole | async"
[canDelete]="['project.member.delete$', 'project.member.delete:'+project.projectId] | hasRole | async" [canDelete]="['project.member.delete$', 'project.member.delete:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: ''] | hasRole | async"
(deleteMember)="removeProjectMember($event)"> (deleteMember)="removeProjectMember($event)">
<ng-template appHasRole selectactions <ng-template appHasRole selectactions
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']"> [appHasRole]="['project.member.delete:' + (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '', 'project.member.delete']">
<button (click)="removeProjectMemberSelection()" color="warn" <button (click)="removeProjectMemberSelection()" color="warn"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" mat-raised-button> matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" mat-raised-button>
<i class="las la-trash"></i> <i class="las la-trash"></i>
@@ -16,7 +16,7 @@
</button> </button>
</ng-template> </ng-template>
<ng-template appHasRole writeactions <ng-template appHasRole writeactions
[appHasRole]="['project.member.write:'+project.projectId,'project.member.write']"> [appHasRole]="['project.member.write:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '','project.member.write']">
<a color="primary" (click)="openAddMember()" color="primary" mat-raised-button> <a color="primary" (click)="openAddMember()" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>

View File

@@ -4,21 +4,19 @@ import { PageEvent } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { import { Member } from 'src/app/proto/generated/zitadel/member_pb';
ProjectGrantMemberView, import { GrantedProject, Project } from 'src/app/proto/generated/zitadel/project_pb';
ProjectGrantView, import { User } from 'src/app/proto/generated/zitadel/user_pb';
ProjectMember,
ProjectMemberView,
ProjectType,
ProjectView,
UserView,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { CreationType, MemberCreateDialogComponent } from '../add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from '../add-member-dialog/member-create-dialog.component';
import { ProjectMembersDataSource } from './project-members-datasource'; import { ProjectMembersDataSource } from './project-members-datasource';
export enum ProjectType {
PROJECTTYPE_OWNED = "OWNED",
PROJECTTYPE_GRANTED = "GRANTED"
}
@Component({ @Component({
selector: 'app-project-members', selector: 'app-project-members',
@@ -27,7 +25,7 @@ import { ProjectMembersDataSource } from './project-members-datasource';
}) })
export class ProjectMembersComponent { export class ProjectMembersComponent {
public INITIALPAGESIZE: number = 25; public INITIALPAGESIZE: number = 25;
public project!: ProjectView.AsObject | ProjectGrantView.AsObject; public project!: Project.AsObject | GrantedProject.AsObject;
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED; public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
public grantId: string = ''; public grantId: string = '';
public projectName: string = ''; public projectName: string = '';
@@ -36,7 +34,9 @@ export class ProjectMembersComponent {
public changePageFactory!: Function; public changePageFactory!: Function;
public changePage: EventEmitter<void> = new EventEmitter(); public changePage: EventEmitter<void> = new EventEmitter();
public selection: Array<ProjectMemberView.AsObject | ProjectGrantMemberView.AsObject> = []; public selection: Array<Member.AsObject> = [];
public ProjectType: any = ProjectType;
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,
private dialog: MatDialog, private dialog: MatDialog,
@@ -50,43 +50,47 @@ export class ProjectMembersComponent {
this.route.params.subscribe(params => { this.route.params.subscribe(params => {
this.grantId = params.grantid; this.grantId = params.grantid;
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
this.mgmtService.GetProjectById(params.projectid).then(project => { this.mgmtService.getProjectByID(params.projectid).then(resp => {
this.project = project.toObject(); if (resp.project) {
this.projectName = this.project.name; this.project = resp.project;
this.dataSource = new ProjectMembersDataSource(this.mgmtService); this.projectName = this.project.name;
this.dataSource.loadMembers(this.project.projectId, this.projectType, 0, this.INITIALPAGESIZE); this.dataSource = new ProjectMembersDataSource(this.mgmtService);
this.dataSource.loadMembers(this.project.id, this.projectType, 0, this.INITIALPAGESIZE);
this.changePageFactory = (event?: PageEvent) => { this.changePageFactory = (event?: PageEvent) => {
return this.dataSource.loadMembers( return this.dataSource.loadMembers(
this.project.projectId, (this.project as Project.AsObject).id,
this.projectType, this.projectType,
event?.pageIndex ?? 0, event?.pageIndex ?? 0,
event?.pageSize ?? this.INITIALPAGESIZE, event?.pageSize ?? this.INITIALPAGESIZE,
this.grantId, this.grantId,
); );
}; };
}
}); });
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { } else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
this.mgmtService.GetGrantedProjectByID(params.projectid, params.grantid).then(project => { this.mgmtService.getGrantedProjectByID(params.projectid, params.grantid).then(resp => {
this.project = project.toObject(); if (resp.grantedProject) {
this.projectName = this.project.projectName; this.project = resp.grantedProject;
this.dataSource = new ProjectMembersDataSource(this.mgmtService); this.projectName = this.project.projectName;
this.dataSource.loadMembers(this.project.projectId, this.dataSource = new ProjectMembersDataSource(this.mgmtService);
this.projectType, this.dataSource.loadMembers(this.project.projectId,
0,
this.INITIALPAGESIZE,
this.grantId,
);
this.changePageFactory = (event?: PageEvent) => {
return this.dataSource.loadMembers(
this.project.projectId,
this.projectType, this.projectType,
event?.pageIndex ?? 0, 0,
event?.pageSize ?? this.INITIALPAGESIZE, this.INITIALPAGESIZE,
this.grantId, this.grantId,
); );
};
this.changePageFactory = (event?: PageEvent) => {
return this.dataSource.loadMembers(
(this.project as GrantedProject.AsObject).projectId,
this.projectType,
event?.pageIndex ?? 0,
event?.pageSize ?? this.INITIALPAGESIZE,
this.grantId,
);
};
}
}); });
} }
}); });
@@ -95,14 +99,14 @@ export class ProjectMembersComponent {
public getRoleOptions(): void { public getRoleOptions(): void {
if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
this.mgmtService.GetProjectGrantMemberRoles().then(resp => { this.mgmtService.listProjectGrantMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.resultList;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { } else if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
this.mgmtService.GetProjectMemberRoles().then(resp => { this.mgmtService.listProjectMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.resultList;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
@@ -112,13 +116,13 @@ export class ProjectMembersComponent {
public removeProjectMemberSelection(): void { public removeProjectMemberSelection(): void {
Promise.all(this.selection.map(member => { Promise.all(this.selection.map(member => {
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
return this.mgmtService.RemoveProjectMember(this.project.projectId, member.userId).then(() => { return this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true); this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { } else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
return this.mgmtService.RemoveProjectGrantMember(this.project.projectId, this.grantId, return this.mgmtService.removeProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId,
member.userId).then(() => { member.userId).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true); this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
}).catch(error => { }).catch(error => {
@@ -132,9 +136,9 @@ export class ProjectMembersComponent {
}); });
} }
public removeProjectMember(member: ProjectMemberView.AsObject | ProjectGrantMemberView.AsObject): void { public removeProjectMember(member: Member.AsObject | Member.AsObject): void {
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
this.mgmtService.RemoveProjectMember(this.project.projectId, member.userId).then(() => { this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId).then(() => {
setTimeout(() => { setTimeout(() => {
this.changePage.emit(); this.changePage.emit();
}, 1000); }, 1000);
@@ -143,7 +147,7 @@ export class ProjectMembersComponent {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { } else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
this.mgmtService.RemoveProjectGrantMember(this.project.projectId, this.grantId, this.mgmtService.removeProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId,
member.userId).then(() => { member.userId).then(() => {
setTimeout(() => { setTimeout(() => {
this.changePage.emit(); this.changePage.emit();
@@ -165,16 +169,16 @@ export class ProjectMembersComponent {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => { Promise.all(users.map(user => {
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
return this.mgmtService.AddProjectMember(this.project.projectId, user.id, roles); return this.mgmtService.addProjectMember((this.project as Project.AsObject).id, user.id, roles);
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { } else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
return this.mgmtService.AddProjectGrantMember(this.project.projectId, this.grantId, return this.mgmtService.addProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId,
user.id, roles); user.id, roles);
} }
})).then(() => { })).then(() => {
@@ -190,18 +194,18 @@ export class ProjectMembersComponent {
}); });
} }
updateRoles(member: ProjectMember.AsObject, selectionChange: MatSelectChange): void { updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
this.mgmtService.ChangeProjectMember(this.project.projectId, member.userId, selectionChange.value) this.mgmtService.updateProjectMember((this.project as Project.AsObject).id, member.userId, selectionChange.value)
.then((_: ProjectMember) => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true); this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { } else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
this.mgmtService.ChangeProjectGrantMember(this.project.projectId, this.mgmtService.updateProjectGrantMember((this.project as GrantedProject.AsObject).projectId,
this.grantId, member.userId, selectionChange.value) this.grantId, member.userId, selectionChange.value)
.then((_: ProjectMember) => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true); this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -29,7 +29,7 @@ export class ProjectRoleDetailComponent {
submitForm(): void { submitForm(): void {
if (this.formGroup.valid && this.key?.value && this.group?.value && this.displayName?.value) { if (this.formGroup.valid && this.key?.value && this.group?.value && this.displayName?.value) {
this.mgmtService.ChangeProjectRole(this.projectId, this.key.value, this.displayName.value, this.group.value) this.mgmtService.updateProjectRole(this.projectId, this.key.value, this.displayName.value, this.group.value)
.then(() => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.ROLECHANGED', true); this.toast.showInfo('PROJECT.TOAST.ROLECHANGED', true);
this.dialogRef.close(true); this.dialogRef.close(true);

View File

@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { ProjectRole } from 'src/app/proto/generated/management_pb'; import { Role } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
/** /**
@@ -10,11 +10,11 @@ import { ManagementService } from 'src/app/services/mgmt.service';
* encapsulate all logic for fetching and manipulating the displayed data * encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering). * (including sorting, pagination, and filtering).
*/ */
export class ProjectRolesDataSource extends DataSource<ProjectRole.AsObject> { export class ProjectRolesDataSource extends DataSource<Role.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public rolesSubject: BehaviorSubject<ProjectRole.AsObject[]> = new BehaviorSubject<ProjectRole.AsObject[]>([]); public rolesSubject: BehaviorSubject<Role.AsObject[]> = new BehaviorSubject<Role.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -26,14 +26,15 @@ export class ProjectRolesDataSource extends DataSource<ProjectRole.AsObject> {
const offset = pageIndex * pageSize; const offset = pageIndex * pageSize;
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchProjectRoles(projectId, pageSize, offset)).pipe( from(this.mgmtService.listProjectRoles(projectId, pageSize, offset)).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); if (resp.details?.totalResult !== undefined) {
this.totalResult = response.totalResult; this.totalResult = resp.details.totalResult;
if (response.viewTimestamp) {
this.viewTimestamp = response.viewTimestamp;
} }
return resp.toObject().resultList; if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details.viewTimestamp;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -48,7 +49,7 @@ export class ProjectRolesDataSource extends DataSource<ProjectRole.AsObject> {
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<ProjectRole.AsObject[]> { public connect(): Observable<Role.AsObject[]> {
return this.rolesSubject.asObservable(); return this.rolesSubject.asObservable();
} }

View File

@@ -4,7 +4,7 @@ import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { ProjectRole } from 'src/app/proto/generated/management_pb'; import { Role } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -22,10 +22,10 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@Input() public actionsVisible: boolean = false; @Input() public actionsVisible: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<ProjectRole.AsObject>; @ViewChild(MatTable) public table!: MatTable<Role.AsObject>;
public dataSource!: ProjectRolesDataSource; public dataSource!: ProjectRolesDataSource;
public selection: SelectionModel<ProjectRole.AsObject> = new SelectionModel<ProjectRole.AsObject>(true, []); public selection: SelectionModel<Role.AsObject> = new SelectionModel<Role.AsObject>(true, []);
@Output() public changedSelection: EventEmitter<Array<ProjectRole.AsObject>> = new EventEmitter(); @Output() public changedSelection: EventEmitter<Array<Role.AsObject>> = new EventEmitter();
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate']; public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate'];
@@ -51,7 +51,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
} }
public selectAllOfGroup(group: string): void { public selectAllOfGroup(group: string): void {
const groupRoles: ProjectRole.AsObject[] = this.dataSource.rolesSubject.getValue() const groupRoles: Role.AsObject[] = this.dataSource.rolesSubject.getValue()
.filter(role => role.group === group); .filter(role => role.group === group);
this.selection.select(...groupRoles); this.selection.select(...groupRoles);
} }
@@ -73,7 +73,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
public masterToggle(): void { public masterToggle(): void {
this.isAllSelected() ? this.isAllSelected() ?
this.selection.clear() : this.selection.clear() :
this.dataSource.rolesSubject.value.forEach((row: ProjectRole.AsObject) => this.selection.select(row)); this.dataSource.rolesSubject.value.forEach((row: Role.AsObject) => this.selection.select(row));
} }
public deleteSelectedRoles(): Promise<any> { public deleteSelectedRoles(): Promise<any> {
@@ -83,7 +83,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
}); });
return Promise.all(this.selection.selected.map(role => { return Promise.all(this.selection.selected.map(role => {
return this.mgmtService.RemoveProjectRole(role.projectId, role.key); return this.mgmtService.removeProjectRole(this.projectId, role.key);
})).then(() => { })).then(() => {
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true); this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
indexes.forEach(index => { indexes.forEach(index => {
@@ -98,9 +98,9 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
}); });
} }
public removeRole(role: ProjectRole.AsObject, index: number): void { public removeRole(role: Role.AsObject, index: number): void {
this.mgmtService this.mgmtService
.RemoveProjectRole(role.projectId, role.key) .removeProjectRole(this.projectId, role.key)
.then(() => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true); this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
this.dataSource.rolesSubject.value.splice(index, 1); this.dataSource.rolesSubject.value.splice(index, 1);
@@ -111,7 +111,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
}); });
} }
public openDetailDialog(role: ProjectRole.AsObject): void { public openDetailDialog(role: Role.AsObject): void {
this.dialog.open(ProjectRoleDetailComponent, { this.dialog.open(ProjectRoleDetailComponent, {
data: { data: {
role, role,

View File

@@ -5,15 +5,8 @@ import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material
import { MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent } from '@angular/material/chips';
import { forkJoin, from, Subject } from 'rxjs'; import { forkJoin, from, Subject } from 'rxjs';
import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators'; import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators';
import { import { ListProjectGrantsResponse, ListProjectsResponse } from 'src/app/proto/generated/zitadel/management_pb';
ProjectGrantSearchResponse, import { GrantedProject, Project, ProjectNameQuery, ProjectQuery } from 'src/app/proto/generated/zitadel/project_pb';
ProjectGrantView,
ProjectSearchKey,
ProjectSearchQuery,
ProjectSearchResponse,
ProjectView,
SearchMethod,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -34,18 +27,18 @@ export class SearchProjectAutocompleteComponent implements OnDestroy {
public separatorKeysCodes: number[] = [ENTER, COMMA]; public separatorKeysCodes: number[] = [ENTER, COMMA];
public myControl: FormControl = new FormControl(); public myControl: FormControl = new FormControl();
public names: string[] = []; public names: string[] = [];
public projects: Array<ProjectGrantView.AsObject | ProjectView.AsObject | any> = []; public projects: Array<GrantedProject.AsObject | Project.AsObject | any> = [];
public filteredProjects: Array<ProjectGrantView.AsObject | ProjectView.AsObject | any> = []; public filteredProjects: Array<GrantedProject.AsObject | Project.AsObject | any> = [];
public isLoading: boolean = false; public isLoading: boolean = false;
@ViewChild('nameInput') public nameInput!: ElementRef<HTMLInputElement>; @ViewChild('nameInput') public nameInput!: ElementRef<HTMLInputElement>;
@ViewChild('auto') public matAutocomplete!: MatAutocomplete; @ViewChild('auto') public matAutocomplete!: MatAutocomplete;
@Input() public singleOutput: boolean = false; @Input() public singleOutput: boolean = false;
@Input() public autocompleteType!: ProjectAutocompleteType; @Input() public autocompleteType!: ProjectAutocompleteType;
@Output() public selectionChanged: EventEmitter< @Output() public selectionChanged: EventEmitter<
ProjectGrantView.AsObject[] GrantedProject.AsObject[]
| ProjectGrantView.AsObject | GrantedProject.AsObject
| ProjectView.AsObject | Project.AsObject
| ProjectView.AsObject[] | Project.AsObject[]
> = new EventEmitter(); > = new EventEmitter();
private unsubscribed$: Subject<void> = new Subject(); private unsubscribed$: Subject<void> = new Subject();
@@ -56,20 +49,20 @@ export class SearchProjectAutocompleteComponent implements OnDestroy {
debounceTime(200), debounceTime(200),
tap(() => this.isLoading = true), tap(() => this.isLoading = true),
switchMap(value => { switchMap(value => {
const query = new ProjectSearchQuery(); const query = new ProjectQuery();
query.setKey(ProjectSearchKey.PROJECTSEARCHKEY_PROJECT_NAME); const nameQuery = new ProjectNameQuery();
query.setValue(value); nameQuery.setName(value);
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE); query.setNameQuery(nameQuery);
switch (this.autocompleteType) { switch (this.autocompleteType) {
case ProjectAutocompleteType.PROJECT_GRANTED: case ProjectAutocompleteType.PROJECT_GRANTED:
return from(this.mgmtService.SearchGrantedProjects(10, 0, [query])); return from(this.mgmtService.listGrantedProjects(10, 0, [query]));
case ProjectAutocompleteType.PROJECT_OWNED: case ProjectAutocompleteType.PROJECT_OWNED:
return from(this.mgmtService.SearchProjects(10, 0, [query])); return from(this.mgmtService.listProjects(10, 0, [query]));
default: default:
return forkJoin([ return forkJoin([
from(this.mgmtService.SearchGrantedProjects(10, 0, [query])), from(this.mgmtService.listGrantedProjects(10, 0, [query])),
from(this.mgmtService.SearchProjects(10, 0, [query])), from(this.mgmtService.listProjects(10, 0, [query])),
]); ]);
} }
}), }),
@@ -77,19 +70,19 @@ export class SearchProjectAutocompleteComponent implements OnDestroy {
switch (this.autocompleteType) { switch (this.autocompleteType) {
case ProjectAutocompleteType.PROJECT_GRANTED: case ProjectAutocompleteType.PROJECT_GRANTED:
this.isLoading = false; this.isLoading = false;
this.filteredProjects = [...(returnValue as ProjectGrantSearchResponse).toObject().resultList]; this.filteredProjects = [...(returnValue as ListProjectGrantsResponse.AsObject).resultList];
break; break;
case ProjectAutocompleteType.PROJECT_OWNED: case ProjectAutocompleteType.PROJECT_OWNED:
this.isLoading = false; this.isLoading = false;
this.filteredProjects = [...(returnValue as ProjectSearchResponse).toObject().resultList]; this.filteredProjects = [...(returnValue as ListProjectsResponse.AsObject).resultList];
break; break;
default: default:
this.isLoading = false; this.isLoading = false;
this.filteredProjects = [ this.filteredProjects = [
...(returnValue as (ProjectSearchResponse | ProjectGrantSearchResponse)[])[0] ...(returnValue as (ListProjectsResponse.AsObject | ListProjectGrantsResponse.AsObject)[])[0]
.toObject().resultList, .resultList,
...(returnValue as (ProjectSearchResponse | ProjectGrantSearchResponse)[])[1] ...(returnValue as (ListProjectsResponse.AsObject | ListProjectGrantsResponse.AsObject)[])[1]
.toObject().resultList, .resultList,
]; ];
break; break;
} }
@@ -133,7 +126,7 @@ export class SearchProjectAutocompleteComponent implements OnDestroy {
} }
} }
public remove(project: ProjectGrantView.AsObject): void { public remove(project: GrantedProject.AsObject): void {
const index = this.projects.indexOf(project); const index = this.projects.indexOf(project);
if (index >= 0) { if (index >= 0) {

View File

@@ -5,12 +5,7 @@ import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material
import { MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent } from '@angular/material/chips';
import { from, Subject } from 'rxjs'; import { from, Subject } from 'rxjs';
import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators'; import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators';
import { import { Role, RoleDisplayNameQuery, RoleQuery } from 'src/app/proto/generated/zitadel/project_pb';
ProjectRole,
ProjectRoleSearchKey,
ProjectRoleSearchQuery,
SearchMethod,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -26,14 +21,14 @@ export class SearchRolesAutocompleteComponent implements OnDestroy {
public separatorKeysCodes: number[] = [ENTER, COMMA]; public separatorKeysCodes: number[] = [ENTER, COMMA];
public myControl: FormControl = new FormControl(); public myControl: FormControl = new FormControl();
public names: string[] = []; public names: string[] = [];
public roles: Array<ProjectRole.AsObject> = []; public roles: Array<Role.AsObject> = [];
public filteredRoles: Array<ProjectRole.AsObject> = []; public filteredRoles: Array<Role.AsObject> = [];
public isLoading: boolean = false; public isLoading: boolean = false;
@ViewChild('nameInput') public nameInput!: ElementRef<HTMLInputElement>; @ViewChild('nameInput') public nameInput!: ElementRef<HTMLInputElement>;
@ViewChild('auto') public matAutocomplete!: MatAutocomplete; @ViewChild('auto') public matAutocomplete!: MatAutocomplete;
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@Input() public singleOutput: boolean = false; @Input() public singleOutput: boolean = false;
@Output() public selectionChanged: EventEmitter<ProjectRole.AsObject[] | ProjectRole.AsObject> = new EventEmitter(); @Output() public selectionChanged: EventEmitter<Role.AsObject[] | Role.AsObject> = new EventEmitter();
private unsubscribed$: Subject<void> = new Subject(); private unsubscribed$: Subject<void> = new Subject();
constructor(private mgmtService: ManagementService) { constructor(private mgmtService: ManagementService) {
@@ -43,15 +38,21 @@ export class SearchRolesAutocompleteComponent implements OnDestroy {
debounceTime(200), debounceTime(200),
tap(() => this.isLoading = true), tap(() => this.isLoading = true),
switchMap(value => { switchMap(value => {
const query = new ProjectRoleSearchQuery(); const query = new RoleQuery();
query.setKey(ProjectRoleSearchKey.PROJECTROLESEARCHKEY_DISPLAY_NAME);
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE); // const key = new RoleKeyQuery();
query.setValue(value); // key.setKey(key)
return from(this.mgmtService.SearchProjectRoles(this.projectId, 10, 0, [query])); // query.setKey(key)
const dQuery = new RoleDisplayNameQuery();
dQuery.setDisplayName(value);
query.setDisplayNameQuery(dQuery);
return from(this.mgmtService.listProjectRoles(this.projectId, 10, 0, [query]));
}), }),
).subscribe((roles) => { ).subscribe((resp) => {
this.isLoading = false; this.isLoading = false;
this.filteredRoles = roles.toObject().resultList; this.filteredRoles = resp.resultList;
}, error => { }, error => {
this.isLoading = false; this.isLoading = false;
}); });
@@ -61,7 +62,7 @@ export class SearchRolesAutocompleteComponent implements OnDestroy {
this.unsubscribed$.next(); this.unsubscribed$.next();
} }
public displayFn(project?: ProjectRole.AsObject): string | undefined { public displayFn(project?: Role.AsObject): string | undefined {
return project ? `${project.displayName}` : undefined; return project ? `${project.displayName}` : undefined;
} }
@@ -91,7 +92,7 @@ export class SearchRolesAutocompleteComponent implements OnDestroy {
} }
} }
public remove(role: ProjectRole.AsObject): void { public remove(role: Role.AsObject): void {
const index = this.roles.indexOf(role); const index = this.roles.indexOf(role);
if (index >= 0) { if (index >= 0) {

View File

@@ -15,7 +15,8 @@ import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material
import { MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent } from '@angular/material/chips';
import { from, of, Subject } from 'rxjs'; import { from, of, Subject } from 'rxjs';
import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators'; import { debounceTime, switchMap, takeUntil, tap } from 'rxjs/operators';
import { SearchMethod, UserSearchKey, UserSearchQuery, UserView } from 'src/app/proto/generated/management_pb'; import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
import { SearchQuery, User, UserNameQuery } from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -39,15 +40,15 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
public globalLoginNameControl: FormControl = new FormControl(); public globalLoginNameControl: FormControl = new FormControl();
public loginNames: string[] = []; public loginNames: string[] = [];
@Input() public users: Array<UserView.AsObject> = []; @Input() public users: Array<User.AsObject> = [];
public filteredUsers: Array<UserView.AsObject> = []; public filteredUsers: Array<User.AsObject> = [];
public isLoading: boolean = false; public isLoading: boolean = false;
@Input() public target: UserTarget = UserTarget.SELF; @Input() public target: UserTarget = UserTarget.SELF;
public hint: string = ''; public hint: string = '';
public UserTarget: any = UserTarget; public UserTarget: any = UserTarget;
@ViewChild('usernameInput') public usernameInput!: ElementRef<HTMLInputElement>; @ViewChild('usernameInput') public usernameInput!: ElementRef<HTMLInputElement>;
@ViewChild('auto') public matAutocomplete!: MatAutocomplete; @ViewChild('auto') public matAutocomplete!: MatAutocomplete;
@Output() public selectionChanged: EventEmitter<UserView.AsObject | UserView.AsObject[]> = new EventEmitter(); @Output() public selectionChanged: EventEmitter<User.AsObject | User.AsObject[]> = new EventEmitter();
@Input() public singleOutput: boolean = false; @Input() public singleOutput: boolean = false;
private unsubscribed$: Subject<void> = new Subject(); private unsubscribed$: Subject<void> = new Subject();
@@ -71,14 +72,15 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
takeUntil(this.unsubscribed$), takeUntil(this.unsubscribed$),
tap(() => this.isLoading = true), tap(() => this.isLoading = true),
switchMap(value => { switchMap(value => {
const query = new UserSearchQuery(); const query = new SearchQuery();
query.setKey(UserSearchKey.USERSEARCHKEY_USER_NAME); const unQuery = new UserNameQuery();
query.setValue(value); unQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE); query.setUserNameQuery(value);
if (this.target === UserTarget.SELF) { if (this.target === UserTarget.SELF) {
return from(this.userService.SearchUsers(10, 0, [query])); return from(this.userService.listUsers(10, 0, [query]));
} else { } else {
return of(); // from(this.userService.GetUserByEmailGlobal(value)); return of();
} }
}), }),
).subscribe((userresp: any) => { ).subscribe((userresp: any) => {
@@ -89,7 +91,7 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
}); });
} }
public displayFn(user?: UserView.AsObject): string | undefined { public displayFn(user?: User.AsObject): string | undefined {
return user ? `${user.preferredLoginName}` : undefined; return user ? `${user.preferredLoginName}` : undefined;
} }
@@ -119,7 +121,7 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
} }
} }
public remove(user: UserView.AsObject): void { public remove(user: User.AsObject): void {
const index = this.users.indexOf(user); const index = this.users.indexOf(user);
if (index >= 0) { if (index >= 0) {
@@ -163,12 +165,12 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
} }
public getGlobalUser(): void { public getGlobalUser(): void {
this.userService.GetUserByLoginNameGlobal(this.globalLoginNameControl.value).then(user => { this.userService.getUserByLoginNameGlobal(this.globalLoginNameControl.value).then(resp => {
if (this.singleOutput) { if (this.singleOutput && resp.user) {
this.users = [user.toObject()]; this.users = [resp.user];
this.selectionChanged.emit(this.users[0]); this.selectionChanged.emit(this.users[0]);
} else { } else if (resp.user) {
this.users.push(user.toObject()); this.users.push(resp.user);
this.selectionChanged.emit(this.users); this.selectionChanged.emit(this.users);
} }
}).catch(error => { }).catch(error => {

View File

@@ -1,23 +1,22 @@
<span class="title" mat-dialog-title>{{'USER.MACHINE.ADDED.TITLE' | translate}}</span> <span class="title" mat-dialog-title>{{'USER.MACHINE.ADDED.TITLE' | translate}}</span>
<div mat-dialog-content> <div mat-dialog-content>
<p class="desc"> {{'USER.MACHINE.ADDED.DESCRIPTION' | translate}}</p> <p class="desc"> {{'USER.MACHINE.ADDED.DESCRIPTION' | translate}}</p>
<ng-container *ngIf="addedKey"> <ng-container *ngIf="keyResponse">
<div class="row"> <div class="row">
<p class="left">{{'USER.MACHINE.ID' | translate}}</p> <p class="left">{{'USER.MACHINE.ID' | translate}}</p>
<p class="right">{{addedKey?.id}}</p> <p class="right">{{keyResponse?.keyId}}</p>
</div>
<div class="row">
<p class="left">{{'USER.MACHINE.TYPE' | translate}}</p>
<p class="right">{{'USER.MACHINE.KEYTYPES.'+addedKey?.type | translate}}</p>
</div> </div>
<div class="row"> <div class="row">
<p class="left">{{'USER.MACHINE.CREATIONDATE' | translate}}</p> <p class="left">{{'USER.MACHINE.CREATIONDATE' | translate}}</p>
<p class="right">{{addedKey?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }} <p class="right">{{keyResponse?.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY,
HH:mm' }}
</p> </p>
</div> </div>
<div class="row"> <div class="row">
<p class="left">{{'USER.MACHINE.EXPIRATIONDATE' | translate}}</p> <p class="left">{{'USER.MACHINE.EXPIRATIONDATE' | translate}}</p>
<p class="right">{{addedKey?.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}} <p class="right">{{keyResponse?.details.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY,
HH:mm'}}
</p> </p>
</div> </div>
<button class="download-button" mat-stroked-button color="primary" (click)="saveFile()">Download</button> <button class="download-button" mat-stroked-button color="primary" (click)="saveFile()">Download</button>

View File

@@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import { AddMachineKeyResponse } from 'src/app/proto/generated/management_pb'; import { AddMachineKeyResponse } from 'src/app/proto/generated/zitadel/management_pb';
@Component({ @Component({
selector: 'app-show-key-dialog', selector: 'app-show-key-dialog',
@@ -9,19 +9,19 @@ import { AddMachineKeyResponse } from 'src/app/proto/generated/management_pb';
styleUrls: ['./show-key-dialog.component.scss'], styleUrls: ['./show-key-dialog.component.scss'],
}) })
export class ShowKeyDialogComponent { export class ShowKeyDialogComponent {
public addedKey!: AddMachineKeyResponse.AsObject; public keyResponse!: AddMachineKeyResponse.AsObject;
constructor( constructor(
public dialogRef: MatDialogRef<ShowKeyDialogComponent>, public dialogRef: MatDialogRef<ShowKeyDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: any,
) { ) {
this.addedKey = data.key; this.keyResponse = data.key;
} }
public saveFile(): void { public saveFile(): void {
const json = atob(this.addedKey.keyDetails.toString()); const json = atob(this.keyResponse.keyDetails.toString());
const blob = new Blob([json], { type: 'text/plain;charset=utf-8' }); const blob = new Blob([json], { type: 'text/plain;charset=utf-8' });
saveAs(blob, `${this.addedKey.id}.json`); saveAs(blob, `${this.keyResponse.keyId}.json`);
} }
public closeDialog(): void { public closeDialog(): void {

View File

@@ -2,14 +2,14 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { ListUserGrantResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { import {
SearchMethod,
UserGrant, UserGrant,
UserGrantSearchKey, UserGrantProjectGrantIDQuery,
UserGrantSearchQuery, UserGrantProjectIDQuery,
UserGrantSearchResponse, UserGrantQuery,
UserGrantView, UserGrantUserIDQuery,
} from 'src/app/proto/generated/management_pb'; } from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
export enum UserGrantContext { export enum UserGrantContext {
@@ -23,7 +23,7 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public grantsSubject: BehaviorSubject<UserGrantView.AsObject[]> = new BehaviorSubject<UserGrantView.AsObject[]>([]); public grantsSubject: BehaviorSubject<UserGrant.AsObject[]> = new BehaviorSubject<UserGrant.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -40,40 +40,44 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
grantId?: string; grantId?: string;
userId?: string; userId?: string;
}, },
queries?: UserGrantSearchQuery[], queries?: UserGrantQuery[],
): void { ): void {
switch (context) { switch (context) {
case UserGrantContext.USER: case UserGrantContext.USER:
if (data && data.userId) { if (data && data.userId) {
this.loadingSubject.next(true); this.loadingSubject.next(true);
const userfilter = new UserGrantSearchQuery();
userfilter.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_USER_ID); const userfilter = new UserGrantQuery();
userfilter.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); const ugUiq = new UserGrantUserIDQuery();
userfilter.setValue(data.userId); ugUiq.setUserId(data.userId);
userfilter.setUserIdQuery(ugUiq);
if (queries) { if (queries) {
queries.push(userfilter); queries.push(userfilter);
} else { } else {
queries = [userfilter]; queries = [userfilter];
} }
const promise = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries); const promise = this.userService.listUserGrants(pageSize, pageSize * pageIndex, queries);
this.loadResponse(promise); this.loadResponse(promise);
} }
break; break;
case UserGrantContext.OWNED_PROJECT: case UserGrantContext.OWNED_PROJECT:
if (data && data.projectId) { if (data && data.projectId) {
this.loadingSubject.next(true); this.loadingSubject.next(true);
const projectfilter = new UserGrantSearchQuery();
projectfilter.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID); const projectfilter = new UserGrantQuery();
projectfilter.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); const ugPfq = new UserGrantProjectIDQuery();
projectfilter.setValue(data.projectId); ugPfq.setProjectId(data.projectId);
projectfilter.setProjectIdQuery(ugPfq);
if (queries) { if (queries) {
queries.push(projectfilter); queries.push(projectfilter);
} else { } else {
queries = [projectfilter]; queries = [projectfilter];
} }
const promise1 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries); const promise1 = this.userService.listUserGrants(pageSize, pageSize * pageIndex, queries);
this.loadResponse(promise1); this.loadResponse(promise1);
} }
break; break;
@@ -81,43 +85,45 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
if (data && data.grantId && data.projectId) { if (data && data.grantId && data.projectId) {
this.loadingSubject.next(true); this.loadingSubject.next(true);
const grantquery: UserGrantSearchQuery = new UserGrantSearchQuery(); const grantfilter = new UserGrantQuery();
grantquery.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_GRANT_ID);
grantquery.setMethod(SearchMethod.SEARCHMETHOD_EQUALS);
grantquery.setValue(data.grantId);
const projectfilter = new UserGrantSearchQuery(); const uggiq = new UserGrantProjectGrantIDQuery();
projectfilter.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID); uggiq.setProjectGrantId(data.grantId);
projectfilter.setValue(data.projectId); grantfilter.setProjectGrantIdQuery(uggiq);
const projectfilter = new UserGrantQuery();
const ugPfq = new UserGrantProjectIDQuery();
ugPfq.setProjectId(data.projectId);
projectfilter.setProjectIdQuery(ugPfq);
if (queries) { if (queries) {
queries.push(projectfilter); queries.push(grantfilter);
queries.push(grantquery);
} else { } else {
queries = [projectfilter, grantquery]; queries = [grantfilter];
} }
const promise2 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries); const promise2 = this.userService.listUserGrants(pageSize, pageSize * pageIndex, queries);
this.loadResponse(promise2); this.loadResponse(promise2);
} }
break; break;
default: default:
this.loadingSubject.next(true); this.loadingSubject.next(true);
const promise3 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries ?? []); const promise3 = this.userService.listUserGrants(pageSize, pageSize * pageIndex, queries ?? []);
this.loadResponse(promise3); this.loadResponse(promise3);
break; break;
} }
} }
private loadResponse(promise: Promise<UserGrantSearchResponse>): void { private loadResponse(promise: Promise<ListUserGrantResponse.AsObject>): void {
from(promise).pipe( from(promise).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); if (resp.details?.totalResult) {
this.totalResult = response.totalResult; this.totalResult = resp.details.totalResult;
if (response.viewTimestamp) {
this.viewTimestamp = response.viewTimestamp;
} }
return response.resultList; if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details.viewTimestamp;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -132,7 +138,7 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<UserGrantView.AsObject[]> { public connect(): Observable<UserGrant.AsObject[]> {
return this.grantsSubject.asObservable(); return this.grantsSubject.asObservable();
} }

View File

@@ -1,9 +1,9 @@
<app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()" <app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp" [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp"
[dataSize]="dataSource?.totalResult" [selection]="selection"> [dataSize]="dataSource?.totalResult" [selection]="selection">
<cnsl-form-field @appearfade *ngIf="userGrantSearchKey != undefined" actions class="filtername"> <cnsl-form-field @appearfade *ngIf="userGrantListSearchKey != undefined" actions class="filtername">
<input cnslInput (keyup)="applyFilter($event)" <input cnslInput (keyup)="applyFilter($event)"
[placeholder]="('USER.TABLE.FILTER.' + userGrantSearchKey.toString()) | translate" #input> [placeholder]="('USER.TABLE.FILTER.' + userGrantListSearchKey.toString()) | translate" #input>
</cnsl-form-field> </cnsl-form-field>
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions <button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
@@ -40,7 +40,7 @@
<ng-container matColumnDef="user"> <ng-container matColumnDef="user">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }} <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }}
<template [ngTemplateOutlet]="templateRef" <template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_DISPLAY_NAME}"></template> [ngTemplateOutletContext]="{key: UserGrantListSearchKey.DISPLAY_NAME}"></template>
</th> </th>
<td mat-cell *matCellDef="let grant"> <td mat-cell *matCellDef="let grant">
{{grant?.displayName}}</td> {{grant?.displayName}}</td>
@@ -49,7 +49,7 @@
<ng-container matColumnDef="org"> <ng-container matColumnDef="org">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }}
<template [ngTemplateOutlet]="templateRef" <template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_ORG_NAME}"></template> [ngTemplateOutletContext]="{key: UserGrantListSearchKey.ORG_NAME}"></template>
</th> </th>
<td mat-cell *matCellDef="let grant"> <td mat-cell *matCellDef="let grant">
{{grant.orgName}} </td> {{grant.orgName}} </td>
@@ -58,7 +58,7 @@
<ng-container matColumnDef="projectId"> <ng-container matColumnDef="projectId">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} <th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }}
<template [ngTemplateOutlet]="templateRef" <template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_NAME}"></template> [ngTemplateOutletContext]="{key: UserGrantListSearchKey.PROJECT_NAME}"></template>
</th> </th>
<td mat-cell *matCellDef="let grant"> <td mat-cell *matCellDef="let grant">
{{grant.projectName}} </td> {{grant.projectName}} </td>
@@ -82,7 +82,7 @@
<th mat-header-cell *matHeaderCellDef class="role-data"> <th mat-header-cell *matHeaderCellDef class="role-data">
{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}
<template [ngTemplateOutlet]="templateRef" <template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_ROLE_KEY}"></template> [ngTemplateOutletContext]="{key: UserGrantListSearchKey.ROLE_KEY}"></template>
</th> </th>
<td mat-cell *matCellDef="let grant; let i = index" class="role-data"> <td mat-cell *matCellDef="let grant; let i = index" class="role-data">
<ng-container <ng-container
@@ -161,7 +161,7 @@
<ng-template #templateRef let-key="key"> <ng-template #templateRef let-key="key">
<button class="search-button" mat-icon-button (click)="setFilter(key)"> <button class="search-button" mat-icon-button (click)="setFilter(key)">
<mat-icon class="icon" *ngIf="this.userGrantSearchKey != key">search</mat-icon> <mat-icon class="icon" *ngIf="this.userGrantListSearchKey != key">search</mat-icon>
<mat-icon class="icon" *ngIf="this.userGrantSearchKey == key">search_off</mat-icon> <mat-icon class="icon" *ngIf="this.userGrantListSearchKey == key">search_off</mat-icon>
</button> </button>
</ng-template> </ng-template>

View File

@@ -6,19 +6,27 @@ import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { enterAnimations } from 'src/app/animations'; import { enterAnimations } from 'src/app/animations';
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
import { import {
ProjectRoleView,
SearchMethod,
UserGrant, UserGrant,
UserGrantSearchKey, UserGrantDisplayNameQuery,
UserGrantSearchQuery, UserGrantOrgNameQuery,
UserGrantView, UserGrantProjectNameQuery,
} from 'src/app/proto/generated/management_pb'; UserGrantQuery,
UserGrantRoleKeyQuery,
} from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource'; import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource';
export enum UserGrantListSearchKey {
DISPLAY_NAME,
ORG_NAME,
PROJECT_NAME,
ROLE_KEY,
}
@Component({ @Component({
selector: 'app-user-grants', selector: 'app-user-grants',
templateUrl: './user-grants.component.html', templateUrl: './user-grants.component.html',
@@ -28,17 +36,17 @@ import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource
], ],
}) })
export class UserGrantsComponent implements OnInit, AfterViewInit { export class UserGrantsComponent implements OnInit, AfterViewInit {
public userGrantSearchKey: UserGrantSearchKey | undefined = undefined; public userGrantListSearchKey: UserGrantListSearchKey | undefined = undefined;
public UserGrantSearchKey: any = UserGrantSearchKey; public UserGrantListSearchKey: any = UserGrantListSearchKey;
public INITIAL_PAGE_SIZE: number = 50; public INITIAL_PAGE_SIZE: number = 50;
@Input() context: UserGrantContext = UserGrantContext.NONE; @Input() context: UserGrantContext = UserGrantContext.NONE;
@Input() refreshOnPreviousRoutes: string[] = []; @Input() refreshOnPreviousRoutes: string[] = [];
public dataSource!: UserGrantsDataSource; public dataSource!: UserGrantsDataSource;
public selection: SelectionModel<UserGrantView.AsObject> = new SelectionModel<UserGrantView.AsObject>(true, []); public selection: SelectionModel<UserGrant.AsObject> = new SelectionModel<UserGrant.AsObject>(true, []);
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<UserGrantView.AsObject>; @ViewChild(MatTable) public table!: MatTable<UserGrant.AsObject>;
@Input() disableWrite: boolean = false; @Input() disableWrite: boolean = false;
@Input() disableDelete: boolean = false; @Input() disableDelete: boolean = false;
@@ -49,7 +57,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@ViewChild('input') public filter!: MatInput; @ViewChild('input') public filter!: MatInput;
public grantRoleOptions: string[] = []; public grantRoleOptions: string[] = [];
public projectRoleOptions: ProjectRoleView.AsObject[] = []; public projectRoleOptions: Role.AsObject[] = [];
public routerLink: any = ['']; public routerLink: any = [''];
public loadedGrantId: string = ''; public loadedGrantId: string = '';
@@ -106,12 +114,36 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
} }
private loadGrantsPage(filterValue?: string): void { private loadGrantsPage(filterValue?: string): void {
let queries: UserGrantSearchQuery[] = []; let queries: UserGrantQuery[] = [];
if (this.userGrantSearchKey !== undefined && filterValue) { if (this.userGrantListSearchKey !== undefined && filterValue) {
const query = new UserGrantSearchQuery(); const query = new UserGrantQuery();
query.setKey(this.userGrantSearchKey); switch (this.userGrantListSearchKey) {
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE); case UserGrantListSearchKey.DISPLAY_NAME:
query.setValue(filterValue); const ugDnQ = new UserGrantDisplayNameQuery();
ugDnQ.setDisplayName(filterValue);
ugDnQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setDisplayNameQuery(ugDnQ);
break;
case UserGrantListSearchKey.ORG_NAME:
const ugOnQ = new UserGrantOrgNameQuery();
ugOnQ.setOrgName(filterValue);
ugOnQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setOrgNameQuery(ugOnQ);
break;
case UserGrantListSearchKey.PROJECT_NAME:
const ugPnQ = new UserGrantProjectNameQuery();
ugPnQ.setProjectName(filterValue);
ugPnQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setProjectNameQuery(ugPnQ);
break;
case UserGrantListSearchKey.ROLE_KEY:
const ugRkQ = new UserGrantRoleKeyQuery();
ugRkQ.setRoleKey(filterValue);
ugRkQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setRoleKeyQuery(ugRkQ);
break;
}
queries = [query]; queries = [query];
} }
@@ -140,8 +172,8 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
this.dataSource.grantsSubject.value.forEach(row => this.selection.select(row)); this.dataSource.grantsSubject.value.forEach(row => this.selection.select(row));
} }
public loadGrantOptions(grant: UserGrantView.AsObject): void { public loadGrantOptions(grant: UserGrant.AsObject): void {
this.grantToEdit = grant.id; this.grantToEdit = grant.grantId;
if (grant.grantId && grant.projectId) { if (grant.grantId && grant.projectId) {
this.getGrantRoleOptions(grant.grantId, grant.projectId); this.getGrantRoleOptions(grant.grantId, grant.projectId);
} else if (grant.projectId) { } else if (grant.projectId) {
@@ -150,23 +182,25 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
} }
private getGrantRoleOptions(grantId: string, projectId: string): void { private getGrantRoleOptions(grantId: string, projectId: string): void {
this.mgmtService.GetGrantedProjectByID(projectId, grantId).then(resp => { this.mgmtService.getGrantedProjectByID(projectId, grantId).then(resp => {
this.loadedGrantId = grantId; if (resp.grantedProject) {
this.grantRoleOptions = resp.toObject().roleKeysList; this.loadedGrantId = grantId;
this.grantRoleOptions = resp.grantedProject?.grantedRoleKeysList;
}
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
private getProjectRoleOptions(projectId: string): void { private getProjectRoleOptions(projectId: string): void {
this.mgmtService.SearchProjectRoles(projectId, 100, 0).then(resp => { this.mgmtService.listProjectRoles(projectId, 100, 0).then(resp => {
this.loadedProjectId = projectId; this.loadedProjectId = projectId;
this.projectRoleOptions = resp.toObject().resultList; this.projectRoleOptions = resp.resultList;
}); });
} }
updateRoles(grant: UserGrant.AsObject, selectionChange: MatSelectChange): void { updateRoles(grant: UserGrant.AsObject, selectionChange: MatSelectChange): void {
this.userService.UpdateUserGrant(grant.id, grant.userId, selectionChange.value) this.userService.updateUserGrant(grant.grantId, grant.userId, selectionChange.value)
.then(() => { .then(() => {
this.toast.showInfo('GRANTS.TOAST.UPDATED', true); this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
}).catch(error => { }).catch(error => {
@@ -175,11 +209,11 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
} }
deleteGrantSelection(): void { deleteGrantSelection(): void {
this.userService.BulkRemoveUserGrant(this.selection.selected.map(grant => grant.id)).then(() => { this.userService.bulkRemoveUserGrant(this.selection.selected.map(grant => grant.grantId)).then(() => {
this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true); this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true);
const data = this.dataSource.grantsSubject.getValue(); const data = this.dataSource.grantsSubject.getValue();
this.selection.selected.forEach((item) => { this.selection.selected.forEach((item) => {
const index = data.findIndex(i => i.id === item.id); const index = data.findIndex(i => i.grantId === item.grantId);
if (index > -1) { if (index > -1) {
data.splice(index, 1); data.splice(index, 1);
this.dataSource.grantsSubject.next(data); this.dataSource.grantsSubject.next(data);
@@ -211,17 +245,17 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
this.loadGrantsPage(filterValue); this.loadGrantsPage(filterValue);
} }
public setFilter(key: UserGrantSearchKey): void { public setFilter(key: UserGrantListSearchKey): void {
setTimeout(() => { setTimeout(() => {
if (this.filter) { if (this.filter) {
(this.filter as any).nativeElement.focus(); (this.filter as any).nativeElement.focus();
} }
}, 100); }, 100);
if (this.userGrantSearchKey !== key) { if (this.userGrantListSearchKey !== key) {
this.userGrantSearchKey = key; this.userGrantListSearchKey = key;
} else { } else {
this.userGrantSearchKey = undefined; this.userGrantListSearchKey = undefined;
this.loadGrantsPage(); this.loadGrantsPage();
} }
} }

View File

@@ -3,7 +3,7 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { FailedEvent } from 'src/app/proto/generated/admin_pb'; import { FailedEvent } from 'src/app/proto/generated/zitadel/admin_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -32,13 +32,9 @@ export class FailedEventsComponent implements AfterViewInit {
public loadEvents(): void { public loadEvents(): void {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.adminService.GetFailedEvents()).pipe( from(this.adminService.listFailedEvents()).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); return resp?.resultList;
// if (response.viewTimestamp) {
// this.viewTimestamp = response.viewTimestamp;
// }
return response.failedEventsList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -49,7 +45,7 @@ export class FailedEventsComponent implements AfterViewInit {
} }
public cancelEvent(viewname: string, db: string, seq: number): void { public cancelEvent(viewname: string, db: string, seq: number): void {
this.adminService.RemoveFailedEvent(viewname, db, seq).then(() => { this.adminService.removeFailedEvent(viewname, db, seq).then(() => {
this.toast.showInfo('IAM.FAILEDEVENTS.DELETESUCCESS', true); this.toast.showInfo('IAM.FAILEDEVENTS.DELETESUCCESS', true);
}); });
} }

View File

@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { IamMemberView } from 'src/app/proto/generated/admin_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
/** /**
@@ -10,10 +10,10 @@ import { AdminService } from 'src/app/services/admin.service';
* encapsulate all logic for fetching and manipulating the displayed data * encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering). * (including sorting, pagination, and filtering).
*/ */
export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> { export class IamMembersDataSource extends DataSource<Member.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public membersSubject: BehaviorSubject<IamMemberView.AsObject[]> = new BehaviorSubject<IamMemberView.AsObject[]>([]); public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -27,14 +27,13 @@ export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.adminService.SearchIamMembers(pageSize, offset)).pipe( from(this.adminService.listIAMMembers(pageSize, offset)).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); this.totalResult = resp.details?.totalResult || 0;
this.totalResult = response.totalResult; if (resp.details?.viewTimestamp) {
if (response.viewTimestamp) { this.viewTimestamp = resp.details?.viewTimestamp;
this.viewTimestamp = response.viewTimestamp;
} }
return response.resultList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -49,7 +48,7 @@ export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> {
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<IamMemberView.AsObject[]> { public connect(): Observable<Member.AsObject[]> {
return this.membersSubject.asObservable(); return this.membersSubject.asObservable();
} }

View File

@@ -3,8 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
import { PageEvent } from '@angular/material/paginator'; import { PageEvent } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { IamMember, IamMemberView } from 'src/app/proto/generated/admin_pb'; import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { ProjectMember, ProjectType, UserView } from 'src/app/proto/generated/management_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -23,7 +24,7 @@ export class IamMembersComponent {
public memberRoleOptions: string[] = []; public memberRoleOptions: string[] = [];
public changePageFactory!: Function; public changePageFactory!: Function;
public changePage: EventEmitter<void> = new EventEmitter(); public changePage: EventEmitter<void> = new EventEmitter();
public selection: Array<IamMemberView.AsObject> = []; public selection: Array<Member.AsObject> = [];
constructor(private adminService: AdminService, constructor(private adminService: AdminService,
private dialog: MatDialog, private dialog: MatDialog,
@@ -42,16 +43,16 @@ export class IamMembersComponent {
} }
public getRoleOptions(): void { public getRoleOptions(): void {
this.adminService.GetIamMemberRoles().then(resp => { this.adminService.listIAMMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.rolesList;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
updateRoles(member: IamMemberView.AsObject, selectionChange: MatSelectChange): void { updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
this.adminService.ChangeIamMember(member.userId, selectionChange.value) this.adminService.updateIAMMember(member.userId, selectionChange.value)
.then((newmember: IamMember) => { .then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true); this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -60,7 +61,7 @@ export class IamMembersComponent {
public removeMemberSelection(): void { public removeMemberSelection(): void {
Promise.all(this.selection.map(member => { Promise.all(this.selection.map(member => {
return this.adminService.RemoveIamMember(member.userId).then(() => { return this.adminService.removeIAMMember(member.userId).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true); this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
this.changePage.emit(); this.changePage.emit();
}).catch(error => { }).catch(error => {
@@ -69,8 +70,8 @@ export class IamMembersComponent {
})); }));
} }
public removeMember(member: ProjectMember.AsObject): void { public removeMember(member: Member.AsObject): void {
this.adminService.RemoveIamMember(member.userId).then(() => { this.adminService.removeIAMMember(member.userId).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true); this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
setTimeout(() => { setTimeout(() => {
this.changePage.emit(); this.changePage.emit();
@@ -90,12 +91,12 @@ export class IamMembersComponent {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => { Promise.all(users.map(user => {
return this.adminService.AddIamMember(user.id, roles); return this.adminService.addIAMMember(user.id, roles);
})).then(() => { })).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true); this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
setTimeout(() => { setTimeout(() => {

View File

@@ -6,7 +6,7 @@ import { MatTableDataSource } from '@angular/material/table';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { View } from 'src/app/proto/generated/admin_pb'; import { View } from 'src/app/proto/generated/zitadel/admin_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -35,9 +35,9 @@ export class IamViewsComponent implements AfterViewInit {
public loadViews(): void { public loadViews(): void {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.adminService.GetViews()).pipe( from(this.adminService.listViews()).pipe(
map(resp => { map(resp => {
return resp.toObject().viewsList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -61,7 +61,7 @@ export class IamViewsComponent implements AfterViewInit {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.adminService.ClearView(viewname, db).then(() => { this.adminService.clearView(viewname, db).then(() => {
this.toast.showInfo('IAM.VIEWS.CLEARED', true); this.toast.showInfo('IAM.VIEWS.CLEARED', true);
this.loadViews(); this.loadViews();
}).catch(error => { }).catch(error => {

View File

@@ -6,7 +6,8 @@ import { catchError, finalize, map } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component'; import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
import { OrgMemberView, UserView } from 'src/app/proto/generated/management_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -20,8 +21,8 @@ export class IamComponent {
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public totalMemberResult: number = 0; public totalMemberResult: number = 0;
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]> public membersSubject: BehaviorSubject<Member.AsObject[]>
= new BehaviorSubject<OrgMemberView.AsObject[]>([]); = new BehaviorSubject<Member.AsObject[]>([]);
public PolicyGridType: any = PolicyGridType; public PolicyGridType: any = PolicyGridType;
@@ -32,10 +33,12 @@ export class IamComponent {
public loadMembers(): void { public loadMembers(): void {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.adminService.SearchIamMembers(100, 0)).pipe( from(this.adminService.listIAMMembers(100, 0)).pipe(
map(resp => { map(resp => {
this.totalMemberResult = resp.toObject().totalResult; if (resp.details?.totalResult) {
return resp.toObject().resultList; this.totalMemberResult = resp.details.totalResult;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -54,12 +57,12 @@ export class IamComponent {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => { Promise.all(users.map(user => {
return this.adminService.AddIamMember(user.id, roles); return this.adminService.addIAMMember(user.id, roles);
})).then(() => { })).then(() => {
this.toast.showInfo('IAM.TOAST.MEMBERADDED'); this.toast.showInfo('IAM.TOAST.MEMBERADDED');
setTimeout(() => { setTimeout(() => {

View File

@@ -6,8 +6,9 @@ import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators'; import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators';
import { CreateHumanRequest, CreateOrgRequest, Gender, OrgSetUpResponse } from 'src/app/proto/generated/admin_pb'; import { SetUpOrgRequest } from 'src/app/proto/generated/zitadel/admin_pb';
import { PasswordComplexityPolicy as MgmtPasswordComplexityPolicy } from 'src/app/proto/generated/management_pb'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -57,7 +58,7 @@ export class OrgCreateComponent {
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED]; public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
public languages: string[] = ['de', 'en']; public languages: string[] = ['de', 'en'];
public policy!: MgmtPasswordComplexityPolicy.AsObject; public policy!: PasswordComplexityPolicy.AsObject;
public usePassword: boolean = false; public usePassword: boolean = false;
public forSelf: boolean = true; public forSelf: boolean = true;
@@ -89,25 +90,29 @@ export class OrgCreateComponent {
public currentCreateStep: number = 1; public currentCreateStep: number = 1;
public finish(): void { public finish(): void {
const createOrgRequest: CreateOrgRequest = new CreateOrgRequest(); const createOrgRequest: SetUpOrgRequest.Org = new SetUpOrgRequest.Org();
createOrgRequest.setName(this.name?.value); createOrgRequest.setName(this.name?.value);
createOrgRequest.setDomain(this.domain?.value); createOrgRequest.setDomain(this.domain?.value);
const humanRequest: CreateHumanRequest = new CreateHumanRequest(); const humanRequest: SetUpOrgRequest.Human = new SetUpOrgRequest.Human();
humanRequest.setEmail(this.email?.value); humanRequest.setEmail(this.email?.value);
humanRequest.setFirstName(this.firstName?.value); humanRequest.setUserName(this.userName?.value);
humanRequest.setLastName(this.lastName?.value);
humanRequest.setNickName(this.nickName?.value);
humanRequest.setGender(this.gender?.value);
humanRequest.setPreferredLanguage(this.preferredLanguage?.value);
const profile: SetUpOrgRequest.Human.Profile = new SetUpOrgRequest.Human.Profile();
profile.setFirstName(this.firstName?.value);
profile.setLastName(this.lastName?.value);
profile.setNickName(this.nickName?.value);
profile.setGender(this.gender?.value);
profile.setPreferredLanguage(this.preferredLanguage?.value);
humanRequest.setProfile(this.firstName?.value);
if (this.usePassword && this.password) { if (this.usePassword && this.password) {
humanRequest.setPassword(this.password?.value); humanRequest.setPassword(this.password?.value);
} }
this.adminService this.adminService
.SetUpOrg(createOrgRequest, humanRequest) .SetUpOrg(createOrgRequest, humanRequest)
.then((org: OrgSetUpResponse) => { .then(() => {
this.router.navigate(['/org/overview']); this.router.navigate(['/org/overview']);
// const orgResp = org.getOrg(); // const orgResp = org.getOrg();
// if (orgResp) { // if (orgResp) {
@@ -146,31 +151,33 @@ export class OrgCreateComponent {
const validators: Validators[] = [Validators.required]; const validators: Validators[] = [Validators.required];
if (this.usePassword) { if (this.usePassword) {
this.mgmtService.GetDefaultPasswordComplexityPolicy().then(data => { this.mgmtService.getDefaultPasswordComplexityPolicy().then(data => {
this.policy = data.toObject(); if (data.policy) {
this.policy = data.policy;
if (this.policy.minLength) { if (this.policy.minLength) {
validators.push(Validators.minLength(this.policy.minLength)); validators.push(Validators.minLength(this.policy.minLength));
} }
if (this.policy.hasLowercase) { if (this.policy.hasLowercase) {
validators.push(lowerCaseValidator); validators.push(lowerCaseValidator);
} }
if (this.policy.hasUppercase) { if (this.policy.hasUppercase) {
validators.push(upperCaseValidator); validators.push(upperCaseValidator);
} }
if (this.policy.hasNumber) { if (this.policy.hasNumber) {
validators.push(numberValidator); validators.push(numberValidator);
} }
if (this.policy.hasSymbol) { if (this.policy.hasSymbol) {
validators.push(symbolValidator); validators.push(symbolValidator);
} }
const pwdValidators = [...validators] as ValidatorFn[]; const pwdValidators = [...validators] as ValidatorFn[];
const confirmPwdValidators = [...validators, passwordConfirmValidator] as ValidatorFn[]; const confirmPwdValidators = [...validators, passwordConfirmValidator] as ValidatorFn[];
this.pwdForm = this.fb.group({ this.pwdForm = this.fb.group({
password: ['', pwdValidators], password: ['', pwdValidators],
confirmPassword: ['', confirmPwdValidators], confirmPassword: ['', confirmPwdValidators],
}); });
}
}); });
} else { } else {
this.pwdForm = this.fb.group({ this.pwdForm = this.fb.group({
@@ -199,7 +206,7 @@ export class OrgCreateComponent {
public createOrgForSelf(): void { public createOrgForSelf(): void {
if (this.name && this.name.value) { if (this.name && this.name.value) {
this.mgmtService.CreateOrg(this.name.value).then((org) => { this.mgmtService.addOrg(this.name.value).then(() => {
this.router.navigate(['/org/overview']); this.router.navigate(['/org/overview']);
// const newOrg = org.toObject(); // const newOrg = org.toObject();
// setTimeout(() => { // setTimeout(() => {

View File

@@ -1,16 +1,16 @@
<span class="title" mat-dialog-title>{{'ORG.PAGES.ORGDOMAIN.TITLE' | translate}} {{domain.domain}}</span> <span class="title" mat-dialog-title>{{'ORG.PAGES.ORGDOMAIN.TITLE' | translate}} {{domain.domainName}}</span>
<div mat-dialog-content> <div mat-dialog-content>
<p class="desc">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION' | translate }}</p> <p class="desc">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION' | translate }}</p>
<p class="desc warn">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_DESC' | translate }}</p> <p class="desc warn">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_DESC' | translate }}</p>
<p *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED && !(dns || http)" <p *ngIf="domain.validationType !== DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED && !(dns || http)"
class="desc"> class="desc">
{{'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING' | translate: domain }} {{'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING' | translate: domain }}
{{'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING_TYPE' | translate}} {{'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING_TYPE' | translate}}
{{'ORG.PAGES.ORGDOMAIN.TYPES.'+ domain.validationType | translate}}</p> {{'ORG.PAGES.ORGDOMAIN.TYPES.'+ domain.validationType | translate}}</p>
<div *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED" <div *ngIf="domain.validationType !== DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED"
class="btn-container"> class="btn-container">
<button color="primary" type="submit" mat-raised-button *ngIf="!(dns || http)" (click)="validate()"> <button color="primary" type="submit" mat-raised-button *ngIf="!(dns || http)" (click)="validate()">
{{ 'ACTIONS.VERIFY' | translate }} {{ 'ACTIONS.VERIFY' | translate }}
@@ -34,8 +34,8 @@
<p class="entry">{{http?.url}}.txt</p> <p class="entry">{{http?.url}}.txt</p>
<div class="btn-container"> <div class="btn-container">
<button mat-stroked-button (click)="saveFile()" <button mat-stroked-button (click)="saveFile()" color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate
color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button> }}</button>
<button color="primary" class="verify-button" type="submit" mat-raised-button (click)="validate()"> <button color="primary" class="verify-button" type="submit" mat-raised-button (click)="validate()">
<span>{{ 'ACTIONS.VERIFY' | translate }}</span> <span>{{ 'ACTIONS.VERIFY' | translate }}</span>
</button> </button>

View File

@@ -1,7 +1,8 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import { OrgDomainValidationResponse, OrgDomainValidationType, OrgDomainView } from 'src/app/proto/generated/management_pb'; import { GenerateOrgDomainValidationResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { Domain, DomainValidationType } from 'src/app/proto/generated/zitadel/org_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -11,12 +12,13 @@ import { ToastService } from 'src/app/services/toast.service';
styleUrls: ['./domain-verification.component.scss'], styleUrls: ['./domain-verification.component.scss'],
}) })
export class DomainVerificationComponent { export class DomainVerificationComponent {
public domain!: OrgDomainView.AsObject; public domain!: Domain.AsObject;
public OrgDomainValidationType: any = OrgDomainValidationType; public DomainValidationType: any = DomainValidationType;
public http!: GenerateOrgDomainValidationResponse.AsObject;
public dns!: GenerateOrgDomainValidationResponse.AsObject;
public http!: OrgDomainValidationResponse.AsObject;
public dns!: OrgDomainValidationResponse.AsObject;
public copied: string = ''; public copied: string = '';
public showNew: boolean = false; public showNew: boolean = false;
@@ -29,24 +31,24 @@ export class DomainVerificationComponent {
private mgmtService: ManagementService, private mgmtService: ManagementService,
) { ) {
this.domain = data.domain; this.domain = data.domain;
if (this.domain.validationType === OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED) { if (this.domain.validationType === DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED) {
this.showNew = true; this.showNew = true;
} }
} }
async loadHttpToken(): Promise<void> { async loadHttpToken(): Promise<void> {
this.mgmtService.GenerateMyOrgDomainValidation( this.mgmtService.generateOrgDomainValidation(
this.domain.domain, this.domain.domainName,
OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_HTTP).then((http) => { DomainValidationType.DOMAIN_VALIDATION_TYPE_HTTP).then((http) => {
this.http = http.toObject(); this.http = http;
}); });
} }
async loadDnsToken(): Promise<void> { async loadDnsToken(): Promise<void> {
this.mgmtService.GenerateMyOrgDomainValidation( this.mgmtService.generateOrgDomainValidation(
this.domain.domain, this.domain.domainName,
OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_DNS).then((dns) => { DomainValidationType.DOMAIN_VALIDATION_TYPE_DNS).then((dns) => {
this.dns = dns.toObject(); this.dns = dns;
}); });
} }
@@ -56,7 +58,7 @@ export class DomainVerificationComponent {
public validate(): void { public validate(): void {
this.validating = true; this.validating = true;
this.mgmtService.ValidateMyOrgDomain(this.domain.domain).then(() => { this.mgmtService.validateOrgDomain(this.domain.domainName).then(() => {
this.dialogRef.close(true); this.dialogRef.close(true);
this.toast.showInfo('ORG.PAGES.ORGDOMAIN.VERIFICATION_SUCCESSFUL', true); this.toast.showInfo('ORG.PAGES.ORGDOMAIN.VERIFICATION_SUCCESSFUL', true);
this.validating = false; this.validating = false;

View File

@@ -1,26 +1,18 @@
import { SelectionModel } from '@angular/cdk/collections'; import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { ChangeType } from 'src/app/modules/changes/changes.component'; import { ChangeType } from 'src/app/modules/changes/changes.component';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component'; import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { import { Member } from 'src/app/proto/generated/zitadel/member_pb';
Org, import { Domain, Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
OrgDomainView, import { User } from 'src/app/proto/generated/zitadel/user_pb';
OrgMember,
OrgMemberSearchResponse,
OrgMemberView,
OrgState,
UserView,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -33,28 +25,22 @@ import { DomainVerificationComponent } from './domain-verification/domain-verifi
templateUrl: './org-detail.component.html', templateUrl: './org-detail.component.html',
styleUrls: ['./org-detail.component.scss'], styleUrls: ['./org-detail.component.scss'],
}) })
export class OrgDetailComponent implements OnInit, OnDestroy { export class OrgDetailComponent implements OnInit {
public org!: Org.AsObject; public org!: Org.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public dataSource: MatTableDataSource<OrgMember.AsObject> = new MatTableDataSource<OrgMember.AsObject>();
public memberResult!: OrgMemberSearchResponse.AsObject;
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
public selection: SelectionModel<OrgMember.AsObject> = new SelectionModel<OrgMember.AsObject>(true, []);
public OrgState: any = OrgState; public OrgState: any = OrgState;
public ChangeType: any = ChangeType; public ChangeType: any = ChangeType;
private subscription: Subscription = new Subscription(); public domains: Domain.AsObject[] = [];
public domains: OrgDomainView.AsObject[] = [];
public primaryDomain: string = ''; public primaryDomain: string = '';
// members // members
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public totalMemberResult: number = 0; public totalMemberResult: number = 0;
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]> public membersSubject: BehaviorSubject<Member.AsObject[]>
= new BehaviorSubject<OrgMemberView.AsObject[]>([]); = new BehaviorSubject<Member.AsObject[]>([]);
public PolicyGridType: any = PolicyGridType; public PolicyGridType: any = PolicyGridType;
constructor( constructor(
@@ -69,13 +55,11 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
this.getData(); this.getData();
} }
public ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private async getData(): Promise<void> { private async getData(): Promise<void> {
this.mgmtService.GetMyOrg().then((org: Org) => { this.mgmtService.getMyOrg().then((resp) => {
this.org = org.toObject(); if (resp.org) {
this.org = resp.org;
}
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
@@ -84,14 +68,14 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
} }
public loadDomains(): void { public loadDomains(): void {
this.mgmtService.SearchMyOrgDomains().then(result => { this.mgmtService.listOrgDomains().then(result => {
this.domains = result.toObject().resultList; this.domains = result.resultList;
this.primaryDomain = this.domains.find(domain => domain.primary)?.domain ?? ''; this.primaryDomain = this.domains.find(domain => domain.isPrimary)?.domainName ?? '';
}); });
} }
public setPrimary(domain: OrgDomainView.AsObject): void { public setPrimary(domain: Domain.AsObject): void {
this.mgmtService.setMyPrimaryOrgDomain(domain.domain).then(() => { this.mgmtService.setPrimaryOrgDomain(domain.domainName).then(() => {
this.toast.showInfo('ORG.TOAST.SETPRIMARY', true); this.toast.showInfo('ORG.TOAST.SETPRIMARY', true);
this.loadDomains(); this.loadDomains();
}).catch((error) => { }).catch((error) => {
@@ -100,14 +84,14 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
} }
public changeState(event: MatButtonToggleChange | any): void { public changeState(event: MatButtonToggleChange | any): void {
if (event.value === OrgState.ORGSTATE_ACTIVE) { if (event.value === OrgState.ORG_STATE_ACTIVE) {
this.mgmtService.ReactivateMyOrg().then(() => { this.mgmtService.reactivateOrg().then(() => {
this.toast.showInfo('ORG.TOAST.REACTIVATED', true); this.toast.showInfo('ORG.TOAST.REACTIVATED', true);
}).catch((error) => { }).catch((error) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (event.value === OrgState.ORGSTATE_INACTIVE) { } else if (event.value === OrgState.ORG_STATE_INACTIVE) {
this.mgmtService.DeactivateMyOrg().then(() => { this.mgmtService.deactivateOrg().then(() => {
this.toast.showInfo('ORG.TOAST.DEACTIVATED', true); this.toast.showInfo('ORG.TOAST.DEACTIVATED', true);
}).catch((error) => { }).catch((error) => {
this.toast.showError(error); this.toast.showError(error);
@@ -123,21 +107,11 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.mgmtService.AddMyOrgDomain(resp).then(domain => { this.mgmtService.addOrgDomain(resp).then(resp => {
const newDomain = domain; const newDomain = resp;
const newDomainView = new OrgDomainView(); // TODO send domainname only
newDomainView.setChangeDate(newDomain.getChangeDate()); // this.verifyDomain(newDomainView.toObject());
newDomainView.setCreationDate(newDomain.getCreationDate());
newDomainView.setDomain(newDomain.getDomain());
newDomainView.setOrgId(newDomain.getOrgId());
newDomainView.setPrimary(newDomain.getPrimary());
newDomainView.setSequence(newDomain.getSequence());
newDomainView.setVerified(newDomain.getVerified());
this.domains.push(newDomainView.toObject());
this.verifyDomain(newDomainView.toObject());
this.toast.showInfo('ORG.TOAST.DOMAINADDED', true); this.toast.showInfo('ORG.TOAST.DOMAINADDED', true);
}); });
} }
@@ -157,9 +131,9 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.mgmtService.RemoveMyOrgDomain(domain).then(() => { this.mgmtService.removeOrgDomain(domain).then(() => {
this.toast.showInfo('ORG.TOAST.DOMAINREMOVED', true); this.toast.showInfo('ORG.TOAST.DOMAINREMOVED', true);
const index = this.domains.findIndex(d => d.domain === domain); const index = this.domains.findIndex(d => d.domainName === domain);
if (index > -1) { if (index > -1) {
this.domains.splice(index, 1); this.domains.splice(index, 1);
} }
@@ -180,12 +154,12 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => { Promise.all(users.map(user => {
return this.mgmtService.AddMyOrgMember(user.id, roles); return this.mgmtService.addOrgMember(user.id, roles);
})).then(() => { })).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true); this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
setTimeout(() => { setTimeout(() => {
@@ -203,7 +177,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
this.router.navigate(['org/members']); this.router.navigate(['org/members']);
} }
public verifyDomain(domain: OrgDomainView.AsObject): void { public verifyDomain(domain: Domain.AsObject): void {
const dialogRef = this.dialog.open(DomainVerificationComponent, { const dialogRef = this.dialog.open(DomainVerificationComponent, {
data: { data: {
domain: domain, domain: domain,
@@ -220,10 +194,12 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
public loadMembers(): void { public loadMembers(): void {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchMyOrgMembers(100, 0)).pipe( from(this.mgmtService.listOrgMembers(100, 0)).pipe(
map(resp => { map(resp => {
this.totalMemberResult = resp.toObject().totalResult; if (resp.details?.totalResult) {
return resp.toObject().resultList; this.totalMemberResult = resp.details?.totalResult;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),

View File

@@ -31,10 +31,10 @@
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header <th mat-header-cell *matHeaderCellDef mat-sort-header
[ngClass]="{'search-active': this.orgSearchKey == MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME}"> [ngClass]="{'search-active': this.orgSearchKey == OrgListSearchKey.NAME}">
{{ 'ORG.PAGES.NAME' | translate }} {{ 'ORG.PAGES.NAME' | translate }}
<template [ngTemplateOutlet]="templateRef" <template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME}"></template> [ngTemplateOutletContext]="{key: OrgListSearchKey.NAME}"></template>
</th> </th>
<td mat-cell *matCellDef="let org"> {{org.name}} </td> <td mat-cell *matCellDef="let org"> {{org.name}} </td>
</ng-container> </ng-container>

View File

@@ -6,9 +6,14 @@ import { Router } from '@angular/router';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { enterAnimations } from 'src/app/animations'; import { enterAnimations } from 'src/app/animations';
import { MyProjectOrgSearchKey, MyProjectOrgSearchQuery, Org, SearchMethod } from 'src/app/proto/generated/auth_pb'; import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
import { Org, OrgNameQuery, OrgQuery } from 'src/app/proto/generated/zitadel/org_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
enum OrgListSearchKey {
NAME = "NAME",
}
@Component({ @Component({
selector: 'app-org-list', selector: 'app-org-list',
templateUrl: './org-list.component.html', templateUrl: './org-list.component.html',
@@ -18,7 +23,7 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
], ],
}) })
export class OrgListComponent implements AfterViewInit { export class OrgListComponent implements AfterViewInit {
public orgSearchKey: MyProjectOrgSearchKey | undefined = undefined; public orgSearchKey: OrgListSearchKey | undefined = undefined;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort; @ViewChild(MatSort) sort!: MatSort;
@@ -29,7 +34,7 @@ export class OrgListComponent implements AfterViewInit {
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public activeOrg!: Org.AsObject; public activeOrg!: Org.AsObject;
public MyProjectOrgSearchKey: any = MyProjectOrgSearchKey; public OrgListSearchKey: any = OrgListSearchKey;
constructor( constructor(
private authService: GrpcAuthService, private authService: GrpcAuthService,
@@ -37,7 +42,7 @@ export class OrgListComponent implements AfterViewInit {
) { ) {
this.loadOrgs(10, 0); this.loadOrgs(10, 0);
this.authService.GetActiveOrg().then(org => this.activeOrg = org); this.authService.getActiveOrg().then(org => this.activeOrg = org);
} }
public ngAfterViewInit(): void { public ngAfterViewInit(): void {
@@ -48,15 +53,16 @@ export class OrgListComponent implements AfterViewInit {
this.loadingSubject.next(true); this.loadingSubject.next(true);
let query; let query;
if (filter) { if (filter) {
query = new MyProjectOrgSearchQuery(); const query = new OrgQuery();
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE); const orgNameQuery = new OrgNameQuery();
query.setKey(MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME); orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setValue(filter); orgNameQuery.setName(filter);
query.setNameQuery(orgNameQuery);
} }
from(this.authService.SearchMyProjectOrgs(limit, offset, query ? [query] : undefined)).pipe( from(this.authService.listMyProjectOrgs(limit, offset, query ? [query] : undefined)).pipe(
map(resp => { map(resp => {
return resp.toObject().resultList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -75,7 +81,7 @@ export class OrgListComponent implements AfterViewInit {
this.loadOrgs(this.paginator.length, this.paginator.pageSize * this.paginator.pageIndex); this.loadOrgs(this.paginator.length, this.paginator.pageSize * this.paginator.pageIndex);
} }
public setFilter(key: MyProjectOrgSearchKey): void { public setFilter(key: OrgListSearchKey): void {
setTimeout(() => { setTimeout(() => {
if (this.filter) { if (this.filter) {
(this.filter as any).nativeElement.focus(); (this.filter as any).nativeElement.focus();

View File

@@ -19,8 +19,8 @@ export class OrgMemberRolesAutocompleteComponent {
@ViewChild('auto') public matAutocomplete!: MatAutocomplete; @ViewChild('auto') public matAutocomplete!: MatAutocomplete;
@Output() public selectionChanged: EventEmitter<string[]> = new EventEmitter(); @Output() public selectionChanged: EventEmitter<string[]> = new EventEmitter();
constructor(private mgmtService: ManagementService, private toast: ToastService) { constructor(private mgmtService: ManagementService, private toast: ToastService) {
this.mgmtService.GetOrgMemberRoles().then(resp => { this.mgmtService.listOrgMemberRoles().then(resp => {
this.allRoles = resp.toObject().rolesList; this.allRoles = resp.resultList;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });

View File

@@ -2,13 +2,13 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { OrgMemberView } from 'src/app/proto/generated/management_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> { export class OrgMembersDataSource extends DataSource<Member.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]> = new BehaviorSubject<OrgMemberView.AsObject[]>([]); public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -20,14 +20,13 @@ export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> {
const offset = pageIndex * pageSize; const offset = pageIndex * pageSize;
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchMyOrgMembers(pageSize, offset)).pipe( from(this.mgmtService.listOrgMembers(pageSize, offset)).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); this.totalResult = resp.details?.totalResult || 0;
this.totalResult = response.totalResult; if (resp.details?.viewTimestamp) {
if (response.viewTimestamp) { this.viewTimestamp = resp.details.viewTimestamp;
this.viewTimestamp = response.viewTimestamp;
} }
return response.resultList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -42,7 +41,7 @@ export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> {
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<OrgMemberView.AsObject[]> { public connect(): Observable<Member.AsObject[]> {
return this.membersSubject.asObservable(); return this.membersSubject.asObservable();
} }

View File

@@ -3,7 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
import { PageEvent } from '@angular/material/paginator'; import { PageEvent } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { Org, OrgMemberView, UserView } from 'src/app/proto/generated/management_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -23,17 +25,19 @@ export class OrgMembersComponent {
public memberRoleOptions: string[] = []; public memberRoleOptions: string[] = [];
public changePageFactory!: Function; public changePageFactory!: Function;
public changePage: EventEmitter<void> = new EventEmitter(); public changePage: EventEmitter<void> = new EventEmitter();
public selection: Array<OrgMemberView.AsObject> = []; public selection: Array<Member.AsObject> = [];
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,
private dialog: MatDialog, private dialog: MatDialog,
private toast: ToastService, private toast: ToastService,
) { ) {
this.mgmtService.GetMyOrg().then(org => { this.mgmtService.getMyOrg().then(resp => {
this.org = org.toObject(); if (resp.org) {
this.dataSource = new OrgMembersDataSource(this.mgmtService); this.org = resp.org;
this.dataSource.loadMembers(0, this.INITIALPAGESIZE); this.dataSource = new OrgMembersDataSource(this.mgmtService);
this.dataSource.loadMembers(0, this.INITIALPAGESIZE);
}
}); });
this.getRoleOptions(); this.getRoleOptions();
@@ -47,15 +51,15 @@ export class OrgMembersComponent {
} }
public getRoleOptions(): void { public getRoleOptions(): void {
this.mgmtService.GetOrgMemberRoles().then(resp => { this.mgmtService.listOrgMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.resultList;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
updateRoles(member: OrgMemberView.AsObject, selectionChange: MatSelectChange): void { updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
this.mgmtService.ChangeMyOrgMember(member.userId, selectionChange.value) this.mgmtService.updateOrgMember(member.userId, selectionChange.value)
.then(() => { .then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true); this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true);
}).catch(error => { }).catch(error => {
@@ -65,7 +69,7 @@ export class OrgMembersComponent {
public removeOrgMemberSelection(): void { public removeOrgMemberSelection(): void {
Promise.all(this.selection.map(member => { Promise.all(this.selection.map(member => {
return this.mgmtService.RemoveMyOrgMember(member.userId).then(() => { return this.mgmtService.removeOrgMember(member.userId).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true); this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -77,8 +81,8 @@ export class OrgMembersComponent {
}); });
} }
public removeOrgMember(member: OrgMemberView.AsObject): void { public removeOrgMember(member: Member.AsObject): void {
this.mgmtService.RemoveMyOrgMember(member.userId).then(() => { this.mgmtService.removeOrgMember(member.userId).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true); this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);
setTimeout(() => { setTimeout(() => {
@@ -99,12 +103,12 @@ export class OrgMembersComponent {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
Promise.all(users.map(user => { Promise.all(users.map(user => {
return this.mgmtService.AddMyOrgMember(user.id, roles); return this.mgmtService.addOrgMember(user.id, roles);
})).then(() => { })).then(() => {
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true); this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
setTimeout(() => { setTimeout(() => {

View File

@@ -41,8 +41,8 @@
</mat-step> </mat-step>
<!-- skip for native applications --> <!-- skip for native applications -->
<mat-step *ngIf="oidcApp.applicationType !== OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE" <mat-step *ngIf="oidcAppRequest.appType !== OIDCAppType.OIDC_APP_TYPE_NATIVE" [stepControl]="secondFormGroup"
[stepControl]="secondFormGroup" [editable]="true"> [editable]="true">
<form [formGroup]="secondFormGroup"> <form [formGroup]="secondFormGroup">
<ng-template matStepLabel>{{'APP.AUTHMETHODSECTION' | translate}}</ng-template> <ng-template matStepLabel>{{'APP.AUTHMETHODSECTION' | translate}}</ng-template>
@@ -65,31 +65,29 @@
<ng-template matStepLabel>{{'APP.OIDC.REDIRECTSECTION' | translate}}</ng-template> <ng-template matStepLabel>{{'APP.OIDC.REDIRECTSECTION' | translate}}</ng-template>
<p class="step-title">{{'APP.OIDC.REDIRECTTITLE' | translate}}</p> <p class="step-title">{{'APP.OIDC.REDIRECTTITLE' | translate}}</p>
<p class="step-description" <p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p> {{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
<p class="step-description" *ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB"> <p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p> {{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<cnsl-redirect-uris class="redirect-section" [canWrite]="true" <cnsl-redirect-uris class="redirect-section" [canWrite]="true"
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE" [isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE"
(changedUris)="oidcApp.redirectUrisList = $event" [urisList]="oidcApp.redirectUrisList" (changedUris)="oidcAppRequest.redirectUrisList = $event" [urisList]="oidcAppRequest.redirectUrisList"
[getValues]="requestRedirectValuesSubject$" title="{{ 'APP.OIDC.REDIRECT' | translate }}"> [getValues]="requestRedirectValuesSubject$" title="{{ 'APP.OIDC.REDIRECT' | translate }}">
</cnsl-redirect-uris> </cnsl-redirect-uris>
<p class="step-title">{{'APP.OIDC.POSTREDIRECTTITLE' | translate}}</p> <p class="step-title">{{'APP.OIDC.POSTREDIRECTTITLE' | translate}}</p>
<p class="step-description" <p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p> {{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
<p class="step-description" <p class="step-description"
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB || oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT"> *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB || oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p> {{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
<cnsl-redirect-uris class="redirect-section" [canWrite]="true" <cnsl-redirect-uris class="redirect-section" [canWrite]="true"
(changedUris)="oidcApp.postLogoutRedirectUrisList = $event" (changedUris)="oidcAppRequest.postLogoutRedirectUrisList = $event"
[urisList]="oidcApp.postLogoutRedirectUrisList" title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}" [urisList]="oidcAppRequest.postLogoutRedirectUrisList"
[getValues]="requestRedirectValuesSubject$" title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}" [getValues]="requestRedirectValuesSubject$"
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"> [isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris> </cnsl-redirect-uris>
<div class="actions"> <div class="actions">
@@ -107,7 +105,7 @@
{{ 'APP.NAME' | translate }} {{ 'APP.NAME' | translate }}
</span> </span>
<span class="right"> <span class="right">
{{oidcApp.name}} {{oidcAppRequest.name}}
</span> </span>
</div> </div>
@@ -117,27 +115,27 @@
{{ 'APP.TYPE' | translate }} {{ 'APP.TYPE' | translate }}
</span> </span>
<span class="right"> <span class="right">
{{'APP.OIDC.APPTYPE.'+oidcApp.applicationType | translate}} {{'APP.OIDC.APPTYPE.'+oidcAppRequest.appType | translate}}
</span> </span>
</div> </div>
<div class="row"> <div class="row">
<span class="left"> <span class="left">
{{ 'APP.GRANT' | translate }} {{ 'APP.GRANT' | translate }}
</span> </span>
<span class="right" *ngIf="oidcApp.grantTypesList?.length > 0"> <span class="right" *ngIf="oidcAppRequest.grantTypesList?.length > 0">
[<span *ngFor="let element of oidcApp.grantTypesList; index as i"> [<span *ngFor="let element of oidcAppRequest.grantTypesList; index as i">
{{'APP.OIDC.GRANT.'+element | translate}} {{'APP.OIDC.GRANT.'+element | translate}}
{{i < oidcApp.grantTypesList.length - 1 ? ', ' : '' }} </span>] {{i < oidcAppRequest.grantTypesList.length - 1 ? ', ' : '' }} </span>]
</span> </span>
</div> </div>
<div class="row"> <div class="row">
<span class="left"> <span class="left">
{{ 'APP.OIDC.RESPONSETYPE' | translate }} {{ 'APP.OIDC.RESPONSETYPE' | translate }}
</span> </span>
<span class="right" *ngIf="oidcApp.responseTypesList?.length > 0"> <span class="right" *ngIf="oidcAppRequest.responseTypesList?.length > 0">
[<span *ngFor="let element of oidcApp.responseTypesList; index as i"> [<span *ngFor="let element of oidcAppRequest.responseTypesList; index as i">
{{('APP.OIDC.RESPONSE.'+element | translate)}} {{('APP.OIDC.RESPONSE.'+element | translate)}}
{{i < oidcApp.responseTypesList.length - 1 ? ', ' : '' }} </span>] {{i < oidcAppRequest.responseTypesList.length - 1 ? ', ' : '' }} </span>]
</span> </span>
</div> </div>
@@ -147,7 +145,7 @@
</span> </span>
<span class="right"> <span class="right">
<span> <span>
{{'APP.OIDC.AUTHMETHOD.'+oidcApp?.authMethodType | translate}} {{'APP.OIDC.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}}
</span> </span>
</span> </span>
</div> </div>
@@ -156,10 +154,10 @@
<span class="left"> <span class="left">
{{ 'APP.OIDC.REDIRECT' | translate }} {{ 'APP.OIDC.REDIRECT' | translate }}
</span> </span>
<span class="right" *ngIf="oidcApp.redirectUrisList?.length > 0"> <span class="right" *ngIf="oidcAppRequest.redirectUrisList?.length > 0">
[<span *ngFor="let redirect of oidcApp.redirectUrisList; index as i"> [<span *ngFor="let redirect of oidcAppRequest.redirectUrisList; index as i">
{{redirect}} {{redirect}}
{{i < oidcApp.redirectUrisList.length - 1 ? ', ' : '' }} </span>] {{i < oidcAppRequest.redirectUrisList.length - 1 ? ', ' : '' }} </span>]
</span> </span>
</div> </div>
@@ -167,10 +165,10 @@
<span class="left"> <span class="left">
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }} {{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
</span> </span>
<span class="right" *ngIf="oidcApp.postLogoutRedirectUrisList?.length > 0"> <span class="right" *ngIf="oidcAppRequest.postLogoutRedirectUrisList?.length > 0">
[<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList; index as i"> [<span *ngFor="let redirect of oidcAppRequest.postLogoutRedirectUrisList; index as i">
{{redirect}} {{redirect}}
{{i < oidcApp.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} </span>] {{i < oidcAppRequest.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} </span>]
</span> </span>
</div> </div>
</ng-container> </ng-container>
@@ -182,7 +180,7 @@
</span> </span>
<span class="right"> <span class="right">
<span> <span>
{{'APP.API.AUTHMETHOD.'+apiApp?.authMethodType | translate}} {{'APP.API.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}}
</span> </span>
</span> </span>
</div> </div>
@@ -247,17 +245,18 @@
<div class="content" *ngIf="formappType?.value?.createType == AppCreateType.OIDC"> <div class="content" *ngIf="formappType?.value?.createType == AppCreateType.OIDC">
<div class="formfield full-width"> <div class="formfield full-width">
<cnsl-redirect-uris class="redirect-section" [canWrite]="true" <cnsl-redirect-uris class="redirect-section" [canWrite]="true"
(changedUris)="oidcApp.redirectUrisList = $event" [urisList]="oidcApp.redirectUrisList" (changedUris)="oidcAppRequest.redirectUrisList = $event"
title="{{ 'APP.OIDC.REDIRECT' | translate }}" [getValues]="requestRedirectValuesSubject$" [urisList]="oidcAppRequest.redirectUrisList" title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"> [getValues]="requestRedirectValuesSubject$"
[isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris> </cnsl-redirect-uris>
<cnsl-redirect-uris class="redirect-section" [canWrite]="true" <cnsl-redirect-uris class="redirect-section" [canWrite]="true"
(changedUris)="oidcApp.postLogoutRedirectUrisList = $event" (changedUris)="oidcAppRequest.postLogoutRedirectUrisList = $event"
[urisList]="oidcApp.postLogoutRedirectUrisList" [urisList]="oidcAppRequest.postLogoutRedirectUrisList"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}" title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[getValues]="requestRedirectValuesSubject$" [getValues]="requestRedirectValuesSubject$"
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"> [isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris> </cnsl-redirect-uris>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,5 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
@@ -8,31 +9,28 @@ import { Subject, Subscription } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators'; import { debounceTime, takeUntil } from 'rxjs/operators';
import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component';
import { import {
APIApplicationCreate,
APIAuthMethodType, APIAuthMethodType,
Application, App,
OIDCApplicationCreate, OIDCAppType,
OIDCApplicationType,
OIDCAuthMethodType, OIDCAuthMethodType,
OIDCConfig,
OIDCGrantType, OIDCGrantType,
OIDCResponseType, OIDCResponseType,
} from 'src/app/proto/generated/management_pb'; } from 'src/app/proto/generated/zitadel/app_pb';
import { AddAPIAppRequest, AddOIDCAppRequest } from 'src/app/proto/generated/zitadel/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import {
WEB_TYPE,
NATIVE_TYPE,
USER_AGENT_TYPE,
API_TYPE,
RadioItemAppType,
AppCreateType
} from '../authtypes';
import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component'; import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component';
import { CODE_METHOD, getPartialConfigFromAuthMethod, IMPLICIT_METHOD, BASIC_AUTH_METHOD, PKCE_METHOD, PK_JWT_METHOD, POST_METHOD } from '../authmethods'; import {
import { StepperSelectionEvent } from '@angular/cdk/stepper'; BASIC_AUTH_METHOD,
CODE_METHOD,
getPartialConfigFromAuthMethod,
IMPLICIT_METHOD,
PK_JWT_METHOD,
PKCE_METHOD,
POST_METHOD,
} from '../authmethods';
import { API_TYPE, AppCreateType, NATIVE_TYPE, RadioItemAppType, USER_AGENT_TYPE, WEB_TYPE } from '../authtypes';
@Component({ @Component({
@@ -47,19 +45,19 @@ export class AppCreateComponent implements OnInit, OnDestroy {
public projectId: string = ''; public projectId: string = '';
public loading: boolean = false; public loading: boolean = false;
public oidcApp: OIDCApplicationCreate.AsObject = new OIDCApplicationCreate().toObject(); public oidcAppRequest: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject();
public apiApp: APIApplicationCreate.AsObject = new APIApplicationCreate().toObject(); public apiAppRequest: AddAPIAppRequest.AsObject = new AddAPIAppRequest().toObject();
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [ public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false, disabled: false }, { type: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE, checked: false, disabled: false },
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false, disabled: false }, { type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN, checked: false, disabled: false },
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false, disabled: false }, { type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN, checked: false, disabled: false },
]; ];
public oidcAppTypes: OIDCApplicationType[] = [ public oidcAppTypes: OIDCAppType[] = [
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB, OIDCAppType.OIDC_APP_TYPE_WEB,
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE, OIDCAppType.OIDC_APP_TYPE_NATIVE,
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT, OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
]; ];
public appTypes: any = [ public appTypes: any = [
WEB_TYPE, WEB_TYPE,
@@ -77,9 +75,9 @@ export class AppCreateComponent implements OnInit, OnDestroy {
// set to oidc first // set to oidc first
public authMethodTypes: { type: OIDCAuthMethodType | APIAuthMethodType, checked: boolean, disabled: boolean; api?: boolean; oidc?: boolean; }[] = [ public authMethodTypes: { type: OIDCAuthMethodType | APIAuthMethodType, checked: boolean, disabled: boolean; api?: boolean; oidc?: boolean; }[] = [
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false, oidc: true }, { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false, oidc: true }, { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false, oidc: true }, { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
]; ];
// stepper // stepper
@@ -90,7 +88,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
public form!: FormGroup; public form!: FormGroup;
public AppCreateType: any = AppCreateType; public AppCreateType: any = AppCreateType;
public OIDCApplicationType: any = OIDCApplicationType; public OIDCAppType: any = OIDCAppType;
public OIDCGrantType: any = OIDCGrantType; public OIDCGrantType: any = OIDCGrantType;
public OIDCAuthMethodType: any = OIDCAuthMethodType; public OIDCAuthMethodType: any = OIDCAuthMethodType;
@@ -99,8 +97,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
checked: boolean, checked: boolean,
disabled: boolean, disabled: boolean,
}[] = [ }[] = [
{ type: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, checked: true, disabled: false }, { type: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, checked: true, disabled: false },
{ type: OIDCGrantType.OIDCGRANTTYPE_IMPLICIT, checked: false, disabled: true }, { type: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT, checked: false, disabled: true },
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true }, // { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
// TODO show when implemented // TODO show when implemented
]; ];
@@ -134,28 +132,28 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.firstFormGroup.valueChanges.subscribe(value => { this.firstFormGroup.valueChanges.subscribe(value => {
if (this.firstFormGroup.valid) { if (this.firstFormGroup.valid) {
this.oidcApp.name = this.name?.value; this.oidcAppRequest.name = this.name?.value;
this.apiApp.name = this.name?.value; this.apiAppRequest.name = this.name?.value;
if (this.isStepperOIDC) { if (this.isStepperOIDC) {
const oidcAppType = (this.appType?.value as RadioItemAppType).oidcApplicationType; const oidcAppType = (this.appType?.value as RadioItemAppType).oidcAppType;
if (oidcAppType !== undefined) { if (oidcAppType !== undefined) {
this.oidcApp.applicationType = oidcAppType; this.oidcAppRequest.appType = oidcAppType;
} }
switch (this.oidcApp.applicationType) { switch (this.oidcAppRequest.appType) {
case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE: case OIDCAppType.OIDC_APP_TYPE_NATIVE:
this.authMethods = [ this.authMethods = [
PKCE_METHOD, PKCE_METHOD,
]; ];
// automatically set to PKCE and skip step // automatically set to PKCE and skip step
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE]; this.oidcAppRequest.responseTypesList = [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE];
this.oidcApp.grantTypesList = [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE]; this.oidcAppRequest.grantTypesList = [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE];
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE; this.oidcAppRequest.authMethodType = OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
break; break;
case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB: case OIDCAppType.OIDC_APP_TYPE_WEB:
// PK_JWT_METHOD.recommended = false; // PK_JWT_METHOD.recommended = false;
this.authMethods = [ this.authMethods = [
PKCE_METHOD, PKCE_METHOD,
@@ -166,7 +164,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.authMethod?.setValue(PKCE_METHOD.key); this.authMethod?.setValue(PKCE_METHOD.key);
break; break;
case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT: case OIDCAppType.OIDC_APP_TYPE_USER_AGENT:
this.authMethods = [ this.authMethods = [
PKCE_METHOD, PKCE_METHOD,
IMPLICIT_METHOD, IMPLICIT_METHOD,
@@ -194,11 +192,11 @@ export class AppCreateComponent implements OnInit, OnDestroy {
const partialConfig = getPartialConfigFromAuthMethod(form.authMethod); const partialConfig = getPartialConfigFromAuthMethod(form.authMethod);
if (this.isStepperOIDC && partialConfig && partialConfig.oidc) { if (this.isStepperOIDC && partialConfig && partialConfig.oidc) {
this.oidcApp.responseTypesList = partialConfig.oidc?.responseTypesList ?? []; this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList ?? [];
this.oidcApp.grantTypesList = partialConfig.oidc?.grantTypesList ?? []; this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList ?? [];
this.oidcApp.authMethodType = partialConfig.oidc?.authMethodType ?? OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE; this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
} else if (this.isStepperAPI && partialConfig && partialConfig.api) { } else if (this.isStepperAPI && partialConfig && partialConfig.api) {
this.apiApp.authMethodType = partialConfig.api?.authMethodType ?? APIAuthMethodType.APIAUTHMETHODTYPE_BASIC; this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
} }
}); });
} }
@@ -216,18 +214,18 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.form.valueChanges.pipe( this.form.valueChanges.pipe(
takeUntil(this.destroyed$), takeUntil(this.destroyed$),
debounceTime(150)).subscribe(() => { debounceTime(150)).subscribe(() => {
this.oidcApp.name = this.formname?.value; this.oidcAppRequest.name = this.formname?.value;
this.apiApp.name = this.formname?.value; this.apiAppRequest.name = this.formname?.value;
this.oidcApp.responseTypesList = this.formresponseTypesList?.value; this.oidcAppRequest.responseTypesList = this.formresponseTypesList?.value;
this.oidcApp.grantTypesList = this.formgrantTypesList?.value; this.oidcAppRequest.grantTypesList = this.formgrantTypesList?.value;
this.oidcApp.authMethodType = this.formauthMethodType?.value; this.oidcAppRequest.authMethodType = this.formauthMethodType?.value;
this.apiApp.authMethodType = this.formauthMethodType?.value; this.apiAppRequest.authMethodType = this.formauthMethodType?.value;
const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcApplicationType; const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcAppType;
if (oidcAppType !== undefined) { if (oidcAppType !== undefined) {
this.oidcApp.applicationType = oidcAppType; this.oidcAppRequest.appType = oidcAppType;
} }
}); });
@@ -245,20 +243,20 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.form.addControl('responseTypesList', responseTypesControl); this.form.addControl('responseTypesList', responseTypesControl);
this.authMethodTypes = [ this.authMethodTypes = [
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false, oidc: true }, { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false, oidc: true }, { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false, oidc: true }, { type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
]; ];
this.authMethod?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC); this.authMethod?.setValue(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC);
} else if (this.isDevAPI) { } else if (this.isDevAPI) {
this.form.removeControl('grantTypesList'); this.form.removeControl('grantTypesList');
this.form.removeControl('responseTypesList'); this.form.removeControl('responseTypesList');
this.authMethodTypes = [ this.authMethodTypes = [
{ type: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT, checked: false, disabled: false, api: true }, { type: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, checked: false, disabled: false, api: true },
{ type: APIAuthMethodType.APIAUTHMETHODTYPE_BASIC, checked: false, disabled: false, api: true }, { type: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, api: true },
]; ];
this.authMethod?.setValue(APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT); this.authMethod?.setValue(APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT);
} }
this.form.updateValueAndValidity(); this.form.updateValueAndValidity();
} }
@@ -271,8 +269,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
private async getData({ projectid }: Params): Promise<void> { private async getData({ projectid }: Params): Promise<void> {
this.projectId = projectid; this.projectId = projectid;
this.oidcApp.projectId = projectid; this.oidcAppRequest.projectId = projectid;
this.apiApp.projectId = projectid; this.apiAppRequest.projectId = projectid;
} }
public close(): void { public close(): void {
@@ -288,15 +286,14 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.loading = true; this.loading = true;
this.mgmtService this.mgmtService
.CreateOIDCApp(this.oidcApp) .addOIDCApp(this.oidcAppRequest)
.then((data: Application) => { .then((resp) => {
this.loading = false; this.loading = false;
const response = data.toObject(); // if (resp.oidcConfig?.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE) {
if (response.oidcConfig?.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE) { // this.showSavedDialog(resp);
this.showSavedDialog(response); // } else {
} else { // this.router.navigate(['projects', this.projectId, 'apps', response.id]);
this.router.navigate(['projects', this.projectId, 'apps', response.id]); // }
}
}) })
.catch(error => { .catch(error => {
this.loading = false; this.loading = false;
@@ -305,15 +302,15 @@ export class AppCreateComponent implements OnInit, OnDestroy {
} else if (appAPICheck) { } else if (appAPICheck) {
this.loading = true; this.loading = true;
this.mgmtService this.mgmtService
.CreateAPIApplication(this.apiApp) .addAPIApp(this.apiAppRequest)
.then((data: Application) => { .then((resp) => {
this.loading = false; this.loading = false;
const response = data.toObject(); // const response = resp.toObject();
if (response.apiConfig?.authMethodType == APIAuthMethodType.APIAUTHMETHODTYPE_BASIC) { // if (response.apiConfig?.authMethodType == APIAuthMethodType.APIAUTHMETHODTYPE_BASIC) {
this.showSavedDialog(response); // this.showSavedDialog(resp);
} else { // } else {
this.router.navigate(['projects', this.projectId, 'apps', response.id]); // this.router.navigate(['projects', this.projectId, 'apps', response.id]);
} // }
}) })
.catch(error => { .catch(error => {
this.loading = false; this.loading = false;
@@ -322,7 +319,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
} }
} }
public showSavedDialog(app: Application.AsObject): void { public showSavedDialog(app: App.AsObject): void {
if (app.oidcConfig?.clientSecret !== undefined) { if (app.oidcConfig?.clientSecret !== undefined) {
const dialogRef = this.dialog.open(AppSecretDialogComponent, { const dialogRef = this.dialog.open(AppSecretDialogComponent, {
data: app.oidcConfig, data: app.oidcConfig,

View File

@@ -106,29 +106,28 @@
{{ 'APP.OIDC.DEVMODE' | translate }} {{ 'APP.OIDC.DEVMODE' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<cnsl-info-section class="step-description" <cnsl-info-section class="step-description" *ngIf="appType?.value == OIDCAppType.OIDC_APP_TYPE_NATIVE">
*ngIf="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
<span>{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</span> <span>{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</span>
</cnsl-info-section> </cnsl-info-section>
<cnsl-info-section class="step-description" <cnsl-info-section class="step-description"
*ngIf="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB || applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT"> *ngIf="OIDCAppType?.value == OIDCAppType.OIDC_APP_TYPE_WEB || appType?.value == OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}} {{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}
</cnsl-info-section> </cnsl-info-section>
<div style="margin: .5rem" class="divider"></div> <div style="margin: .5rem" class="divider"></div>
<cnsl-redirect-uris *ngIf="applicationType?.value !== undefined" class="redirect-section" <cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
[canWrite]="canWrite" [devMode]="devMode?.value" [getValues]="requestRedirectValuesSubject$" [devMode]="devMode?.value" [getValues]="requestRedirectValuesSubject$"
(changedUris)="redirectUrisList = $event" [urisList]="redirectUrisList" (changedUris)="redirectUrisList = $event" [urisList]="redirectUrisList"
title="{{ 'APP.OIDC.REDIRECT' | translate }}" title="{{ 'APP.OIDC.REDIRECT' | translate }}"
[isNative]="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"> [isNative]="appType?.value == OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris> </cnsl-redirect-uris>
<cnsl-redirect-uris *ngIf="applicationType?.value !== undefined" class="redirect-section" <cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
[canWrite]="canWrite" [devMode]="devMode?.value" (changedUris)="postLogoutRedirectUrisList = $event" [devMode]="devMode?.value" (changedUris)="postLogoutRedirectUrisList = $event"
[urisList]="postLogoutRedirectUrisList" [getValues]="requestRedirectValuesSubject$" [urisList]="postLogoutRedirectUrisList" [getValues]="requestRedirectValuesSubject$"
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}" title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
[isNative]="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"> [isNative]="appType?.value == OIDCAppType.OIDC_APP_TYPE_NATIVE">
</cnsl-redirect-uris> </cnsl-redirect-uris>
</div> </div>
@@ -262,7 +261,7 @@
<div class="meta-row"> <div class="meta-row">
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span> <span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
<span *ngIf="app && app.state !== undefined" <span *ngIf="app && app.state !== undefined"
[ngClass]="{'active': app.state === AppState.APPSTATE_ACTIVE, 'inactive': app.state === AppState.APPSTATE_INACTIVE}" [ngClass]="{'active': app.state === AppState.APP_STATE_ACTIVE, 'inactive': app.state === AppState.APP_STATE_INACTIVE}"
class="state">{{'APP.PAGES.DETAIL.STATE.'+app.state | translate}}</span> class="state">{{'APP.PAGES.DETAIL.STATE.'+app.state | translate}}</span>
</div> </div>
</div> </div>

View File

@@ -2,11 +2,11 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatButtonToggleChange } from '@angular/material/button-toggle'; import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, Params, Router, RouterLink } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
@@ -18,25 +18,36 @@ import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.com
import { import {
APIAuthMethodType, APIAuthMethodType,
APIConfig, APIConfig,
APIConfigUpdate, App,
Application,
AppState, AppState,
ClientSecret, OIDCAppType,
OIDCApplicationType,
OIDCAuthMethodType, OIDCAuthMethodType,
OIDCConfig, OIDCConfig,
OIDCConfigUpdate,
OIDCGrantType, OIDCGrantType,
OIDCResponseType, OIDCResponseType,
OIDCTokenType, OIDCTokenType,
ZitadelDocs, } from 'src/app/proto/generated/zitadel/app_pb';
} from 'src/app/proto/generated/management_pb'; import {
GetOIDCInformationResponse,
UpdateAPIAppConfigRequest,
UpdateOIDCAppConfigRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component'; import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component';
import { CODE_METHOD, getAuthMethodFromPartialConfig, getPartialConfigFromAuthMethod, IMPLICIT_METHOD, PKCE_METHOD, PK_JWT_METHOD, POST_METHOD, CUSTOM_METHOD, BASIC_AUTH_METHOD } from '../authmethods'; import {
BASIC_AUTH_METHOD,
CODE_METHOD,
CUSTOM_METHOD,
getAuthMethodFromPartialConfig,
getPartialConfigFromAuthMethod,
IMPLICIT_METHOD,
PK_JWT_METHOD,
PKCE_METHOD,
POST_METHOD,
} from '../authmethods';
@Component({ @Component({
selector: 'app-app-detail', selector: 'app-app-detail',
@@ -56,33 +67,33 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public authMethods: RadioItemAuthType[] = []; public authMethods: RadioItemAuthType[] = [];
private subscription?: Subscription; private subscription?: Subscription;
public projectId: string = ''; public projectId: string = '';
public app!: Application.AsObject; public app!: App.AsObject;
public oidcResponseTypes: OIDCResponseType[] = [ public oidcResponseTypes: OIDCResponseType[] = [
OIDCResponseType.OIDCRESPONSETYPE_CODE, OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN,
OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN,
]; ];
public oidcGrantTypes: OIDCGrantType[] = [ public oidcGrantTypes: OIDCGrantType[] = [
OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
OIDCGrantType.OIDCGRANTTYPE_IMPLICIT, OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT,
OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN,
]; ];
public oidcAppTypes: OIDCApplicationType[] = [ public oidcAppTypes: OIDCAppType[] = [
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB, OIDCAppType.OIDC_APP_TYPE_WEB,
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT, OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE, OIDCAppType.OIDC_APP_TYPE_NATIVE,
]; ];
public oidcAuthMethodType: OIDCAuthMethodType[] = [ public oidcAuthMethodType: OIDCAuthMethodType[] = [
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
]; ];
public oidcTokenTypes: OIDCTokenType[] = [ public oidcTokenTypes: OIDCTokenType[] = [
OIDCTokenType.OIDCTOKENTYPE_BEARER, OIDCTokenType.OIDC_TOKEN_TYPE_BEARER,
OIDCTokenType.OIDCTOKENTYPE_JWT, OIDCTokenType.OIDC_TOKEN_TYPE_JWT,
]; ];
public AppState: any = AppState; public AppState: any = AppState;
@@ -94,9 +105,9 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public postLogoutRedirectUrisList: string[] = []; public postLogoutRedirectUrisList: string[] = [];
public isZitadel: boolean = false; public isZitadel: boolean = false;
public docs!: ZitadelDocs.AsObject; public docs!: GetOIDCInformationResponse.AsObject;
public OIDCApplicationType: any = OIDCApplicationType; public OIDCAppType: any = OIDCAppType;
public OIDCAuthMethodType: any = OIDCAuthMethodType; public OIDCAuthMethodType: any = OIDCAuthMethodType;
public APIAuthMethodType: any = APIAuthMethodType; public APIAuthMethodType: any = APIAuthMethodType;
public OIDCTokenType: any = OIDCTokenType; public OIDCTokenType: any = OIDCTokenType;
@@ -142,7 +153,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
clientId: [{ value: '', disabled: true }], clientId: [{ value: '', disabled: true }],
responseTypesList: [{ value: [], disabled: true }], responseTypesList: [{ value: [], disabled: true }],
grantTypesList: [{ value: [], disabled: true }], grantTypesList: [{ value: [], disabled: true }],
applicationType: [{ value: '', disabled: true }], appType: [{ value: '', disabled: true }],
authMethodType: [{ value: '', disabled: true }], authMethodType: [{ value: '', disabled: true }],
accessTokenType: [{ value: '', disabled: true }], accessTokenType: [{ value: '', disabled: true }],
accessTokenRoleAssertion: [{ value: false, disabled: true }], accessTokenRoleAssertion: [{ value: false, disabled: true }],
@@ -192,93 +203,95 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.initLinks(); this.initLinks();
this.mgmtService.GetIam().then(iam => { this.mgmtService.getIAM().then(iam => {
this.isZitadel = iam.toObject().iamProjectId === this.projectId; this.isZitadel = iam.iamProjectId === this.projectId;
}); });
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid]).pipe(take(1)).subscribe((allowed) => { this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid]).pipe(take(1)).subscribe((allowed) => {
this.canWrite = allowed; this.canWrite = allowed;
this.mgmtService.GetApplicationById(projectid, id).then(app => { this.mgmtService.getAppByID(projectid, id).then(app => {
this.app = app.toObject(); if (app.app) {
this.appNameForm.patchValue(this.app); this.app = app.app;
this.appNameForm.patchValue(this.app);
if (this.app.oidcConfig) { if (this.app.oidcConfig) {
this.getAuthMethodOptions('OIDC'); this.getAuthMethodOptions('OIDC');
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig }); this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig });
this.currentAuthMethod = this.initialAuthMethod; this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) { if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) { if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD); this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
} }
} else { } else if (this.app.apiConfig) {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD); this.getAuthMethodOptions('API');
}
} else if (this.app.apiConfig) {
this.getAuthMethodOptions('API');
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig }); this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig });
this.currentAuthMethod = this.initialAuthMethod; this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) { if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) { if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD); this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
} }
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
}
}
if (allowed) {
this.appNameForm.enable();
this.oidcForm.enable();
this.apiForm.enable();
}
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig?.clockSkew) {
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
}
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
} }
this.showSaveSnack(); if (allowed) {
}); this.appNameForm.enable();
this.oidcForm.enable();
this.apiForm.valueChanges.subscribe((apiConfig) => { this.apiForm.enable();
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
} }
this.showSaveSnack(); if (this.app.oidcConfig?.redirectUrisList) {
}); this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig?.clockSkew) {
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
}
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
}
this.showSaveSnack();
});
this.apiForm.valueChanges.subscribe((apiConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
}
this.showSaveSnack();
});
}
}).catch(error => { }).catch(error => {
console.error(error); console.error(error);
this.toast.showError(error); this.toast.showError(error);
this.errorMessage = error.message; this.errorMessage = error.message;
}); });
}); });
this.docs = (await this.mgmtService.GetZitadelDocs()).toObject(); this.docs = (await this.mgmtService.getOIDCInformation());
} }
private async showSaveSnack(): Promise<void> { private async showSaveSnack(): Promise<void> {
@@ -297,14 +310,14 @@ export class AppDetailComponent implements OnInit, OnDestroy {
private getAuthMethodOptions(type: string): void { private getAuthMethodOptions(type: string): void {
if (type == 'OIDC') { if (type == 'OIDC') {
switch (this.app.oidcConfig?.applicationType) { switch (this.app.oidcConfig?.appType) {
case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE: case OIDCAppType.OIDC_APP_TYPE_NATIVE:
this.authMethods = [ this.authMethods = [
PKCE_METHOD, PKCE_METHOD,
CUSTOM_METHOD, CUSTOM_METHOD,
]; ];
break; break;
case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB: case OIDCAppType.OIDC_APP_TYPE_WEB:
this.authMethods = [ this.authMethods = [
PKCE_METHOD, PKCE_METHOD,
CODE_METHOD, CODE_METHOD,
@@ -312,7 +325,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
POST_METHOD, POST_METHOD,
]; ];
break; break;
case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT: case OIDCAppType.OIDC_APP_TYPE_USER_AGENT:
this.authMethods = [ this.authMethods = [
PKCE_METHOD, PKCE_METHOD,
IMPLICIT_METHOD, IMPLICIT_METHOD,
@@ -338,10 +351,10 @@ export class AppDetailComponent implements OnInit, OnDestroy {
if (partialConfig && partialConfig.oidc && this.app.oidcConfig) { if (partialConfig && partialConfig.oidc && this.app.oidcConfig) {
this.app.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? []; this.app.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? [];
this.app.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? []; this.app.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? [];
this.app.oidcConfig.authMethodType = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE; this.app.oidcConfig.authMethodType = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
this.oidcForm.patchValue(this.app.oidcConfig); this.oidcForm.patchValue(this.app.oidcConfig);
} else if (partialConfig && partialConfig.api && this.app.apiConfig) { } else if (partialConfig && partialConfig.api && this.app.apiConfig) {
this.app.apiConfig.authMethodType = (partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.APIAUTHMETHODTYPE_BASIC; this.app.apiConfig.authMethodType = (partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
this.apiAuthMethodType?.setValue(this.app.apiConfig.authMethodType); this.apiAuthMethodType?.setValue(this.app.apiConfig.authMethodType);
} }
} }
@@ -358,7 +371,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp && this.projectId && this.app.id) { if (resp && this.projectId && this.app.id) {
this.mgmtService.RemoveApplication(this.projectId, this.app.id).then(() => { this.mgmtService.removeApp(this.projectId, this.app.id).then(() => {
this.toast.showInfo('APP.TOAST.DELETED', true); this.toast.showInfo('APP.TOAST.DELETED', true);
this.router.navigate(['/projects', this.projectId]); this.router.navigate(['/projects', this.projectId]);
@@ -370,14 +383,14 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public changeState(event: MatButtonToggleChange): void { public changeState(event: MatButtonToggleChange): void {
if (event.value === AppState.APPSTATE_ACTIVE) { if (event.value === AppState.APP_STATE_ACTIVE) {
this.mgmtService.ReactivateApplication(this.projectId, this.app.id).then(() => { this.mgmtService.reactivateApp(this.projectId, this.app.id).then(() => {
this.toast.showInfo('APP.TOAST.REACTIVATED', true); this.toast.showInfo('APP.TOAST.REACTIVATED', true);
}).catch((error: any) => { }).catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (event.value === AppState.APPSTATE_INACTIVE) { } else if (event.value === AppState.APP_STATE_INACTIVE) {
this.mgmtService.DeactivateApplication(this.projectId, this.app.id).then(() => { this.mgmtService.deactivateApp(this.projectId, this.app.id).then(() => {
this.toast.showInfo('APP.TOAST.DEACTIVATED', true); this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
}).catch((error: any) => { }).catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
@@ -390,7 +403,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.app.name = this.name?.value; this.app.name = this.name?.value;
this.mgmtService this.mgmtService
.UpdateApplication(this.projectId, this.app.id, this.name?.value) .updateApp(this.projectId, this.app.id, this.name?.value)
.then(() => { .then(() => {
this.toast.showInfo('APP.TOAST.UPDATED', true); this.toast.showInfo('APP.TOAST.UPDATED', true);
this.editState = false; this.editState = false;
@@ -412,7 +425,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
if (this.app.oidcConfig) { if (this.app.oidcConfig) {
this.app.oidcConfig.responseTypesList = this.responseTypesList?.value; this.app.oidcConfig.responseTypesList = this.responseTypesList?.value;
this.app.oidcConfig.grantTypesList = this.grantTypesList?.value; this.app.oidcConfig.grantTypesList = this.grantTypesList?.value;
this.app.oidcConfig.applicationType = this.applicationType?.value; this.app.oidcConfig.appType = this.appType?.value;
this.app.oidcConfig.authMethodType = this.authMethodType?.value; this.app.oidcConfig.authMethodType = this.authMethodType?.value;
this.app.oidcConfig.redirectUrisList = this.redirectUrisList; this.app.oidcConfig.redirectUrisList = this.redirectUrisList;
this.app.oidcConfig.postLogoutRedirectUrisList = this.postLogoutRedirectUrisList; this.app.oidcConfig.postLogoutRedirectUrisList = this.postLogoutRedirectUrisList;
@@ -422,16 +435,15 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.app.oidcConfig.idTokenRoleAssertion = this.idTokenRoleAssertion?.value; this.app.oidcConfig.idTokenRoleAssertion = this.idTokenRoleAssertion?.value;
this.app.oidcConfig.idTokenUserinfoAssertion = this.idTokenUserinfoAssertion?.value; this.app.oidcConfig.idTokenUserinfoAssertion = this.idTokenUserinfoAssertion?.value;
const req = new UpdateOIDCAppConfigRequest();
const req = new OIDCConfigUpdate();
req.setProjectId(this.projectId); req.setProjectId(this.projectId);
req.setApplicationId(this.app.id); req.setAppId(this.app.id);
req.setRedirectUrisList(this.app.oidcConfig.redirectUrisList); req.setRedirectUrisList(this.app.oidcConfig.redirectUrisList);
req.setResponseTypesList(this.app.oidcConfig.responseTypesList); req.setResponseTypesList(this.app.oidcConfig.responseTypesList);
req.setAuthMethodType(this.app.oidcConfig.authMethodType); req.setAuthMethodType(this.app.oidcConfig.authMethodType);
req.setPostLogoutRedirectUrisList(this.app.oidcConfig.postLogoutRedirectUrisList); req.setPostLogoutRedirectUrisList(this.app.oidcConfig.postLogoutRedirectUrisList);
req.setGrantTypesList(this.app.oidcConfig.grantTypesList); req.setGrantTypesList(this.app.oidcConfig.grantTypesList);
req.setApplicationType(this.app.oidcConfig.applicationType); req.setAppType(this.app.oidcConfig.appType);
req.setDevMode(this.app.oidcConfig.devMode); req.setDevMode(this.app.oidcConfig.devMode);
req.setAccessTokenType(this.app.oidcConfig.accessTokenType); req.setAccessTokenType(this.app.oidcConfig.accessTokenType);
req.setAccessTokenRoleAssertion(this.app.oidcConfig.accessTokenRoleAssertion); req.setAccessTokenRoleAssertion(this.app.oidcConfig.accessTokenRoleAssertion);
@@ -444,7 +456,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
req.setClockSkew(dur); req.setClockSkew(dur);
} }
this.mgmtService this.mgmtService
.UpdateOIDCAppConfig(req) .updateOIDCAppConfig(req)
.then(() => { .then(() => {
if (this.app.oidcConfig) { if (this.app.oidcConfig) {
const config = { oidc: this.app.oidcConfig }; const config = { oidc: this.app.oidcConfig };
@@ -463,13 +475,13 @@ export class AppDetailComponent implements OnInit, OnDestroy {
if (this.apiForm.valid && this.app.apiConfig) { if (this.apiForm.valid && this.app.apiConfig) {
this.app.apiConfig.authMethodType = this.apiAuthMethodType?.value; this.app.apiConfig.authMethodType = this.apiAuthMethodType?.value;
const req = new APIConfigUpdate(); const req = new UpdateAPIAppConfigRequest();
req.setProjectId(this.projectId); req.setProjectId(this.projectId);
req.setApplicationId(this.app.id); req.setAppId(this.app.id);
req.setAuthMethodType(this.app.apiConfig.authMethodType); req.setAuthMethodType(this.app.apiConfig.authMethodType);
this.mgmtService this.mgmtService
.UpdateAPIAppConfig(req) .updateAPIAppConfig(req)
.then(() => { .then(() => {
if (this.app.apiConfig) { if (this.app.apiConfig) {
const config = { api: this.app.apiConfig }; const config = { api: this.app.apiConfig };
@@ -484,12 +496,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public regenerateOIDCClientSecret(): void { public regenerateOIDCClientSecret(): void {
this.mgmtService.RegenerateOIDCClientSecret(this.app.id, this.projectId).then((data: ClientSecret) => { this.mgmtService.regenerateOIDCClientSecret(this.app.id, this.projectId).then(resp => {
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true); this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
this.dialog.open(AppSecretDialogComponent, { this.dialog.open(AppSecretDialogComponent, {
data: { data: {
// clientId: data.toObject() as ClientSecret.AsObject.clientId, // clientId: data.toObject() as ClientSecret.AsObject.clientId,
clientSecret: data.toObject().clientSecret, clientSecret: resp.clientSecret,
}, },
width: '400px', width: '400px',
}); });
@@ -500,12 +512,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public regenerateAPIClientSecret(): void { public regenerateAPIClientSecret(): void {
this.mgmtService.RegenerateAPIClientSecret(this.app.id, this.projectId).then((data: ClientSecret) => { this.mgmtService.regenerateAPIClientSecret(this.app.id, this.projectId).then(resp => {
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true); this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
this.dialog.open(AppSecretDialogComponent, { this.dialog.open(AppSecretDialogComponent, {
data: { data: {
// clientId: data.toObject().clientId ?? '', // clientId: data.toObject().clientId ?? '',
clientSecret: data.toObject().clientSecret, clientSecret: resp.clientSecret,
}, },
width: '400px', width: '400px',
}); });
@@ -535,8 +547,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
return this.oidcForm.get('grantTypesList'); return this.oidcForm.get('grantTypesList');
} }
public get applicationType(): AbstractControl | null { public get appType(): AbstractControl | null {
return this.oidcForm.get('applicationType'); return this.oidcForm.get('appType');
} }
public get authMethodType(): AbstractControl | null { public get authMethodType(): AbstractControl | null {

View File

@@ -1,5 +1,12 @@
import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component';
import { APIAuthMethodType, APIConfig, OIDCAuthMethodType, OIDCConfig, OIDCGrantType, OIDCResponseType } from 'src/app/proto/generated/management_pb'; import {
APIAuthMethodType,
APIConfig,
OIDCAuthMethodType,
OIDCConfig,
OIDCGrantType,
OIDCResponseType,
} from 'src/app/proto/generated/zitadel/app_pb';
export const CODE_METHOD: RadioItemAuthType = { export const CODE_METHOD: RadioItemAuthType = {
key: 'CODE', key: 'CODE',
@@ -8,9 +15,9 @@ export const CODE_METHOD: RadioItemAuthType = {
disabled: false, disabled: false,
prefix: 'CODE', prefix: 'CODE',
background: 'rgb(89 115 128)', background: 'rgb(89 115 128)',
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
recommended: false, recommended: false,
}; };
export const PKCE_METHOD: RadioItemAuthType = { export const PKCE_METHOD: RadioItemAuthType = {
@@ -20,9 +27,9 @@ export const PKCE_METHOD: RadioItemAuthType = {
disabled: false, disabled: false,
prefix: 'PKCE', prefix: 'PKCE',
background: 'rgb(80 110 92)', background: 'rgb(80 110 92)',
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
recommended: true, recommended: true,
}; };
export const POST_METHOD: RadioItemAuthType = { export const POST_METHOD: RadioItemAuthType = {
@@ -32,9 +39,9 @@ export const POST_METHOD: RadioItemAuthType = {
disabled: false, disabled: false,
prefix: 'POST', prefix: 'POST',
background: 'rgb(144 75 75)', background: 'rgb(144 75 75)',
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
notRecommended: true, notRecommended: true,
}; };
export const PK_JWT_METHOD: RadioItemAuthType = { export const PK_JWT_METHOD: RadioItemAuthType = {
@@ -44,10 +51,10 @@ export const PK_JWT_METHOD: RadioItemAuthType = {
disabled: false, disabled: false,
prefix: 'JWT', prefix: 'JWT',
background: 'rgb(89, 93, 128)', background: 'rgb(89, 93, 128)',
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT, authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
apiAuthMethod: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT, apiAuthMethod: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
// recommended: true, // recommended: true,
}; };
export const BASIC_AUTH_METHOD: RadioItemAuthType = { export const BASIC_AUTH_METHOD: RadioItemAuthType = {
@@ -57,10 +64,10 @@ export const BASIC_AUTH_METHOD: RadioItemAuthType = {
disabled: false, disabled: false,
prefix: 'BASIC', prefix: 'BASIC',
background: 'rgb(144 75 75)', background: 'rgb(144 75 75)',
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE, responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
apiAuthMethod: APIAuthMethodType.APIAUTHMETHODTYPE_BASIC, apiAuthMethod: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC,
}; };
export const IMPLICIT_METHOD: RadioItemAuthType = { export const IMPLICIT_METHOD: RadioItemAuthType = {
@@ -70,9 +77,9 @@ export const IMPLICIT_METHOD: RadioItemAuthType = {
disabled: false, disabled: false,
prefix: 'IMP', prefix: 'IMP',
background: 'rgb(144 75 75)', background: 'rgb(144 75 75)',
responseType: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN,
grantType: OIDCGrantType.OIDCGRANTTYPE_IMPLICIT, grantType: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT,
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
notRecommended: true, notRecommended: true,
}; };
@@ -97,61 +104,61 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
case CODE_METHOD.key: case CODE_METHOD.key:
config = { config = {
oidc: { oidc: {
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
}, },
}; };
return config; return config;
case PKCE_METHOD.key: case PKCE_METHOD.key:
config = { config = {
oidc: { oidc: {
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
} }
}; };
return config; return config;
case POST_METHOD.key: case POST_METHOD.key:
config = { config = {
oidc: { oidc: {
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
} }
}; };
return config; return config;
case PK_JWT_METHOD.key: case PK_JWT_METHOD.key:
config = { config = {
oidc: { oidc: {
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
}, },
api: { api: {
authMethodType: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT, authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
} }
}; };
return config; return config;
case BASIC_AUTH_METHOD.key: case BASIC_AUTH_METHOD.key:
config = { config = {
oidc: { oidc: {
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
}, },
api: { api: {
authMethodType: APIAuthMethodType.APIAUTHMETHODTYPE_BASIC, authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC,
} }
}; };
return config; return config;
case IMPLICIT_METHOD.key: case IMPLICIT_METHOD.key:
config = { config = {
oidc: { oidc: {
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_IMPLICIT], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
}, },
api: { api: {
authMethodType: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT, authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
} }
}; };
return config; return config;
@@ -165,41 +172,41 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
const toCheck = [config.oidc.responseTypesList, config.oidc.grantTypesList, config.oidc.authMethodType]; const toCheck = [config.oidc.responseTypesList, config.oidc.grantTypesList, config.oidc.authMethodType];
const code = JSON.stringify( const code = JSON.stringify(
[ [
[OIDCResponseType.OIDCRESPONSETYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
] ]
); );
const pkce = JSON.stringify( const pkce = JSON.stringify(
[ [
[OIDCResponseType.OIDCRESPONSETYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
] ]
); );
const post = JSON.stringify( const post = JSON.stringify(
[ [
[OIDCResponseType.OIDCRESPONSETYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
] ]
); );
const pk_jwt = JSON.stringify( const pk_jwt = JSON.stringify(
[ [
[OIDCResponseType.OIDCRESPONSETYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
] ]
); );
const implicit = JSON.stringify( const implicit = JSON.stringify(
[ [
[OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN], [OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
[OIDCGrantType.OIDCGRANTTYPE_IMPLICIT], [OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
] ]
); );
@@ -214,8 +221,8 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
} }
} else if (config.api && config.api.authMethodType !== undefined) { } else if (config.api && config.api.authMethodType !== undefined) {
switch (config.api.authMethodType.toString()) { switch (config.api.authMethodType.toString()) {
case APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT.toString(): return PK_JWT_METHOD.key; case APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT.toString(): return PK_JWT_METHOD.key;
case APIAuthMethodType.APIAUTHMETHODTYPE_BASIC.toString(): return BASIC_AUTH_METHOD.key; case APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC.toString(): return BASIC_AUTH_METHOD.key;
default: default:
return CUSTOM_METHOD.key; return CUSTOM_METHOD.key;
} }

View File

@@ -1,4 +1,4 @@
import { OIDCApplicationType } from 'src/app/proto/generated/management_pb'; import { OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
// export enum AppType { // export enum AppType {
// "WEB", // "WEB",
@@ -15,7 +15,7 @@ export enum AppCreateType {
export interface RadioItemAppType { export interface RadioItemAppType {
// key: string; // key: string;
createType: AppCreateType; createType: AppCreateType;
oidcApplicationType?: OIDCApplicationType; oidcAppType?: OIDCAppType;
titleI18nKey: string; titleI18nKey: string;
descI18nKey: string; descI18nKey: string;
prefix: string; prefix: string;
@@ -27,7 +27,7 @@ export const WEB_TYPE = {
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.TITLE', titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.DESCRIPTION', descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.DESCRIPTION',
createType: AppCreateType.OIDC, createType: AppCreateType.OIDC,
oidcApplicationType: OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB, oidcApplicationType: OIDCAppType.OIDC_APP_TYPE_WEB,
prefix: 'WEB', prefix: 'WEB',
background: 'rgb(80, 110, 110)', background: 'rgb(80, 110, 110)',
}; };
@@ -37,7 +37,7 @@ export const USER_AGENT_TYPE = {
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.TITLE', titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.DESCRIPTION', descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.DESCRIPTION',
createType: AppCreateType.OIDC, createType: AppCreateType.OIDC,
oidcApplicationType: OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT, oidcApplicationType: OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
prefix: 'UA', prefix: 'UA',
background: '#6a506e', background: '#6a506e',
}; };
@@ -47,7 +47,7 @@ export const NATIVE_TYPE = {
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.TITLE', titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.TITLE',
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.DESCRIPTION', descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.DESCRIPTION',
createType: AppCreateType.OIDC, createType: AppCreateType.OIDC,
oidcApplicationType: OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE, oidcApplicationType: OIDCAppType.OIDC_APP_TYPE_NATIVE,
prefix: 'N', prefix: 'N',
background: '#595d80', background: '#595d80',
}; };

View File

@@ -1,6 +1,6 @@
<app-meta-layout> <app-meta-layout>
<div class="max-width-container"> <div class="max-width-container">
<div class="head" *ngIf="project?.id"> <div class="head" *ngIf="project?.projectId">
<a [routerLink]="[ '/granted-projects' ]" mat-icon-button> <a [routerLink]="[ '/granted-projects' ]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon> <mat-icon class="icon">arrow_back</mat-icon>
</a> </a>

View File

@@ -1,29 +1,17 @@
import { SelectionModel } from '@angular/cdk/collections';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs'; import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { ChangeType } from 'src/app/modules/changes/changes.component'; import { ChangeType } from 'src/app/modules/changes/changes.component';
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
import { import { Member } from 'src/app/proto/generated/zitadel/member_pb';
Application, import { GrantedProject, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
ApplicationSearchResponse, import { User } from 'src/app/proto/generated/zitadel/user_pb';
ProjectGrantView,
ProjectMember,
ProjectMemberSearchResponse,
ProjectMemberView,
ProjectRole,
ProjectRoleSearchResponse,
ProjectState,
ProjectType,
UserGrantSearchKey,
UserView,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -35,41 +23,22 @@ import { ToastService } from 'src/app/services/toast.service';
export class GrantedProjectDetailComponent implements OnInit, OnDestroy { export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
public projectId: string = ''; public projectId: string = '';
public grantId: string = ''; public grantId: string = '';
public project!: ProjectGrantView.AsObject; public project!: GrantedProject.AsObject;
public pageSizeRoles: number = 10;
public roleDataSource: MatTableDataSource<ProjectRole.AsObject> = new MatTableDataSource<ProjectRole.AsObject>();
public roleResult!: ProjectRoleSearchResponse.AsObject;
public roleColumns: string[] = ['name', 'displayname', 'group', 'actions'];
public pageSizeMembers: number = 10;
public projectDataSource: MatTableDataSource<ProjectMember.AsObject> = new MatTableDataSource<ProjectMember.AsObject>();
public memberResult!: ProjectMemberSearchResponse.AsObject;
public memberColumns: string[] = ['firstname', 'lastname', 'username', 'email', 'roles'];
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
public pageSizeApps: number = 10;
public appsDataSource: MatTableDataSource<Application.AsObject> = new MatTableDataSource<Application.AsObject>();
public appsResult!: ApplicationSearchResponse.AsObject;
public appsColumns: string[] = ['name'];
public ProjectState: any = ProjectState; public ProjectState: any = ProjectState;
public ProjectType: any = ProjectType; public ProjectType: any = ProjectType;
public ChangeType: any = ChangeType; public ChangeType: any = ChangeType;
public grid: boolean = true;
private subscription?: Subscription; private subscription?: Subscription;
public editstate: boolean = false;
public isZitadel: boolean = false; public isZitadel: boolean = false;
UserGrantContext: any = UserGrantContext; UserGrantContext: any = UserGrantContext;
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
// members // members
public totalMemberResult: number = 0; public totalMemberResult: number = 0;
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]> public membersSubject: BehaviorSubject<Member.AsObject[]>
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]); = new BehaviorSubject<Member.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -96,13 +65,15 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
this.projectId = id; this.projectId = id;
this.grantId = grantId; this.grantId = grantId;
this.mgmtService.GetIam().then(iam => { this.mgmtService.getIAM().then(iam => {
this.isZitadel = iam.toObject().iamProjectId === this.projectId; this.isZitadel = iam.iamProjectId === this.projectId;
}); });
if (this.projectId && this.grantId) { if (this.projectId && this.grantId) {
this.mgmtService.GetGrantedProjectByID(this.projectId, this.grantId).then(proj => { this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then(proj => {
this.project = proj.toObject(); if (proj.grantedProject) {
this.project = proj.grantedProject;
}
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
@@ -113,11 +84,13 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
public loadMembers(): void { public loadMembers(): void {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchProjectGrantMembers(this.projectId, from(this.mgmtService.listProjectGrantMembers(this.projectId,
this.grantId, 100, 0)).pipe( this.grantId, 100, 0)).pipe(
map(resp => { map(resp => {
this.totalMemberResult = resp.toObject().totalResult; if (resp.details?.totalResult) {
return resp.toObject().resultList; this.totalMemberResult = resp.details.totalResult;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -140,12 +113,12 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
users.forEach(user => { users.forEach(user => {
return this.mgmtService.AddProjectGrantMember( return this.mgmtService.addProjectGrantMember(
this.projectId, this.projectId,
this.grantId, this.grantId,
user.id, user.id,

View File

@@ -2,8 +2,9 @@ import { animate, animateChild, query, stagger, style, transition, trigger } fro
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Org } from 'src/app/proto/generated/auth_pb'; import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { ProjectGrantView, ProjectState, ProjectType } from 'src/app/proto/generated/management_pb'; import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { GrantedProject, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
import { StorageKey, StorageService } from 'src/app/services/storage.service'; import { StorageKey, StorageService } from 'src/app/services/storage.service';
@Component({ @Component({
@@ -31,12 +32,12 @@ import { StorageKey, StorageService } from 'src/app/services/storage.service';
], ],
}) })
export class GrantedProjectGridComponent implements OnChanges { export class GrantedProjectGridComponent implements OnChanges {
@Input() items: Array<ProjectGrantView.AsObject> = []; @Input() items: Array<GrantedProject.AsObject> = [];
public notPinned: Array<ProjectGrantView.AsObject> = []; public notPinned: Array<GrantedProject.AsObject> = [];
@Output() newClicked: EventEmitter<boolean> = new EventEmitter(); @Output() newClicked: EventEmitter<boolean> = new EventEmitter();
@Output() changedView: EventEmitter<boolean> = new EventEmitter(); @Output() changedView: EventEmitter<boolean> = new EventEmitter();
@Input() loading: boolean = false; @Input() loading: boolean = false;
public selection: SelectionModel<ProjectGrantView.AsObject> = new SelectionModel<ProjectGrantView.AsObject>(true, []); public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
public showNewProject: boolean = false; public showNewProject: boolean = false;
public ProjectState: any = ProjectState; public ProjectState: any = ProjectState;
@@ -71,7 +72,7 @@ export class GrantedProjectGridComponent implements OnChanges {
this.getPrefixedItem('pinned-granted-projects').then(storageEntry => { this.getPrefixedItem('pinned-granted-projects').then(storageEntry => {
if (storageEntry) { if (storageEntry) {
const array: string[] = JSON.parse(storageEntry); const array: string[] = JSON.parse(storageEntry);
const toSelect: ProjectGrantView.AsObject[] = this.items.filter((item, index) => { const toSelect: GrantedProject.AsObject[] = this.items.filter((item, index) => {
if (array.includes(item.projectId)) { if (array.includes(item.projectId)) {
return true; return true;
} }

View File

@@ -7,7 +7,7 @@ import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { ProjectGrantView } from 'src/app/proto/generated/management_pb'; import { GrantedProject } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -39,13 +39,13 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public dataSource: MatTableDataSource<ProjectGrantView.AsObject> = public dataSource: MatTableDataSource<GrantedProject.AsObject> =
new MatTableDataSource<ProjectGrantView.AsObject>(); new MatTableDataSource<GrantedProject.AsObject>();
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public grantedProjectList: ProjectGrantView.AsObject[] = []; public grantedProjectList: GrantedProject.AsObject[] = [];
public displayedColumns: string[] = ['select', 'name', 'resourceOwnerName', 'state', 'creationDate', 'changeDate']; public displayedColumns: string[] = ['select', 'name', 'resourceOwnerName', 'state', 'creationDate', 'changeDate'];
public selection: SelectionModel<ProjectGrantView.AsObject> = new SelectionModel<ProjectGrantView.AsObject>(true, []); public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -89,12 +89,13 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
private async getData(limit: number, offset: number): Promise<void> { private async getData(limit: number, offset: number): Promise<void> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
this.mgmtService.SearchGrantedProjects(limit, offset).then(res => { this.mgmtService.listGrantedProjects(limit, offset).then(resp => {
const response = res.toObject(); this.grantedProjectList = resp.resultList;
this.grantedProjectList = response.resultList; if (resp.details?.totalResult) {
this.totalResult = response.totalResult; this.totalResult = resp.details.totalResult;
if (response.viewTimestamp) { }
this.viewTimestamp = response.viewTimestamp; if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details?.viewTimestamp;
} }
if (this.totalResult > 5) { if (this.totalResult > 5) {
this.grid = false; this.grid = false;

View File

@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from 'src/app/guards/auth.guard'; import { AuthGuard } from 'src/app/guards/auth.guard';
import { RoleGuard } from 'src/app/guards/role.guard'; import { RoleGuard } from 'src/app/guards/role.guard';
import { ProjectType } from 'src/app/proto/generated/management_pb'; import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component'; import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component';
import { GrantedProjectsComponent } from './granted-projects.component'; import { GrantedProjectsComponent } from './granted-projects.component';

View File

@@ -1,8 +1,9 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { Application, OIDCApplicationType, OIDCResponseType } from 'src/app/proto/generated/management_pb'; import { App, OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { NATIVE_TYPE, USER_AGENT_TYPE, WEB_TYPE } from '../../../apps/authtypes'; import { NATIVE_TYPE, USER_AGENT_TYPE, WEB_TYPE } from '../../../apps/authtypes';
@Component({ @Component({
@@ -14,10 +15,10 @@ export class ApplicationGridComponent implements OnInit {
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@Output() public changeView: EventEmitter<void> = new EventEmitter(); @Output() public changeView: EventEmitter<void> = new EventEmitter();
public appsSubject: BehaviorSubject<Application.AsObject[]> = new BehaviorSubject<Application.AsObject[]>([]); public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public OIDCApplicationType: any = OIDCApplicationType; public OIDCApplicationType: any = OIDCAppType;
public NATIVE_TYPE: any = NATIVE_TYPE; public NATIVE_TYPE: any = NATIVE_TYPE;
public WEB_TYPE: any = WEB_TYPE; public WEB_TYPE: any = WEB_TYPE;
@@ -30,14 +31,14 @@ export class ApplicationGridComponent implements OnInit {
} }
public loadApps(): void { public loadApps(): void {
from(this.mgmtService.SearchApplications(this.projectId, 100, 0)).pipe( from(this.mgmtService.listApps(this.projectId, 100, 0)).pipe(
map(resp => { map(resp => {
return resp.toObject().resultList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
).subscribe((apps) => { ).subscribe((apps) => {
this.appsSubject.next(apps as Application.AsObject[]); this.appsSubject.next(apps as App.AsObject[]);
}); });
} }

View File

@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { Application } from 'src/app/proto/generated/management_pb'; import { App } from 'src/app/proto/generated/zitadel/app_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
/** /**
@@ -10,11 +10,11 @@ import { ManagementService } from 'src/app/services/mgmt.service';
* encapsulate all logic for fetching and manipulating the displayed data * encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering). * (including sorting, pagination, and filtering).
*/ */
export class ProjectApplicationsDataSource extends DataSource<Application.AsObject> { export class ProjectApplicationsDataSource extends DataSource<App.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public appsSubject: BehaviorSubject<Application.AsObject[]> = new BehaviorSubject<Application.AsObject[]>([]); public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -26,12 +26,14 @@ export class ProjectApplicationsDataSource extends DataSource<Application.AsObje
const offset = pageIndex * pageSize; const offset = pageIndex * pageSize;
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchApplications(projectId, pageSize, offset)).pipe( from(this.mgmtService.listApps(projectId, pageSize, offset)).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); const response = resp;
this.totalResult = response.totalResult; if (response.details?.totalResult) {
if (response.viewTimestamp) { this.totalResult = response.details.totalResult;
this.viewTimestamp = response.viewTimestamp; }
if (response.details?.viewTimestamp) {
this.viewTimestamp = response.details.viewTimestamp;
} }
return response.resultList; return response.resultList;
}), }),
@@ -48,7 +50,7 @@ export class ProjectApplicationsDataSource extends DataSource<Application.AsObje
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<Application.AsObject[]> { public connect(): Observable<App.AsObject[]> {
return this.appsSubject.asObservable(); return this.appsSubject.asObservable();
} }

View File

@@ -5,9 +5,8 @@ import { MatSort } from '@angular/material/sort';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { merge, of } from 'rxjs'; import { merge, of } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { Application } from 'src/app/proto/generated/management_pb'; import { App } from 'src/app/proto/generated/zitadel/app_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
import { ProjectApplicationsDataSource } from './applications-datasource'; import { ProjectApplicationsDataSource } from './applications-datasource';
@@ -22,13 +21,13 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatSort) public sort!: MatSort; @ViewChild(MatSort) public sort!: MatSort;
@ViewChild(MatTable) public table!: MatTable<Application.AsObject>; @ViewChild(MatTable) public table!: MatTable<App.AsObject>;
public dataSource!: ProjectApplicationsDataSource; public dataSource!: ProjectApplicationsDataSource;
public selection: SelectionModel<Application.AsObject> = new SelectionModel<Application.AsObject>(true, []); public selection: SelectionModel<App.AsObject> = new SelectionModel<App.AsObject>(true, []);
public displayedColumns: string[] = ['select', 'name', 'type']; public displayedColumns: string[] = ['select', 'name', 'type'];
constructor(private mgmtService: ManagementService, private toast: ToastService) { } constructor(private mgmtService: ManagementService) { }
public ngOnInit(): void { public ngOnInit(): void {
this.dataSource = new ProjectApplicationsDataSource(this.mgmtService); this.dataSource = new ProjectApplicationsDataSource(this.mgmtService);
@@ -60,7 +59,7 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
public masterToggle(): void { public masterToggle(): void {
this.isAllSelected() ? this.isAllSelected() ?
this.selection.clear() : this.selection.clear() :
this.dataSource.appsSubject.value.forEach((row: Application.AsObject) => this.selection.select(row)); this.dataSource.appsSubject.value.forEach((row: App.AsObject) => this.selection.select(row));
} }
public refreshPage(): void { public refreshPage(): void {

View File

@@ -1,6 +1,6 @@
<app-meta-layout> <app-meta-layout>
<div class="max-width-container"> <div class="max-width-container">
<div class="head" *ngIf="project?.projectId"> <div class="head" *ngIf="project?.id">
<a [routerLink]="[ '/projects' ]" mat-icon-button> <a [routerLink]="[ '/projects' ]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon> <mat-icon class="icon">arrow_back</mat-icon>
</a> </a>
@@ -22,12 +22,12 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<button mat-stroked-button color="warn" <button mat-stroked-button color="warn"
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.projectId]| hasRole | async) == false" [disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECTSTATE_ACTIVE" *ngIf="project?.state === ProjectState.PROJECTSTATE_ACTIVE"
(click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' | (click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' |
translate}}</button> translate}}</button>
<button mat-stroked-button color="warn" <button mat-stroked-button color="warn"
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.projectId]| hasRole | async) == false" [disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
*ngIf="project?.state === ProjectState.PROJECTSTATE_INACTIVE" *ngIf="project?.state === ProjectState.PROJECTSTATE_INACTIVE"
(click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' | (click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' |
translate}}</button> translate}}</button>
@@ -53,7 +53,7 @@
</div> </div>
<ng-container *ngIf="project"> <ng-container *ngIf="project">
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.projectId, 'project.app.read']"> <ng-template appHasRole [appHasRole]="['project.app.read:' + project.id, 'project.app.read']">
<app-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false" <app-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false"
[projectId]="projectId"></app-application-grid> [projectId]="projectId"></app-application-grid>
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}"> <app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
@@ -68,18 +68,18 @@
<ng-container *ngIf="isZitadel == false"> <ng-container *ngIf="isZitadel == false">
<ng-template appHasRole <ng-template appHasRole
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']"> [appHasRole]="['project.grant.read:' + project.id, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}" <app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}"> description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants <app-project-grants
[refreshOnPreviousRoutes]="['/projects/'+projectId+'/grants/create','/projects/'+projectId+'/roles/create']" [refreshOnPreviousRoutes]="['/projects/'+projectId+'/grants/create','/projects/'+projectId+'/roles/create']"
[disabled]="((['project.grant.write$', 'project.grant.write:'+ project.projectId]| hasRole | async))== false" [disabled]="((['project.grant.write$', 'project.grant.write:'+ project.id]| hasRole | async))== false"
[projectId]="projectId"> [projectId]="projectId">
</app-project-grants> </app-project-grants>
</app-card> </app-card>
</ng-template> </ng-template>
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.projectId, 'project.role.read']"> <ng-template appHasRole [appHasRole]="['project.role.read:' + project.id, 'project.role.read']">
<app-card id="roles" title="{{ 'PROJECT.ROLE.TITLE' | translate }}" <app-card id="roles" title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}"> description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
<p>{{'PROJECT.ROLE.OPTIONS' | translate}}</p> <p>{{'PROJECT.ROLE.OPTIONS' | translate}}</p>
@@ -92,14 +92,14 @@
<p class="desc">{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}</p> <p class="desc">{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}</p>
<div class="divider"></div> <div class="divider"></div>
<app-project-roles <app-project-roles
[disabled]="(['project.role.write$', 'project.role.write:'+ project.projectId]| hasRole | async) == false" [disabled]="(['project.role.write$', 'project.role.write:'+ project.id]| hasRole | async) == false"
[actionsVisible]="true" [projectId]="projectId"> [actionsVisible]="true" [projectId]="projectId">
</app-project-roles> </app-project-roles>
</app-card> </app-card>
</ng-template> </ng-template>
<ng-template appHasRole [appHasRole]="['user.grant.read']"> <ng-template appHasRole [appHasRole]="['user.grant.read']">
<app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}" <app-card *ngIf="project?.id" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}"> description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId" <app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
[refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]" [refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]"
@@ -131,11 +131,11 @@
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}" [membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()" description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()" (showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
[disabled]="(['project.member.write$', 'project.member.write:'+ project.projectId]| hasRole | async) == false"> [disabled]="(['project.member.write$', 'project.member.write:'+ project.id]| hasRole | async) == false">
</app-contributors> </app-contributors>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col"> <mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col">
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.projectId"></app-changes> <app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
</div> </div>

View File

@@ -1,4 +1,3 @@
import { SelectionModel } from '@angular/cdk/collections';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, EventEmitter, OnDestroy, OnInit } from '@angular/core'; import { Component, EventEmitter, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
@@ -9,22 +8,14 @@ import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { ChangeType } from 'src/app/modules/changes/changes.component'; import { ChangeType } from 'src/app/modules/changes/changes.component';
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { import { App } from 'src/app/proto/generated/zitadel/app_pb';
Application, import { ListAppsResponse, UpdateProjectRequest } from 'src/app/proto/generated/zitadel/management_pb';
ApplicationSearchResponse, import { Member } from 'src/app/proto/generated/zitadel/member_pb';
ProjectMember, import { Project, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
ProjectMemberSearchResponse, import { User } from 'src/app/proto/generated/zitadel/user_pb';
ProjectMemberView,
ProjectRole,
ProjectRoleSearchResponse,
ProjectState,
ProjectType,
ProjectView,
UserGrantSearchKey,
UserView,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -36,22 +27,11 @@ import { ToastService } from 'src/app/services/toast.service';
}) })
export class OwnedProjectDetailComponent implements OnInit, OnDestroy { export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
public projectId: string = ''; public projectId: string = '';
public project!: ProjectView.AsObject; public project!: Project.AsObject;
public pageSizeRoles: number = 10;
public roleDataSource: MatTableDataSource<ProjectRole.AsObject> = new MatTableDataSource<ProjectRole.AsObject>();
public roleResult!: ProjectRoleSearchResponse.AsObject;
public roleColumns: string[] = ['name', 'displayname', 'group', 'actions'];
public pageSizeMembers: number = 10;
public projectDataSource: MatTableDataSource<ProjectMember.AsObject> = new MatTableDataSource<ProjectMember.AsObject>();
public memberResult!: ProjectMemberSearchResponse.AsObject;
public memberColumns: string[] = ['firstname', 'lastname', 'username', 'email', 'roles'];
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
public pageSizeApps: number = 10; public pageSizeApps: number = 10;
public appsDataSource: MatTableDataSource<Application.AsObject> = new MatTableDataSource<Application.AsObject>(); public appsDataSource: MatTableDataSource<App.AsObject> = new MatTableDataSource<App.AsObject>();
public appsResult!: ApplicationSearchResponse.AsObject; public appsResult!: ListAppsResponse.AsObject;
public appsColumns: string[] = ['name']; public appsColumns: string[] = ['name'];
public ProjectState: any = ProjectState; public ProjectState: any = ProjectState;
@@ -64,13 +44,12 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
public isZitadel: boolean = false; public isZitadel: boolean = false;
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
public UserGrantContext: any = UserGrantContext; public UserGrantContext: any = UserGrantContext;
// members // members
public totalMemberResult: number = 0; public totalMemberResult: number = 0;
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]> public membersSubject: BehaviorSubject<Member.AsObject[]>
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]); = new BehaviorSubject<Member.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public refreshChanges$: EventEmitter<void> = new EventEmitter(); public refreshChanges$: EventEmitter<void> = new EventEmitter();
@@ -96,12 +75,14 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
private async getData({ id }: Params): Promise<void> { private async getData({ id }: Params): Promise<void> {
this.projectId = id; this.projectId = id;
this.mgmtService.GetIam().then(iam => { this.mgmtService.getIAM().then(iam => {
this.isZitadel = iam.toObject().iamProjectId === this.projectId; this.isZitadel = iam.iamProjectId === this.projectId;
}); });
this.mgmtService.GetProjectById(id).then(proj => { this.mgmtService.getProjectByID(id).then(resp => {
this.project = proj.toObject(); if (resp.project) {
this.project = resp.project;
}
}).catch(error => { }).catch(error => {
console.error(error); console.error(error);
this.toast.showError(error); this.toast.showError(error);
@@ -112,10 +93,12 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
public loadMembers(): void { public loadMembers(): void {
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchProjectMembers(this.projectId, 100, 0)).pipe( from(this.mgmtService.listProjectMembers(this.projectId, 100, 0)).pipe(
map(resp => { map(resp => {
this.totalMemberResult = resp.toObject().totalResult; if (resp.details?.totalResult) {
return resp.toObject().resultList; this.totalMemberResult = resp.details?.totalResult;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -125,7 +108,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
} }
public changeState(newState: ProjectState): void { public changeState(newState: ProjectState): void {
if (newState === ProjectState.PROJECTSTATE_ACTIVE) { if (newState === ProjectState.PROJECT_STATE_ACTIVE) {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
confirmKey: 'ACTIONS.REACTIVATE', confirmKey: 'ACTIONS.REACTIVATE',
@@ -137,9 +120,9 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.mgmtService.ReactivateProject(this.projectId).then(() => { this.mgmtService.reactivateProject(this.projectId).then(() => {
this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true); this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true);
this.project.state = ProjectState.PROJECTSTATE_ACTIVE; this.project.state = ProjectState.PROJECT_STATE_ACTIVE;
this.refreshChanges$.emit(); this.refreshChanges$.emit();
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -147,7 +130,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
} }
}); });
} else if (newState === ProjectState.PROJECTSTATE_INACTIVE) { } else if (newState === ProjectState.PROJECT_STATE_INACTIVE) {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
confirmKey: 'ACTIONS.DEACTIVATE', confirmKey: 'ACTIONS.DEACTIVATE',
@@ -159,9 +142,9 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.mgmtService.DeactivateProject(this.projectId).then(() => { this.mgmtService.deactivateProject(this.projectId).then(() => {
this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true); this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true);
this.project.state = ProjectState.PROJECTSTATE_INACTIVE; this.project.state = ProjectState.PROJECT_STATE_INACTIVE;
this.refreshChanges$.emit(); this.refreshChanges$.emit();
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -183,7 +166,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
this.mgmtService.RemoveProject(this.projectId).then(() => { this.mgmtService.removeProject(this.projectId).then(() => {
this.toast.showInfo('PROJECT.TOAST.DELETED', true); this.toast.showInfo('PROJECT.TOAST.DELETED', true);
const params: Params = { const params: Params = {
'deferredReload': true, 'deferredReload': true,
@@ -197,7 +180,13 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
} }
public saveProject(): void { public saveProject(): void {
this.mgmtService.UpdateProject(this.project.projectId, this.project).then(() => { const req = new UpdateProjectRequest();
req.setId(this.project.id);
req.setName(this.project.name);
req.setProjectRoleAssertion(this.project.projectRoleAssertion);
req.setProjectRoleCheck(this.project.projectRoleCheck);
this.mgmtService.updateProject(req).then(() => {
this.toast.showInfo('PROJECT.TOAST.UPDATED', true); this.toast.showInfo('PROJECT.TOAST.UPDATED', true);
this.refreshChanges$.emit(); this.refreshChanges$.emit();
}).catch(error => { }).catch(error => {
@@ -218,19 +207,19 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
const dialogRef = this.dialog.open(MemberCreateDialogComponent, { const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
data: { data: {
creationType: CreationType.PROJECT_OWNED, creationType: CreationType.PROJECT_OWNED,
projectId: this.project.projectId, projectId: this.project.id,
}, },
width: '400px', width: '400px',
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp) { if (resp) {
const users: UserView.AsObject[] = resp.users; const users: User.AsObject[] = resp.users;
const roles: string[] = resp.roles; const roles: string[] = resp.roles;
if (users && users.length && roles && roles.length) { if (users && users.length && roles && roles.length) {
users.forEach(user => { users.forEach(user => {
return this.mgmtService.AddProjectMember(this.projectId, user.id, roles) return this.mgmtService.addProjectMember(this.projectId, user.id, roles)
.then(() => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true); this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
setTimeout(() => { setTimeout(() => {
@@ -246,6 +235,6 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
} }
public showDetail(): void { public showDetail(): void {
this.router.navigate(['projects', this.project.projectId, 'members']); this.router.navigate(['projects', this.project.id, 'members']);
} }
} }

View File

@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { ProjectGrant } from 'src/app/proto/generated/management_pb'; import { GrantedProject } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
/** /**
@@ -10,10 +10,10 @@ import { ManagementService } from 'src/app/services/mgmt.service';
* encapsulate all logic for fetching and manipulating the displayed data * encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering). * (including sorting, pagination, and filtering).
*/ */
export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> { export class ProjectGrantsDataSource extends DataSource<GrantedProject.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public grantsSubject: BehaviorSubject<ProjectGrant.AsObject[]> = new BehaviorSubject<ProjectGrant.AsObject[]>([]); public grantsSubject: BehaviorSubject<GrantedProject.AsObject[]> = new BehaviorSubject<GrantedProject.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -25,14 +25,15 @@ export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
const offset = pageIndex * pageSize; const offset = pageIndex * pageSize;
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchProjectGrants(projectId, pageSize, offset)).pipe( from(this.mgmtService.listProjectGrants(projectId, pageSize, offset)).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); if (resp.details?.totalResult) {
this.totalResult = response.totalResult; this.totalResult = resp.details.totalResult;
if (response.viewTimestamp) {
this.viewTimestamp = response.viewTimestamp;
} }
return response.resultList; if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details?.viewTimestamp;
}
return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -47,7 +48,7 @@ export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<ProjectGrant.AsObject[]> { public connect(): Observable<GrantedProject.AsObject[]> {
return this.grantsSubject.asObservable(); return this.grantsSubject.asObservable();
} }

View File

@@ -5,7 +5,7 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { ProjectGrant, ProjectRoleView } from 'src/app/proto/generated/management_pb'; import { GrantedProject, Role } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -28,10 +28,10 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<ProjectGrant.AsObject>; @ViewChild(MatTable) public table!: MatTable<GrantedProject.AsObject>;
public dataSource!: ProjectGrantsDataSource; public dataSource!: ProjectGrantsDataSource;
public selection: SelectionModel<ProjectGrant.AsObject> = new SelectionModel<ProjectGrant.AsObject>(true, []); public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
public memberRoleOptions: ProjectRoleView.AsObject[] = []; public memberRoleOptions: Role.AsObject[] = [];
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'grantedOrgName', 'dates']; public displayedColumns: string[] = ['select', 'grantedOrgName', 'dates'];
@@ -73,14 +73,14 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
} }
public getRoleOptions(projectId: string): void { public getRoleOptions(projectId: string): void {
this.mgmtService.SearchProjectRoles(projectId, 100, 0).then(resp => { this.mgmtService.listProjectRoles(projectId, 100, 0).then(resp => {
this.memberRoleOptions = resp.toObject().resultList; this.memberRoleOptions = resp.resultList;
}); });
} }
updateRoles(grant: ProjectGrant.AsObject, selectionChange: MatSelectChange): void { updateRoles(grant: GrantedProject.AsObject, selectionChange: MatSelectChange): void {
this.mgmtService.UpdateProjectGrant(grant.id, grant.projectId, selectionChange.value) this.mgmtService.updateProjectGrant(grant.grantId, grant.projectId, selectionChange.value)
.then((newgrant: ProjectGrant) => { .then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTCHANGED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTCHANGED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -89,14 +89,14 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
deleteSelectedGrants(): void { deleteSelectedGrants(): void {
const promises = this.selection.selected.map(grant => { const promises = this.selection.selected.map(grant => {
return this.mgmtService.RemoveProjectGrant(grant.id, grant.projectId); return this.mgmtService.removeProjectGrant(grant.grantId, grant.projectId);
}); });
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true); this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true);
const data = this.dataSource.grantsSubject.getValue(); const data = this.dataSource.grantsSubject.getValue();
this.selection.selected.forEach((item) => { this.selection.selected.forEach((item) => {
const index = data.findIndex(i => i.id === item.id); const index = data.findIndex(i => i.grantId === item.grantId);
if (index > -1) { if (index > -1) {
data.splice(index, 1); data.splice(index, 1);
this.dataSource.grantsSubject.next(data); this.dataSource.grantsSubject.next(data);

View File

@@ -3,9 +3,10 @@ import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { Org } from 'src/app/proto/generated/auth_pb'; import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { ProjectState, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb'; import { Project, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { StorageKey, StorageService } from 'src/app/services/storage.service'; import { StorageKey, StorageService } from 'src/app/services/storage.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -39,14 +40,14 @@ import { ToastService } from 'src/app/services/toast.service';
], ],
}) })
export class OwnedProjectGridComponent implements OnChanges { export class OwnedProjectGridComponent implements OnChanges {
@Input() items: Array<ProjectView.AsObject> = []; @Input() items: Array<Project.AsObject> = [];
public notPinned: Array<ProjectView.AsObject> = []; public notPinned: Array<Project.AsObject> = [];
@Output() newClicked: EventEmitter<boolean> = new EventEmitter(); @Output() newClicked: EventEmitter<boolean> = new EventEmitter();
@Output() changedView: EventEmitter<boolean> = new EventEmitter(); @Output() changedView: EventEmitter<boolean> = new EventEmitter();
@Input() loading: boolean = false; @Input() loading: boolean = false;
public selection: SelectionModel<ProjectView.AsObject> = new SelectionModel<ProjectView.AsObject>(true, []); public selection: SelectionModel<Project.AsObject> = new SelectionModel<Project.AsObject>(true, []);
public showNewProject: boolean = false; public showNewProject: boolean = false;
public ProjectState: any = ProjectState; public ProjectState: any = ProjectState;
@@ -61,10 +62,10 @@ export class OwnedProjectGridComponent implements OnChanges {
) { ) {
this.selection.changed.subscribe(selection => { this.selection.changed.subscribe(selection => {
this.setPrefixedItem('pinned-projects', JSON.stringify( this.setPrefixedItem('pinned-projects', JSON.stringify(
this.selection.selected.map(item => item.projectId), this.selection.selected.map(item => item.id),
)).then(() => { )).then(() => {
selection.added.forEach(item => { selection.added.forEach(item => {
const index = this.notPinned.findIndex(i => i.projectId === item.projectId); const index = this.notPinned.findIndex(i => i.id === item.id);
this.notPinned.splice(index, 1); this.notPinned.splice(index, 1);
}); });
this.notPinned.push(...selection.removed); this.notPinned.push(...selection.removed);
@@ -72,11 +73,11 @@ export class OwnedProjectGridComponent implements OnChanges {
}); });
} }
public selectItem(item: ProjectView.AsObject, event?: any): void { public selectItem(item: Project.AsObject, event?: any): void {
if (event && !event.target.classList.contains('mat-icon')) { if (event && !event.target.classList.contains('mat-icon')) {
this.router.navigate(['/projects', item.projectId]); this.router.navigate(['/projects', item.id]);
} else if (!event) { } else if (!event) {
this.router.navigate(['/projects', item.projectId]); this.router.navigate(['/projects', item.id]);
} }
} }
@@ -95,8 +96,8 @@ export class OwnedProjectGridComponent implements OnChanges {
this.getPrefixedItem('pinned-projects').then(storageEntry => { this.getPrefixedItem('pinned-projects').then(storageEntry => {
if (storageEntry) { if (storageEntry) {
const array: string[] = JSON.parse(storageEntry); const array: string[] = JSON.parse(storageEntry);
const toSelect: ProjectView.AsObject[] = this.items.filter((item, index) => { const toSelect: Project.AsObject[] = this.items.filter((item, index) => {
if (array.includes(item.projectId)) { if (array.includes(item.id)) {
return true; return true;
} }
}); });
@@ -125,12 +126,12 @@ export class OwnedProjectGridComponent implements OnChanges {
this.changedView.emit(true); this.changedView.emit(true);
} }
public toggle(item: ProjectView.AsObject, event: any): void { public toggle(item: Project.AsObject, event: any): void {
event.stopPropagation(); event.stopPropagation();
this.selection.toggle(item); this.selection.toggle(item);
} }
public deleteProject(event: any, item: ProjectView.AsObject): void { public deleteProject(event: any, item: Project.AsObject): void {
event.stopPropagation(); event.stopPropagation();
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
@@ -143,20 +144,20 @@ export class OwnedProjectGridComponent implements OnChanges {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp && item.projectId !== this.zitadelProjectId) { if (resp && item.id !== this.zitadelProjectId) {
this.mgmtService.RemoveProject(item.projectId).then(() => { this.mgmtService.removeProject(item.id).then(() => {
this.toast.showInfo('PROJECT.TOAST.DELETED', true); this.toast.showInfo('PROJECT.TOAST.DELETED', true);
const index = this.items.findIndex(iter => iter.projectId === item.projectId); const index = this.items.findIndex(iter => iter.id === item.id);
if (index > -1) { if (index > -1) {
this.items.splice(index, 1); this.items.splice(index, 1);
} }
const indexSelection = this.selection.selected.findIndex(iter => iter.projectId === item.projectId); const indexSelection = this.selection.selected.findIndex(iter => iter.id === item.id);
if (indexSelection > -1) { if (indexSelection > -1) {
this.selection.selected.splice(indexSelection, 1); this.selection.selected.splice(indexSelection, 1);
} }
const indexPinned = this.notPinned.findIndex(iter => iter.projectId === item.projectId); const indexPinned = this.notPinned.findIndex(iter => iter.id === item.id);
if (indexPinned > -1) { if (indexPinned > -1) {
this.notPinned.splice(indexPinned, 1); this.notPinned.splice(indexPinned, 1);
} }

View File

@@ -10,7 +10,7 @@ import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { ProjectView } from 'src/app/proto/generated/management_pb'; import { Project } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -42,14 +42,14 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public dataSource: MatTableDataSource<ProjectView.AsObject> = public dataSource: MatTableDataSource<Project.AsObject> =
new MatTableDataSource<ProjectView.AsObject>(); new MatTableDataSource<Project.AsObject>();
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatPaginator) public paginator!: MatPaginator;
public ownedProjectList: ProjectView.AsObject[] = []; public ownedProjectList: Project.AsObject[] = [];
public displayedColumns: string[] = ['select', 'name', 'state', 'creationDate', 'changeDate', 'actions']; public displayedColumns: string[] = ['select', 'name', 'state', 'creationDate', 'changeDate', 'actions'];
public selection: SelectionModel<ProjectView.AsObject> = new SelectionModel<ProjectView.AsObject>(true, []); public selection: SelectionModel<Project.AsObject> = new SelectionModel<Project.AsObject>(true, []);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -66,8 +66,8 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
private toast: ToastService, private toast: ToastService,
private dialog: MatDialog, private dialog: MatDialog,
) { ) {
this.mgmtService.GetIam().then(iam => { this.mgmtService.getIAM().then(iam => {
this.zitadelProjectId = iam.toObject().iamProjectId; this.zitadelProjectId = iam.iamProjectId;
}); });
} }
@@ -108,15 +108,16 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
private async getData(limit?: number, offset?: number): Promise<void> { private async getData(limit?: number, offset?: number): Promise<void> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
this.mgmtService.SearchProjects(limit, offset).then(res => { this.mgmtService.listProjects(limit, offset).then(resp => {
const response = res.toObject(); this.ownedProjectList = resp.resultList;
this.ownedProjectList = response.resultList; if (resp.details?.totalResult) {
this.totalResult = response.totalResult; this.totalResult = resp.details.totalResult;
}
if (this.totalResult > 10) { if (this.totalResult > 10) {
this.grid = false; this.grid = false;
} }
if (response.viewTimestamp) { if (resp.details?.viewTimestamp) {
this.viewTimestamp = response.viewTimestamp; this.viewTimestamp = resp.details?.viewTimestamp;
} }
this.dataSource.data = this.ownedProjectList; this.dataSource.data = this.ownedProjectList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
@@ -131,7 +132,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
public reactivateSelectedProjects(): void { public reactivateSelectedProjects(): void {
const promises = this.selection.selected.map(project => { const promises = this.selection.selected.map(project => {
this.mgmtService.ReactivateProject(project.projectId); this.mgmtService.reactivateProject(project.id);
}); });
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
@@ -144,7 +145,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
public deactivateSelectedProjects(): void { public deactivateSelectedProjects(): void {
const promises = this.selection.selected.map(project => { const promises = this.selection.selected.map(project => {
this.mgmtService.DeactivateProject(project.projectId); this.mgmtService.deactivateProject(project.id);
}); });
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
@@ -159,7 +160,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize); this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
} }
public deleteProject(item: ProjectView.AsObject): void { public deleteProject(item: Project.AsObject): void {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
confirmKey: 'ACTIONS.DELETE', confirmKey: 'ACTIONS.DELETE',
@@ -171,8 +172,8 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (this.zitadelProjectId && resp && item.projectId !== this.zitadelProjectId) { if (this.zitadelProjectId && resp && item.id !== this.zitadelProjectId) {
this.mgmtService.RemoveProject(item.projectId).then(() => { this.mgmtService.removeProject(item.id).then(() => {
this.toast.showInfo('PROJECT.TOAST.DELETED', true); this.toast.showInfo('PROJECT.TOAST.DELETED', true);
setTimeout(() => { setTimeout(() => {
this.refreshPage(); this.refreshPage();

View File

@@ -1,7 +1,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { RoleGuard } from 'src/app/guards/role.guard'; import { RoleGuard } from 'src/app/guards/role.guard';
import { ProjectType } from 'src/app/proto/generated/management_pb'; import { ProjectType } from 'src/app/modules/project-members/project-members.component';
import { OwnedProjectsComponent } from './owned-projects.component'; import { OwnedProjectsComponent } from './owned-projects.component';

View File

@@ -10,7 +10,7 @@
<div class="row"> <div class="row">
<span class="first">{{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}}</span> <span class="first">{{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<span>{{grant?.resourceOwnerName}}</span> <span>{{grant?.details?.resourceOwner}}</span>
</div> </div>
</div> </div>
@@ -25,9 +25,9 @@
</div> </div>
</div> </div>
<cnsl-form-field class="formfield" appearance="outline" *ngIf="grant && grant.roleKeysList"> <cnsl-form-field class="formfield" appearance="outline" *ngIf="grant && grant.grantedRoleKeysList">
<cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label> <cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label>
<mat-select [(ngModel)]="grant.roleKeysList" multiple (selectionChange)="updateRoles($event)"> <mat-select [(ngModel)]="grant.grantedRoleKeysList" multiple (selectionChange)="updateRoles($event)">
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key"> <mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
{{role.key}} {{role.key}}
</mat-option> </mat-option>
@@ -39,17 +39,17 @@
<h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1> <h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1>
<p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p> <p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
<app-members-table *ngIf="grant" style="width: 100%;" [dataSource]="dataSource" [canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.id] | hasRole | async" <app-members-table *ngIf="grant" style="width: 100%;" [dataSource]="dataSource" [canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async"
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)" [memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)"
[factoryLoadFunc]="changePageFactory" (changedSelection)="selection = $event" [refreshTrigger]="changePage"> [factoryLoadFunc]="changePageFactory" (changedSelection)="selection = $event" [refreshTrigger]="changePage">
<button selectactions (click)="removeProjectMemberSelection()" <button selectactions (click)="removeProjectMemberSelection()"
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.id] | hasRole | async) == false" [disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) == false"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" color="warn" mat-raised-button> matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" color="warn" mat-raised-button>
<i class="las la-trash"></i> <i class="las la-trash"></i>
{{'ACTIONS.SELECTIONDELETE' | translate}} {{'ACTIONS.SELECTIONDELETE' | translate}}
</button> </button>
<a writeactions color="primary" <a writeactions color="primary"
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.id] | hasRole | async) == false" [disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) == false"
(click)="openAddMember()" color="primary" mat-raised-button> (click)="openAddMember()" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>

View File

@@ -3,15 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
import { PageEvent } from '@angular/material/paginator'; import { PageEvent } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { import { ProjectType } from 'src/app/modules/project-members/project-members.component';
ProjectGrant, import { Member } from 'src/app/proto/generated/zitadel/member_pb';
ProjectGrantMember, import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb';
ProjectGrantMemberView,
ProjectGrantState,
ProjectGrantView,
ProjectRoleView,
ProjectType,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -29,7 +23,7 @@ import { ProjectGrantMembersDataSource } from './project-grant-members-datasourc
export class ProjectGrantDetailComponent { export class ProjectGrantDetailComponent {
public INITIALPAGESIZE: number = 25; public INITIALPAGESIZE: number = 25;
public grant!: ProjectGrantView.AsObject; public grant!: GrantedProject.AsObject;
public projectid: string = ''; public projectid: string = '';
public grantid: string = ''; public grantid: string = '';
@@ -39,12 +33,12 @@ export class ProjectGrantDetailComponent {
public isZitadel: boolean = false; public isZitadel: boolean = false;
ProjectGrantState: any = ProjectGrantState; ProjectGrantState: any = ProjectGrantState;
public projectRoleOptions: ProjectRoleView.AsObject[] = []; public projectRoleOptions: Role.AsObject[] = [];
public memberRoleOptions: Array<string> = []; public memberRoleOptions: Array<string> = [];
public changePageFactory!: Function; public changePageFactory!: Function;
public changePage: EventEmitter<void> = new EventEmitter(); public changePage: EventEmitter<void> = new EventEmitter();
public selection: Array<ProjectGrantMemberView.AsObject> = []; public selection: Array<Member.AsObject> = [];
public dataSource!: ProjectGrantMembersDataSource; public dataSource!: ProjectGrantMembersDataSource;
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,
@@ -71,22 +65,24 @@ export class ProjectGrantDetailComponent {
); );
}; };
this.mgmtService.ProjectGrantByID(this.grantid, this.projectid).then((grant) => { this.mgmtService.getProjectGrantByID(this.grantid, this.projectid).then((resp) => {
this.grant = grant.toObject(); if (resp.projectGrant) {
this.grant = resp.projectGrant;
}
}); });
}); });
} }
public changeState(newState: ProjectGrantState): void { public changeState(newState: ProjectGrantState): void {
if (newState === ProjectGrantState.PROJECTGRANTSTATE_ACTIVE) { if (newState === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE) {
this.mgmtService.ReactivateProjectGrant(this.grantid, this.projectid).then(() => { this.mgmtService.reactivateProjectGrant(this.grantid, this.projectid).then(() => {
this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true); this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true);
this.grant.state = newState; this.grant.state = newState;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (newState === ProjectGrantState.PROJECTGRANTSTATE_INACTIVE) { } else if (newState === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE) {
this.mgmtService.DeactivateProjectGrant(this.grantid, this.projectid).then(() => { this.mgmtService.deactivateProjectGrant(this.grantid, this.projectid).then(() => {
this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true); this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true);
this.grant.state = newState; this.grant.state = newState;
}).catch(error => { }).catch(error => {
@@ -96,22 +92,22 @@ export class ProjectGrantDetailComponent {
} }
public getRoleOptions(projectId: string): void { public getRoleOptions(projectId: string): void {
this.mgmtService.SearchProjectRoles(projectId, 100, 0).then(resp => { this.mgmtService.listProjectRoles(projectId, 100, 0).then(resp => {
this.projectRoleOptions = resp.toObject().resultList; this.projectRoleOptions = resp.resultList;
}); });
} }
public getMemberRoleOptions(): void { public getMemberRoleOptions(): void {
this.mgmtService.GetProjectGrantMemberRoles().then(resp => { this.mgmtService.listProjectGrantMemberRoles().then(resp => {
this.memberRoleOptions = resp.toObject().rolesList; this.memberRoleOptions = resp.resultList;
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
updateRoles(selectionChange: MatSelectChange): void { updateRoles(selectionChange: MatSelectChange): void {
this.mgmtService.UpdateProjectGrant(this.grant.id, this.grant.projectId, selectionChange.value) this.mgmtService.updateProjectGrant(this.grant.grantId, this.grant.projectId, selectionChange.value)
.then((newgrant: ProjectGrant) => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.GRANTUPDATED'); this.toast.showInfo('PROJECT.TOAST.GRANTUPDATED');
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@@ -120,7 +116,7 @@ export class ProjectGrantDetailComponent {
public removeProjectMemberSelection(): void { public removeProjectMemberSelection(): void {
Promise.all(this.selection.map(member => { Promise.all(this.selection.map(member => {
return this.mgmtService.RemoveProjectGrantMember(this.grant.projectId, this.grant.id, member.userId).then(() => { return this.mgmtService.removeProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId).then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERREMOVED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERREMOVED', true);
setTimeout(() => { setTimeout(() => {
this.changePage.emit(); this.changePage.emit();
@@ -132,11 +128,11 @@ export class ProjectGrantDetailComponent {
} }
public async openAddMember(): Promise<any> { public async openAddMember(): Promise<any> {
const keysList = (await this.mgmtService.GetProjectGrantMemberRoles()).toObject(); const keysList = (await this.mgmtService.listProjectGrantMemberRoles());
const dialogRef = this.dialog.open(ProjectGrantMembersCreateDialogComponent, { const dialogRef = this.dialog.open(ProjectGrantMembersCreateDialogComponent, {
data: { data: {
roleKeysList: keysList.rolesList, roleKeysList: keysList.resultList,
}, },
width: '400px', width: '400px',
}); });
@@ -144,9 +140,9 @@ export class ProjectGrantDetailComponent {
dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => { dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => {
if (dataToAdd) { if (dataToAdd) {
Promise.all(dataToAdd.userIds.map((userid: string) => { Promise.all(dataToAdd.userIds.map((userid: string) => {
return this.mgmtService.AddProjectGrantMember( return this.mgmtService.addProjectGrantMember(
this.grant.projectId, this.grant.projectId,
this.grant.id, this.grant.grantId,
userid, userid,
dataToAdd.rolesKeyList, dataToAdd.rolesKeyList,
); );
@@ -162,9 +158,9 @@ export class ProjectGrantDetailComponent {
}); });
} }
updateMemberRoles(member: ProjectGrantMember.AsObject, selectionChange: MatSelectChange): void { updateMemberRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
this.mgmtService.ChangeProjectGrantMember(this.grant.projectId, this.grant.id, member.userId, selectionChange.value) this.mgmtService.updateProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId, selectionChange.value)
.then((_: ProjectGrantMember) => { .then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERCHANGED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERCHANGED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -1,7 +1,7 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { UserView } from 'src/app/proto/generated/management_pb'; import { User } from 'src/app/proto/generated/zitadel/user_pb';
export interface ProjectGrantMembersCreateDialogExportType { export interface ProjectGrantMembersCreateDialogExportType {
userIds: string[]; userIds: string[];
@@ -22,7 +22,7 @@ export class ProjectGrantMembersCreateDialogComponent {
@Inject(MAT_DIALOG_DATA) public data: any, @Inject(MAT_DIALOG_DATA) public data: any,
) { } ) { }
public selectUsers(users: UserView.AsObject[]): void { public selectUsers(users: User.AsObject[]): void {
this.userIds = users.map(user => user.id); this.userIds = users.map(user => user.id);
} }

View File

@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs'; import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { ProjectMember } from 'src/app/proto/generated/management_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
/** /**
@@ -10,11 +10,11 @@ import { ManagementService } from 'src/app/services/mgmt.service';
* encapsulate all logic for fetching and manipulating the displayed data * encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering). * (including sorting, pagination, and filtering).
*/ */
export class ProjectGrantMembersDataSource extends DataSource<ProjectMember.AsObject> { export class ProjectGrantMembersDataSource extends DataSource<Member.AsObject> {
public totalResult: number = 0; public totalResult: number = 0;
public viewTimestamp!: Timestamp.AsObject; public viewTimestamp!: Timestamp.AsObject;
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]); public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@@ -28,15 +28,14 @@ export class ProjectGrantMembersDataSource extends DataSource<ProjectMember.AsOb
this.loadingSubject.next(true); this.loadingSubject.next(true);
from(this.mgmtService.SearchProjectGrantMembers(projectId, from(this.mgmtService.listProjectGrantMembers(projectId,
grantId, pageSize, offset)).pipe( grantId, pageSize, offset)).pipe(
map(resp => { map(resp => {
const response = resp.toObject(); this.totalResult = resp.details?.totalResult || 0;
this.totalResult = response.totalResult; if (resp.details?.viewTimestamp) {
if (response.viewTimestamp) { this.viewTimestamp = resp.details?.viewTimestamp;
this.viewTimestamp = response.viewTimestamp;
} }
return response.resultList; return resp.resultList;
}), }),
catchError(() => of([])), catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)), finalize(() => this.loadingSubject.next(false)),
@@ -51,7 +50,7 @@ export class ProjectGrantMembersDataSource extends DataSource<ProjectMember.AsOb
* the returned stream emits new items. * the returned stream emits new items.
* @returns A stream of the items to be rendered. * @returns A stream of the items to be rendered.
*/ */
public connect(): Observable<ProjectMember.AsObject[]> { public connect(): Observable<Member.AsObject[]> {
return this.membersSubject.asObservable(); return this.membersSubject.asObservable();
} }

View File

@@ -1,7 +1,7 @@
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Project, ProjectCreateRequest } from 'src/app/proto/generated/management_pb'; import { AddProjectRequest, AddProjectResponse } from 'src/app/proto/generated/zitadel/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -11,7 +11,7 @@ import { ToastService } from 'src/app/services/toast.service';
styleUrls: ['./project-create.component.scss'], styleUrls: ['./project-create.component.scss'],
}) })
export class ProjectCreateComponent implements OnInit { export class ProjectCreateComponent implements OnInit {
public project: ProjectCreateRequest.AsObject = new ProjectCreateRequest().toObject(); public project: AddProjectRequest.AsObject = new AddProjectRequest().toObject();
constructor( constructor(
private router: Router, private router: Router,
@@ -26,9 +26,9 @@ export class ProjectCreateComponent implements OnInit {
public saveProject(): void { public saveProject(): void {
this.mgmtService this.mgmtService
.CreateProject(this.project) .addProject(this.project)
.then((data: Project) => { .then((resp: AddProjectResponse.AsObject) => {
this.router.navigate(['projects', data.getId()]); this.router.navigate(['projects', resp.id]);
}) })
.catch(error => { .catch(error => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -2,7 +2,8 @@ import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Org, ProjectRole } from 'src/app/proto/generated/management_pb'; import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -42,13 +43,15 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
public searchOrg(domain: string): void { public searchOrg(domain: string): void {
this.mgmtService.getOrgByDomainGlobal(domain).then((ret) => { this.mgmtService.getOrgByDomainGlobal(domain).then((ret) => {
const tmp = ret.toObject(); if (ret.org) {
this.authService.GetActiveOrg().then((org) => { const tmp = ret.org;
if (tmp !== org) { this.authService.getActiveOrg().then((org) => {
this.org = tmp; if (tmp !== org) {
} this.org = tmp;
}); }
this.org = ret.toObject(); });
this.org = ret.org;
}
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
@@ -60,8 +63,8 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
public addGrant(): void { public addGrant(): void {
this.mgmtService this.mgmtService
.CreateProjectGrant(this.org.id, this.projectId, this.rolesKeyList) .addProjectGrant(this.org.id, this.projectId, this.rolesKeyList)
.then((data) => { .then(() => {
this.close(); this.close();
}) })
.catch(error => { .catch(error => {
@@ -69,7 +72,7 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
}); });
} }
public selectRoles(roles: ProjectRole.AsObject[]): void { public selectRoles(roles: Role.AsObject[]): void {
this.rolesKeyList = roles.map(role => role.key); this.rolesKeyList = roles.map(role => role.key);
} }

View File

@@ -4,7 +4,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ProjectRoleAdd } from 'src/app/proto/generated/management_pb'; import { BulkAddProjectRolesRequest } from 'src/app/proto/generated/zitadel/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -84,16 +84,15 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
} }
public addRole(): void { public addRole(): void {
const rolesToAdd: ProjectRoleAdd[] = this.formArray.value.map((element: any) => { const rolesToAdd: BulkAddProjectRolesRequest.Role[] = this.formArray.value.map((element: any) => {
const role = new ProjectRoleAdd(); const role = new BulkAddProjectRolesRequest.Role;
role.setKey(element.key); role.setKey(element.key);
role.setDisplayName(element.displayName); role.setDisplayName(element.displayName);
role.setGroup(element.group); role.setGroup(element.group);
role.setId(this.projectId);
return role; return role;
}); });
this.mgmtService.BulkAddProjectRole(this.projectId, rolesToAdd).then(() => { this.mgmtService.bulkAddProjectRoles(this.projectId, rolesToAdd).then(() => {
this.router.navigate(['projects', this.projectId]); this.router.navigate(['projects', this.projectId]);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -4,8 +4,9 @@ import { ActivatedRoute, Params } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { UserTarget } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component'; import { UserTarget } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
import { Org } from 'src/app/proto/generated/auth_pb'; import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { ProjectGrantView, ProjectRole, ProjectView, UserGrant, UserView } from 'src/app/proto/generated/management_pb'; import { GrantedProject, Project, Role } from 'src/app/proto/generated/zitadel/project_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -22,7 +23,7 @@ export class UserGrantCreateComponent implements OnDestroy {
public userId: string = ''; public userId: string = '';
public projectId: string = ''; public projectId: string = '';
public project!: ProjectGrantView.AsObject | ProjectView.AsObject; public project!: GrantedProject.AsObject | Project.AsObject;
public grantId: string = ''; public grantId: string = '';
public rolesList: string[] = []; public rolesList: string[] = [];
@@ -38,11 +39,11 @@ export class UserGrantCreateComponent implements OnDestroy {
public grantRolesKeyList: string[] = []; public grantRolesKeyList: string[] = [];
public user!: UserView.AsObject; public user!: User.AsObject;
public UserTarget: any = UserTarget; public UserTarget: any = UserTarget;
public ProjectGrantView: any = ProjectGrantView; public ProjectGrantView: any = GrantedProject;
public ProjectView: any = ProjectView; public ProjectView: any = Project;
constructor( constructor(
private userService: ManagementService, private userService: ManagementService,
private toast: ToastService, private toast: ToastService,
@@ -63,22 +64,26 @@ export class UserGrantCreateComponent implements OnDestroy {
this.context = UserGrantContext.OWNED_PROJECT; this.context = UserGrantContext.OWNED_PROJECT;
} else if (this.projectId && this.grantId) { } else if (this.projectId && this.grantId) {
this.context = UserGrantContext.GRANTED_PROJECT; this.context = UserGrantContext.GRANTED_PROJECT;
this.mgmtService.GetGrantedProjectByID(this.projectId, this.grantId).then(resp => { this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then(resp => {
this.grantRolesKeyList = resp.toObject().roleKeysList; if (resp.grantedProject?.grantedRoleKeysList) {
this.grantRolesKeyList = resp.grantedProject?.grantedRoleKeysList;
}
}).catch((error: any) => { }).catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.userId) { } else if (this.userId) {
this.context = UserGrantContext.USER; this.context = UserGrantContext.USER;
this.mgmtService.GetUserByID(this.userId).then(resp => { this.mgmtService.getUserByID(this.userId).then(resp => {
this.user = resp.toObject(); if (resp.user) {
this.user = resp.user;
}
}).catch((error: any) => { }).catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}); });
this.authService.GetActiveOrg().then(org => { this.authService.getActiveOrg().then(org => {
this.org = org; this.org = org;
}); });
} }
@@ -90,11 +95,11 @@ export class UserGrantCreateComponent implements OnDestroy {
public addGrant(): void { public addGrant(): void {
switch (this.context) { switch (this.context) {
case UserGrantContext.OWNED_PROJECT: case UserGrantContext.OWNED_PROJECT:
this.userService.CreateUserGrant( this.userService.addUserGrant(
this.userId, this.userId,
this.rolesList, this.rolesList,
this.projectId, this.projectId,
).then((data: UserGrant) => { ).then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTADDED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTADDED', true);
this.close(); this.close();
}).catch((error: any) => { }).catch((error: any) => {
@@ -102,12 +107,12 @@ export class UserGrantCreateComponent implements OnDestroy {
}); });
break; break;
case UserGrantContext.GRANTED_PROJECT: case UserGrantContext.GRANTED_PROJECT:
this.userService.CreateUserGrant( this.userService.addUserGrant(
this.userId, this.userId,
this.rolesList, this.rolesList,
this.projectId, this.projectId,
this.grantId, this.grantId,
).then((data: UserGrant) => { ).then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
this.close(); this.close();
}).catch((error: any) => { }).catch((error: any) => {
@@ -117,16 +122,16 @@ export class UserGrantCreateComponent implements OnDestroy {
case UserGrantContext.USER: case UserGrantContext.USER:
let grantId; let grantId;
if ((this.project as ProjectGrantView.AsObject)?.id) { if ((this.project as GrantedProject.AsObject)?.grantId) {
grantId = (this.project as ProjectGrantView.AsObject).id; grantId = (this.project as GrantedProject.AsObject).grantId;
} }
this.userService.CreateUserGrant( this.userService.addUserGrant(
this.userId, this.userId,
this.rolesList, this.rolesList,
this.project.projectId, (this.project as GrantedProject.AsObject).projectId,
grantId, grantId,
).then((data: UserGrant) => { ).then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
this.close(); this.close();
}).catch((error: any) => { }).catch((error: any) => {
@@ -136,16 +141,16 @@ export class UserGrantCreateComponent implements OnDestroy {
case UserGrantContext.NONE: case UserGrantContext.NONE:
let tempGrantId; let tempGrantId;
if ((this.project as ProjectGrantView.AsObject)?.id) { if ((this.project as GrantedProject.AsObject)?.projectId) {
tempGrantId = (this.project as ProjectGrantView.AsObject).id; tempGrantId = (this.project as GrantedProject.AsObject).projectId;
} }
this.userService.CreateUserGrant( this.userService.addUserGrant(
this.userId, this.userId,
this.rolesList, this.rolesList,
this.project.projectId, (this.project as GrantedProject.AsObject).projectId,
tempGrantId, tempGrantId,
).then((data: UserGrant) => { ).then(() => {
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true); this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
this.close(); this.close();
}).catch((error: any) => { }).catch((error: any) => {
@@ -156,17 +161,17 @@ export class UserGrantCreateComponent implements OnDestroy {
} }
public selectProject(project: ProjectView.AsObject | ProjectGrantView.AsObject | any): void { public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
this.project = project; this.project = project;
this.projectId = project.projectId; this.projectId = project.projectId;
this.grantRolesKeyList = project.roleKeysList ?? []; this.grantRolesKeyList = project.roleKeysList ?? [];
} }
public selectUser(user: UserView.AsObject): void { public selectUser(user: User.AsObject): void {
this.userId = user.id; this.userId = user.id;
} }
public selectRoles(roles: ProjectRole.AsObject[]): void { public selectRoles(roles: Role.AsObject[]): void {
this.rolesList = roles.map(role => role.key); this.rolesList = roles.map(role => role.key);
} }

View File

@@ -2,36 +2,17 @@ import { Component, OnDestroy } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { CreateMachineRequest } from 'src/app/proto/generated/admin_pb'; import { AddMachineUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
import { UserResponse } from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
function noEmailValidator(c: AbstractControl): any {
const EMAIL_REGEXP: RegExp = /^((?!@).)*$/gm;
if (!c.parent || !c) {
return;
}
const username = c.parent.get('userName');
if (!username) {
return;
}
return EMAIL_REGEXP.test(username.value) ? null : {
noEmailValidator: {
valid: false,
},
};
}
@Component({ @Component({
selector: 'app-user-create-machine', selector: 'app-user-create-machine',
templateUrl: './user-create-machine.component.html', templateUrl: './user-create-machine.component.html',
styleUrls: ['./user-create-machine.component.scss'], styleUrls: ['./user-create-machine.component.scss'],
}) })
export class UserCreateMachineComponent implements OnDestroy { export class UserCreateMachineComponent implements OnDestroy {
public user: CreateMachineRequest.AsObject = new CreateMachineRequest().toObject(); public user: AddMachineUserRequest.AsObject = new AddMachineUserRequest().toObject();
public userForm!: FormGroup; public userForm!: FormGroup;
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
@@ -64,16 +45,17 @@ export class UserCreateMachineComponent implements OnDestroy {
this.loading = true; this.loading = true;
const machineReq = new CreateMachineRequest(); const machineReq = new AddMachineUserRequest();
machineReq.setDescription(this.description?.value); machineReq.setDescription(this.description?.value);
machineReq.setName(this.name?.value); machineReq.setName(this.name?.value);
machineReq.setUserName(this.userName?.value);
this.userService this.userService
.CreateUserMachine(this.userName?.value, machineReq) .addMachineUser(machineReq)
.then((data: UserResponse) => { .then((data) => {
this.loading = false; this.loading = false;
this.toast.showInfo('USER.TOAST.CREATED', true); this.toast.showInfo('USER.TOAST.CREATED', true);
const id = data.getId(); const id = data.userId;
if (id) { if (id) {
this.router.navigate(['users', id]); this.router.navigate(['users', id]);
} }

View File

@@ -2,13 +2,9 @@ import { ChangeDetectorRef, Component, OnDestroy, ViewChild } from '@angular/cor
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { import { AddHumanUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
CreateHumanRequest, import { Domain } from 'src/app/proto/generated/zitadel/org_pb';
CreateUserRequest, import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
Gender,
OrgDomain,
UserResponse,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@@ -36,7 +32,7 @@ function noEmailValidator(c: AbstractControl): any {
styleUrls: ['./user-create.component.scss'], styleUrls: ['./user-create.component.scss'],
}) })
export class UserCreateComponent implements OnDestroy { export class UserCreateComponent implements OnDestroy {
public user: CreateUserRequest.AsObject = new CreateUserRequest().toObject(); public user: AddHumanUserRequest.AsObject = new AddHumanUserRequest().toObject();
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED]; public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
public languages: string[] = ['de', 'en']; public languages: string[] = ['de', 'en'];
public userForm!: FormGroup; public userForm!: FormGroup;
@@ -47,7 +43,7 @@ export class UserCreateComponent implements OnDestroy {
public loading: boolean = false; public loading: boolean = false;
@ViewChild('suffix') public suffix!: any; @ViewChild('suffix') public suffix!: any;
private primaryDomain!: OrgDomain.AsObject; private primaryDomain!: Domain.AsObject;
constructor( constructor(
private router: Router, private router: Router,
@@ -58,8 +54,10 @@ export class UserCreateComponent implements OnDestroy {
) { ) {
this.loading = true; this.loading = true;
this.loadOrg(); this.loadOrg();
this.mgmtService.GetMyOrgIamPolicy().then((iampolicy) => { this.mgmtService.getOrgIAMPolicy().then((resp) => {
this.userLoginMustBeDomain = iampolicy.toObject().userLoginMustBeDomain; if (resp.policy?.userLoginMustBeDomain) {
this.userLoginMustBeDomain = resp.policy.userLoginMustBeDomain;
}
this.initForm(); this.initForm();
this.loading = false; this.loading = false;
this.envSuffixLabel = this.envSuffix(); this.envSuffixLabel = this.envSuffix();
@@ -74,8 +72,8 @@ export class UserCreateComponent implements OnDestroy {
} }
private async loadOrg(): Promise<void> { private async loadOrg(): Promise<void> {
const domains = (await this.mgmtService.SearchMyOrgDomains().then(doms => doms.toObject())); const domains = (await this.mgmtService.listOrgDomains());
const found = domains.resultList.find(domain => domain.primary); const found = domains.resultList.find(resp => resp.isPrimary);
if (found) { if (found) {
this.primaryDomain = found; this.primaryDomain = found;
} }
@@ -110,22 +108,26 @@ export class UserCreateComponent implements OnDestroy {
this.loading = true; this.loading = true;
const humanReq = new CreateHumanRequest(); const profileReq = new AddHumanUserRequest.Profile();
humanReq.setFirstName(this.firstName?.value); profileReq.setFirstName(this.firstName?.value);
humanReq.setLastName(this.lastName?.value); profileReq.setLastName(this.lastName?.value);
humanReq.setNickName(this.nickName?.value); profileReq.setNickName(this.nickName?.value);
humanReq.setPreferredLanguage(this.preferredLanguage?.value); profileReq.setPreferredLanguage(this.preferredLanguage?.value);
profileReq.setGender(this.gender?.value);
const humanReq = new AddHumanUserRequest();
humanReq.setUserName(this.userName?.value);
humanReq.setProfile(profileReq);
humanReq.setEmail(this.email?.value); humanReq.setEmail(this.email?.value);
humanReq.setPhone(this.phone?.value); humanReq.setPhone(this.phone?.value);
humanReq.setGender(this.gender?.value);
humanReq.setCountry(this.country?.value);
this.mgmtService this.mgmtService
.CreateUserHuman(this.userName?.value, humanReq) .addHumanUser(humanReq)
.then((data: UserResponse) => { .then((data) => {
this.loading = false; this.loading = false;
this.toast.showInfo('USER.TOAST.CREATED', true); this.toast.showInfo('USER.TOAST.CREATED', true);
this.router.navigate(['users', data.getId()]); this.router.navigate(['users', data.userId]);
}) })
.catch(error => { .catch(error => {
this.loading = false; this.loading = false;
@@ -161,25 +163,10 @@ export class UserCreateComponent implements OnDestroy {
public get phone(): AbstractControl | null { public get phone(): AbstractControl | null {
return this.userForm.get('phone'); return this.userForm.get('phone');
} }
public get streetAddress(): AbstractControl | null {
return this.userForm.get('streetAddress');
}
public get postalCode(): AbstractControl | null {
return this.userForm.get('postalCode');
}
public get locality(): AbstractControl | null {
return this.userForm.get('locality');
}
public get region(): AbstractControl | null {
return this.userForm.get('region');
}
public get country(): AbstractControl | null {
return this.userForm.get('country');
}
private envSuffix(): string { private envSuffix(): string {
if (this.userLoginMustBeDomain && this.primaryDomain?.domain) { if (this.userLoginMustBeDomain && this.primaryDomain?.domainName) {
return `@${this.primaryDomain.domain}`; return `@${this.primaryDomain.domainName}`;
} else { } else {
return ''; return '';
} }

View File

@@ -15,7 +15,7 @@
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} </th>
<td mat-cell *matCellDef="let mfa"><span class="centered"> <td mat-cell *matCellDef="let mfa"><span class="centered">
{{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}} {{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}}
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY" <i matTooltip="verified" *ngIf="mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY"
class="verified las la-check-circle"></i> class="verified las la-check-circle"></i>
</span> </span>
</td> </td>

Some files were not shown because too many files have changed in this diff Show More