mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:17:35 +00:00
feat(auth): My user changes (#318)
* fix: project by id loads project from view and from eventstore * fix: correct search key for role * feat(auth): my user changes * fix: improve error handling in change converters * fix: log-id
This commit is contained in:
@@ -80,6 +80,11 @@ var AuthService_AuthMethods = utils_auth.MethodMapping{
|
||||
CheckParam: "",
|
||||
},
|
||||
|
||||
"/caos.zitadel.auth.api.v1.AuthService/GetMyUserChanges": utils_auth.Option{
|
||||
Permission: "authenticated",
|
||||
CheckParam: "",
|
||||
},
|
||||
|
||||
"/caos.zitadel.auth.api.v1.AuthService/UpdateMyUserAddress": utils_auth.Option{
|
||||
Permission: "authenticated",
|
||||
CheckParam: "",
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -211,6 +211,45 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/users/me/changes": {
|
||||
"get": {
|
||||
"operationId": "GetMyUserChanges",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1Changes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "sequence_offset",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "asc",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "boolean",
|
||||
"format": "boolean"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"AuthService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/users/me/email": {
|
||||
"get": {
|
||||
"operationId": "GetMyUserEmail",
|
||||
@@ -552,7 +591,7 @@
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/protobufStruct"
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -563,19 +602,6 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"protobufListValue": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"values": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufValue"
|
||||
},
|
||||
"description": "Repeated field of dynamically typed values."
|
||||
}
|
||||
},
|
||||
"description": "`ListValue` is a wrapper around a repeated field of values.\n\nThe JSON representation for `ListValue` is JSON array."
|
||||
},
|
||||
"protobufNullValue": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -584,50 +610,49 @@
|
||||
"default": "NULL_VALUE",
|
||||
"description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value."
|
||||
},
|
||||
"protobufStruct": {
|
||||
"v1Change": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufValue"
|
||||
},
|
||||
"description": "Unordered map of dynamically typed values."
|
||||
}
|
||||
},
|
||||
"description": "`Struct` represents a structured data value, consisting of fields\nwhich map to dynamically typed values. In some languages, `Struct`\nmight be supported by a native representation. For example, in\nscripting languages like JS a struct is represented as an\nobject. The details of that representation are described together\nwith the proto support for the language.\n\nThe JSON representation for `Struct` is JSON object."
|
||||
},
|
||||
"protobufValue": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"null_value": {
|
||||
"$ref": "#/definitions/protobufNullValue",
|
||||
"description": "Represents a null value."
|
||||
},
|
||||
"number_value": {
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Represents a double value."
|
||||
},
|
||||
"string_value": {
|
||||
"change_date": {
|
||||
"type": "string",
|
||||
"description": "Represents a string value."
|
||||
"format": "date-time"
|
||||
},
|
||||
"bool_value": {
|
||||
"type": "boolean",
|
||||
"format": "boolean",
|
||||
"description": "Represents a boolean value."
|
||||
"event_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"struct_value": {
|
||||
"$ref": "#/definitions/protobufStruct",
|
||||
"description": "Represents a structured value."
|
||||
"sequence": {
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
"list_value": {
|
||||
"$ref": "#/definitions/protobufListValue",
|
||||
"description": "Represents a repeated `Value`."
|
||||
"editor_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"editor": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"description": "`Value` represents a dynamically typed value which can be either\nnull, a number, a string, a boolean, a recursive struct value, or a\nlist of values. A producer of value is expected to set one of that\nvariants, absence of any variant indicates an error.\n\nThe JSON representation for `Value` is JSON value."
|
||||
}
|
||||
},
|
||||
"v1Changes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"changes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1Change"
|
||||
}
|
||||
},
|
||||
"offset": {
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
"limit": {
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1Gender": {
|
||||
"type": "string",
|
||||
|
@@ -128,3 +128,11 @@ func (s *Server) RemoveMfaOTP(ctx context.Context, _ *empty.Empty) (_ *empty.Emp
|
||||
s.repo.RemoveMyMfaOTP(ctx)
|
||||
return &empty.Empty{}, err
|
||||
}
|
||||
|
||||
func (s *Server) GetMyUserChanges(ctx context.Context, request *ChangesRequest) (*Changes, error) {
|
||||
changes, err := s.repo.MyUserChanges(ctx, request.SequenceOffset, request.Limit, request.Asc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return userChangesToResponse(changes, request.GetSequenceOffset(), request.GetLimit()), nil
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"golang.org/x/text/language"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
func userViewFromModel(user *usr_model.UserView) *UserView {
|
||||
@@ -335,3 +338,36 @@ func mfaTypeFromModel(mfatype usr_model.MfaType) MfaType {
|
||||
return MfaType_MFATYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func userChangesToResponse(response *usr_model.UserChanges, offset uint64, limit uint64) (_ *Changes) {
|
||||
return &Changes{
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
Changes: userChangesToMgtAPI(response),
|
||||
}
|
||||
}
|
||||
|
||||
func userChangesToMgtAPI(changes *usr_model.UserChanges) (_ []*Change) {
|
||||
result := make([]*Change, len(changes.Changes))
|
||||
|
||||
for i, change := range changes.Changes {
|
||||
var data *structpb.Struct
|
||||
changedData, err := json.Marshal(change.Data)
|
||||
if err == nil {
|
||||
data = new(structpb.Struct)
|
||||
err = protojson.Unmarshal(changedData, data)
|
||||
logging.Log("GRPC-0kRsY").OnError(err).Debug("unable to marshal changed data to struct")
|
||||
}
|
||||
|
||||
result[i] = &Change{
|
||||
ChangeDate: change.ChangeDate,
|
||||
EventType: change.EventType,
|
||||
Sequence: change.Sequence,
|
||||
Data: data,
|
||||
EditorId: change.ModifierId,
|
||||
Editor: change.ModifierName,
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
@@ -189,6 +189,16 @@ service AuthService {
|
||||
};
|
||||
}
|
||||
|
||||
rpc GetMyUserChanges(ChangesRequest) returns (Changes) {
|
||||
option (google.api.http) = {
|
||||
get: "/users/me/changes"
|
||||
};
|
||||
|
||||
option (caos.zitadel.utils.v1.auth_option) = {
|
||||
permission: "authenticated"
|
||||
};
|
||||
}
|
||||
|
||||
rpc UpdateMyUserAddress(UpdateUserAddressRequest) returns (UserAddress) {
|
||||
option (google.api.http) = {
|
||||
put: "/users/me/address"
|
||||
@@ -623,3 +633,24 @@ enum SearchMethod {
|
||||
SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4;
|
||||
SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5;
|
||||
}
|
||||
|
||||
message ChangesRequest {
|
||||
uint64 limit= 1;
|
||||
uint64 sequence_offset = 2;
|
||||
bool asc = 3;
|
||||
}
|
||||
|
||||
message Changes {
|
||||
repeated Change changes = 1;
|
||||
uint64 offset = 2;
|
||||
uint64 limit = 3;
|
||||
}
|
||||
|
||||
message Change {
|
||||
google.protobuf.Timestamp change_date = 1;
|
||||
string event_type = 2;
|
||||
uint64 sequence = 3;
|
||||
string editor_id = 4;
|
||||
string editor = 5;
|
||||
google.protobuf.Struct data = 6;
|
||||
}
|
@@ -494,11 +494,14 @@ func userChangesToMgtAPI(changes *usr_model.UserChanges) (_ []*Change) {
|
||||
result := make([]*Change, len(changes.Changes))
|
||||
|
||||
for i, change := range changes.Changes {
|
||||
b, err := json.Marshal(change.Data)
|
||||
data := &structpb.Struct{}
|
||||
err = protojson.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
var data *structpb.Struct
|
||||
changedData, err := json.Marshal(change.Data)
|
||||
if err == nil {
|
||||
data = new(structpb.Struct)
|
||||
err = protojson.Unmarshal(changedData, data)
|
||||
logging.Log("GRPC-a7F54").OnError(err).Debug("unable to marshal changed data to struct")
|
||||
}
|
||||
|
||||
result[i] = &Change{
|
||||
ChangeDate: change.ChangeDate,
|
||||
EventType: change.EventType,
|
||||
|
Reference in New Issue
Block a user