feat: user v2alpha email API (#5708)

* chore(proto): update versions

* change protoc plugin

* some cleanups

* define api for setting emails in new api

* implement user.SetEmail

* move SetEmail buisiness logic into command

* resuse newCryptoCode

* command: add ChangeEmail unit tests

Not complete, was not able to mock the generator.

* Revert "resuse newCryptoCode"

This reverts commit c89e90ae35.

* undo change to crypto code generators

* command: use a generator so we can test properly

* command: reorganise ChangeEmail

improve test coverage

* implement VerifyEmail

including unit tests

* add URL template tests

* proto: change context to object

* remove old auth option

* remove old auth option

* fix linting errors

run gci on modified files

* add permission checks and fix some errors

* comments

* comments

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
Silvan
2023-04-25 09:02:29 +02:00
committed by GitHub
parent 2a79e77c7b
commit 095ec21678
58 changed files with 3589 additions and 399 deletions

View File

@@ -2801,7 +2801,7 @@ service AdminService {
rpc ResetCustomPasswordResetMessageTextToDefault(ResetCustomPasswordResetMessageTextToDefaultRequest) returns (ResetCustomPasswordResetMessageTextToDefaultResponse) {
option (google.api.http) = {
delete: "/text/message/verifyemail/{language}"
delete: "/text/message/passwordreset/{language}"
};
option (zitadel.v1.auth_option) = {

View File

@@ -5588,7 +5588,7 @@ service ManagementService {
rpc ResetCustomPasswordResetMessageTextToDefault(ResetCustomPasswordResetMessageTextToDefaultRequest) returns (ResetCustomPasswordResetMessageTextToDefaultResponse) {
option (google.api.http) = {
delete: "/text/message/verifyemail/{language}"
delete: "/text/message/passwordreset/{language}"
};
option (zitadel.v1.auth_option) = {

View File

@@ -0,0 +1,40 @@
syntax = "proto3";
package zitadel.object.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha;object";
import "google/protobuf/timestamp.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
message OrgContext {
oneof ctx {
string org_id = 1;
string org_domain = 2;
}
}
message Details {
//sequence represents the order of events. It's always counting
//
// on read: the sequence of the last event reduced by the projection
//
// on manipulation: the timestamp of the event(s) added by the manipulation
uint64 sequence = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2\"";
}
];
//change_date is the timestamp when the object was changed
//
// on read: the timestamp of the last event reduced by the projection
//
// on manipulation: the timestamp of the event(s) added by the manipulation
google.protobuf.Timestamp change_date = 2;
//resource_owner is the organization or instance_id an object belongs to
string resource_owner = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
}
];
}

View File

@@ -0,0 +1,27 @@
syntax = "proto3";
package zitadel.user.v2alpha;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
message SendEmailVerificationCode {
optional string url_template = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1;
max_length: 200;
example: "\"https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}\"";
description: "\"Optionally set a url_template, which will be used in the verification mail sent by ZITADEL to guide the user to your verification page. If no template is set, the default ZITADEL url will be used.\""
}
];
}
message ReturnEmailVerificationCode {}

View File

@@ -3,39 +3,32 @@ syntax = "proto3";
package zitadel.user.v2alpha;
import "zitadel/options.proto";
import "zitadel/user/v2alpha/user.proto";
import "zitadel/object/v2alpha/object.proto";
import "zitadel/user/v2alpha/email.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user";
service UserService {
// TestGet simply demonstrates how the context (org, instance) could be handled in a GET request //
//
// this request is subject to change and currently used for demonstration only
rpc TestGet (TestGetRequest) returns (TestGetResponse) {
rpc SetEmail (SetEmailRequest) returns (SetEmailResponse) {
option (google.api.http) = {
get: "/v2alpha/users/test"
};
}
// TestPOST simply demonstrates how the context (org, instance) could be handled in a POST request
//
// this request is subject to change and currently used for demonstration only
rpc TestPost (TestPostRequest) returns (TestPostResponse) {
option (google.api.http) = {
post: "/v2alpha/users/test"
post: "/v2alpha/users/{user_id}/email"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "authenticated"
};
}
// TestAuth demonstrates how the context (org, instance) could be handled in combination of the authorized context
//
// this request is subject to change and currently used for demonstration only
rpc TestAuth (TestAuthRequest) returns (TestAuthResponse) {
rpc VerifyEmail (VerifyEmailRequest) returns (VerifyEmailResponse) {
option (google.api.http) = {
get: "/v2alpha/users/test_auth"
post: "/v2alpha/users/{user_id}/email/_verify"
body: "*"
};
option (zitadel.v1.auth_option) = {
@@ -44,35 +37,61 @@ service UserService {
}
}
message TestGetRequest{
Context ctx = 1;
}
message TestGetResponse{
string ctx = 1;
}
message TestPostRequest{
Context ctx = 1;
}
message TestPostResponse{
string ctx = 1;
}
message TestAuthRequest{
Context ctx = 1;
}
message TestAuthResponse{
User user = 1;
Context ctx = 2;
}
message Context {
oneof ctx {
bool instance = 1;
string org_id = 2;
string org_domain = 3;
message SetEmailRequest{
string user_id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1;
max_length: 200;
example: "\"69629026806489455\"";
}
];
string email = 2 [
(validate.rules).string = {min_len: 1, max_len: 200, email: true},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1;
max_length: 200;
example: "\"mini@mouse.com\"";
}
];
// if no verification is specified, an email is sent with the default url
oneof verification {
SendEmailVerificationCode send_code = 3;
ReturnEmailVerificationCode return_code = 4;
bool is_verified = 5 [(validate.rules).bool.const = true];
}
}
message SetEmailResponse{
zitadel.object.v2alpha.Details details = 1;
// in case the verification was set to return_code, the code will be returned
optional string verification_code = 2;
}
message VerifyEmailRequest{
string user_id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1;
max_length: 200;
example: "\"69629026806489455\"";
}
];
string verification_code = 2 [
(validate.rules).string = {min_len: 1, max_len: 20},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1;
max_length: 20;
example: "\"SKJd342k\"";
description: "\"the verification code generated during the set email request\"";
}
];
}
message VerifyEmailResponse{
zitadel.object.v2alpha.Details details = 1;
}