mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:17:35 +00:00
feat(api): add user creation to user service (#5745)
* 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
* begin user creation
* change protos
* implement metadata and move context
* merge commands
* 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
* update email requests
* rename proto requests
* cleanup and docs
* simplify
* simplify
* fix setup
* remove unused proto messages / fields
---------
Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
@@ -6,11 +6,11 @@ option go_package = "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha;object";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
|
||||
message OrgContext {
|
||||
oneof ctx {
|
||||
message Organisation {
|
||||
oneof org {
|
||||
string org_id = 1;
|
||||
string org_domain = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,23 @@ import "google/api/field_behavior.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
|
||||
message SetHumanEmail {
|
||||
string email = 1 [
|
||||
(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 = 2;
|
||||
ReturnEmailVerificationCode return_code = 3;
|
||||
bool is_verified = 4 [(validate.rules).bool.const = true];
|
||||
}
|
||||
}
|
||||
|
||||
message SendEmailVerificationCode {
|
||||
optional string url_template = 1 [
|
||||
|
53
proto/zitadel/user/v2alpha/password.proto
Normal file
53
proto/zitadel/user/v2alpha/password.proto
Normal file
@@ -0,0 +1,53 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.v2alpha;
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user";
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
|
||||
message SetUserPassword {
|
||||
oneof type {
|
||||
Password password = 1;
|
||||
HashedPassword hashed_password = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Password {
|
||||
string password = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"Secr3tP4ssw0rd!\"";
|
||||
min_length: 1,
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
bool change_required = 2;
|
||||
}
|
||||
|
||||
message HashedPassword {
|
||||
string hash = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"$2a$12$lJ08fqVr8bFJilRVnDT9QeULI7YW.nT3iwUv6dyg0aCrfm3UY8XR2\"";
|
||||
description: "\"hashed password\"";
|
||||
min_length: 1,
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
string algorithm = 2 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200, const: "bcrypt"},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"bcrypt\"";
|
||||
description: "\"algorithm used for the hash. currently only bcrypt is supported\"";
|
||||
min_length: 1,
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
bool change_required = 3;
|
||||
}
|
@@ -4,6 +4,87 @@ package zitadel.user.v2alpha;
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user";
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
|
||||
message User {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
enum Gender {
|
||||
GENDER_UNSPECIFIED = 0;
|
||||
GENDER_FEMALE = 1;
|
||||
GENDER_MALE = 2;
|
||||
GENDER_DIVERSE = 3;
|
||||
}
|
||||
|
||||
message SetHumanProfile {
|
||||
string first_name = 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: "\"Minnie\"";
|
||||
}
|
||||
];
|
||||
string last_name = 2 [
|
||||
(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: "\"Mouse\"";
|
||||
}
|
||||
];
|
||||
optional string nick_name = 3 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
max_length: 200;
|
||||
example: "\"Mini\"";
|
||||
}
|
||||
];
|
||||
optional string display_name = 4 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
max_length: 200;
|
||||
example: "\"Minnie Mouse\"";
|
||||
}
|
||||
];
|
||||
optional string preferred_language = 5 [
|
||||
(validate.rules).string = {max_len: 10},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
max_length: 10;
|
||||
example: "\"en\"";
|
||||
}
|
||||
];
|
||||
optional zitadel.user.v2alpha.Gender gender = 6 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"GENDER_FEMALE\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
message SetMetadataEntry {
|
||||
string key = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"my-key\"";
|
||||
min_length: 1,
|
||||
max_length: 200;
|
||||
}
|
||||
];
|
||||
bytes value = 2 [
|
||||
(validate.rules).bytes = {min_len: 1, max_len: 500000},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "The value has to be base64 encoded.";
|
||||
example: "\"VGhpcyBpcyBteSB0ZXN0IHZhbHVl\"";
|
||||
min_length: 1,
|
||||
max_length: 500000;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ package zitadel.user.v2alpha;
|
||||
import "zitadel/options.proto";
|
||||
import "zitadel/object/v2alpha/object.proto";
|
||||
import "zitadel/user/v2alpha/email.proto";
|
||||
import "zitadel/user/v2alpha/password.proto";
|
||||
import "zitadel/user/v2alpha/user.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "google/api/field_behavior.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
@@ -12,8 +14,91 @@ import "validate/validate.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha;user";
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "User Service";
|
||||
version: "2.0-alpha";
|
||||
description: "This API is intended to manage users in a ZITADEL instance. This project is in alpha state. It can AND will continue breaking until the services provide the same functionality as the current login.";
|
||||
contact:{
|
||||
name: "ZITADEL"
|
||||
url: "https://zitadel.com"
|
||||
email: "hi@zitadel.com"
|
||||
}
|
||||
license: {
|
||||
name: "Apache 2.0",
|
||||
url: "https://github.com/zitadel/zitadel/blob/main/LICENSE";
|
||||
};
|
||||
};
|
||||
schemes: HTTPS;
|
||||
schemes: HTTP;
|
||||
|
||||
consumes: "application/json";
|
||||
consumes: "application/grpc";
|
||||
|
||||
produces: "application/json";
|
||||
produces: "application/grpc";
|
||||
|
||||
consumes: "application/grpc-web+proto";
|
||||
produces: "application/grpc-web+proto";
|
||||
|
||||
host: "$ZITADEL_DOMAIN";
|
||||
base_path: "/";
|
||||
|
||||
external_docs: {
|
||||
description: "Detailed information about ZITADEL",
|
||||
url: "https://zitadel.com/docs"
|
||||
}
|
||||
|
||||
responses: {
|
||||
key: "403";
|
||||
value: {
|
||||
description: "Returned when the user does not have permission to access the resource.";
|
||||
schema: {
|
||||
json_schema: {
|
||||
ref: "#/definitions/rpcStatus";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
key: "404";
|
||||
value: {
|
||||
description: "Returned when the resource does not exist.";
|
||||
schema: {
|
||||
json_schema: {
|
||||
ref: "#/definitions/rpcStatus";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
service UserService {
|
||||
|
||||
// Create a new human user
|
||||
rpc AddHumanUser (AddHumanUserRequest) returns (AddHumanUserResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v2alpha/users/human"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "user.write"
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
summary: "Create a user (Human)";
|
||||
description: "Create/import a new user with the type human. The newly created user will get a verification email if either the email address is not marked as verified and you did not request the verification to be returned."
|
||||
responses: {
|
||||
key: "200"
|
||||
value: {
|
||||
description: "OK";
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Change the email of a user
|
||||
rpc SetEmail (SetEmailRequest) returns (SetEmailResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v2alpha/users/{user_id}/email"
|
||||
@@ -23,8 +108,20 @@ service UserService {
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "authenticated"
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
summary: "Change the user email";
|
||||
description: "Change the email address of a user. If the state is set to not verified, a verification code will be generated, which can be either returned or sent to the user by email."
|
||||
responses: {
|
||||
key: "200"
|
||||
value: {
|
||||
description: "OK";
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Verify the email with the provided code
|
||||
rpc VerifyEmail (VerifyEmailRequest) returns (VerifyEmailResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v2alpha/users/{user_id}/email/_verify"
|
||||
@@ -34,9 +131,61 @@ service UserService {
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "authenticated"
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
summary: "Verify the email";
|
||||
description: "Verify the email with the generated code."
|
||||
responses: {
|
||||
key: "200"
|
||||
value: {
|
||||
description: "OK";
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message AddHumanUserRequest{
|
||||
// optionally set your own id unique for the user
|
||||
optional string user_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
example: "\"d654e6ba-70a3-48ef-a95d-37c8d8a7901a\"";
|
||||
}
|
||||
];
|
||||
// optionally set a unique username, if none is provided the email will be used
|
||||
optional string username = 2 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
example: "\"minnie-mouse\"";
|
||||
}
|
||||
];
|
||||
zitadel.object.v2alpha.Organisation organisation = 3;
|
||||
SetHumanProfile profile = 4 [
|
||||
(validate.rules).message.required = true,
|
||||
(google.api.field_behavior) = REQUIRED
|
||||
];
|
||||
SetHumanEmail email = 5 [
|
||||
(validate.rules).message.required = true,
|
||||
(google.api.field_behavior) = REQUIRED
|
||||
];
|
||||
repeated SetMetadataEntry metadata = 6;
|
||||
oneof password_type {
|
||||
Password password = 7;
|
||||
HashedPassword hashed_password = 8;
|
||||
}
|
||||
}
|
||||
|
||||
message AddHumanUserResponse {
|
||||
string user_id = 1;
|
||||
zitadel.object.v2alpha.Details details = 2;
|
||||
optional string email_code = 3;
|
||||
}
|
||||
|
||||
message SetEmailRequest{
|
||||
string user_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
|
Reference in New Issue
Block a user