From 44f5a3d182e82c513b78685a2b42d525cf2db583 Mon Sep 17 00:00:00 2001 From: Arnaud Delcasse Date: Tue, 13 Jan 2026 15:33:38 +0100 Subject: [PATCH] dete accounts --- grpcapi/comasvc.pb.go | 142 +++++++++++++++++++++++++++------- grpcapi/comasvc.proto | 9 ++- grpcapi/comasvc_grpc.pb.go | 38 +++++++++ grpcapi/grpcapi.go | 7 ++ handlers/accounts.go | 4 + storage/accounts.go | 1 + storage/mongodb.go | 14 ++++ storage/postgresql.go | 28 +++++++ storage/postgresql/schema.hcl | 5 ++ storage/storage.go | 1 + 10 files changed, 221 insertions(+), 28 deletions(-) diff --git a/grpcapi/comasvc.pb.go b/grpcapi/comasvc.pb.go index 99abee6..f80d06a 100755 --- a/grpcapi/comasvc.pb.go +++ b/grpcapi/comasvc.pb.go @@ -863,6 +863,86 @@ func (*ChangePasswordResponse) Descriptor() ([]byte, []int) { return file_comasvc_proto_rawDescGZIP(), []int{17} } +type DeleteAccountRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,22,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteAccountRequest) Reset() { + *x = DeleteAccountRequest{} + mi := &file_comasvc_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAccountRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAccountRequest) ProtoMessage() {} + +func (x *DeleteAccountRequest) ProtoReflect() protoreflect.Message { + mi := &file_comasvc_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteAccountRequest.ProtoReflect.Descriptor instead. +func (*DeleteAccountRequest) Descriptor() ([]byte, []int) { + return file_comasvc_proto_rawDescGZIP(), []int{18} +} + +func (x *DeleteAccountRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteAccountResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteAccountResponse) Reset() { + *x = DeleteAccountResponse{} + mi := &file_comasvc_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteAccountResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAccountResponse) ProtoMessage() {} + +func (x *DeleteAccountResponse) ProtoReflect() protoreflect.Message { + mi := &file_comasvc_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteAccountResponse.ProtoReflect.Descriptor instead. +func (*DeleteAccountResponse) Descriptor() ([]byte, []int) { + return file_comasvc_proto_rawDescGZIP(), []int{19} +} + var File_comasvc_proto protoreflect.FileDescriptor const file_comasvc_proto_rawDesc = "" + @@ -914,7 +994,10 @@ const file_comasvc_proto_rawDesc = "" + "\x15ChangePasswordRequest\x12\x0e\n" + "\x02id\x18\x0f \x01(\tR\x02id\x12\x1a\n" + "\bpassword\x18\x10 \x01(\tR\bpassword\"\x18\n" + - "\x16ChangePasswordResponse2\xcc\x04\n" + + "\x16ChangePasswordResponse\"&\n" + + "\x14DeleteAccountRequest\x12\x0e\n" + + "\x02id\x18\x16 \x01(\tR\x02id\"\x17\n" + + "\x15DeleteAccountResponse2\x8e\x05\n" + "\x10MobilityAccounts\x121\n" + "\bRegister\x12\x10.RegisterRequest\x1a\x11.RegisterResponse\"\x00\x127\n" + "\n" + @@ -924,7 +1007,8 @@ const file_comasvc_proto_rawDesc = "" + "GetAccount\x12\x12.GetAccountRequest\x1a\x13.GetAccountResponse\"\x00\x12O\n" + "\x12GetAccountUsername\x12\x1a.GetAccountUsernameRequest\x1a\x1b.GetAccountUsernameResponse\"\x00\x12:\n" + "\vGetAccounts\x12\x13.GetAccountsRequest\x1a\x14.GetAccountsResponse\"\x00\x12I\n" + - "\x10GetAccountsBatch\x12\x18.GetAccountsBatchRequest\x1a\x19.GetAccountsBatchResponse\"\x00\x12(\n" + + "\x10GetAccountsBatch\x12\x18.GetAccountsBatchRequest\x1a\x19.GetAccountsBatchResponse\"\x00\x12@\n" + + "\rDeleteAccount\x12\x15.DeleteAccountRequest\x1a\x16.DeleteAccountResponse\"\x00\x12(\n" + "\x05Login\x12\r.LoginRequest\x1a\x0e.LoginResponse\"\x00\x12C\n" + "\x0eChangePassword\x12\x16.ChangePasswordRequest\x1a\x17.ChangePasswordResponse\"\x00B9Z7git.coopgo.io/coopgo-platform/mobility-accounts/grpcapib\x06proto3" @@ -940,7 +1024,7 @@ func file_comasvc_proto_rawDescGZIP() []byte { return file_comasvc_proto_rawDescData } -var file_comasvc_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_comasvc_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_comasvc_proto_goTypes = []any{ (*LoginRequest)(nil), // 0: LoginRequest (*LoginResponse)(nil), // 1: LoginResponse @@ -960,18 +1044,20 @@ var file_comasvc_proto_goTypes = []any{ (*GetAccountsBatchResponse)(nil), // 15: GetAccountsBatchResponse (*ChangePasswordRequest)(nil), // 16: ChangePasswordRequest (*ChangePasswordResponse)(nil), // 17: ChangePasswordResponse - (*Account)(nil), // 18: Account + (*DeleteAccountRequest)(nil), // 18: DeleteAccountRequest + (*DeleteAccountResponse)(nil), // 19: DeleteAccountResponse + (*Account)(nil), // 20: Account } var file_comasvc_proto_depIdxs = []int32{ - 18, // 0: LoginResponse.account:type_name -> Account - 18, // 1: RegisterRequest.account:type_name -> Account - 18, // 2: RegisterResponse.account:type_name -> Account - 18, // 3: UpdateDataRequest.account:type_name -> Account - 18, // 4: UpdateDataResponse.account:type_name -> Account - 18, // 5: GetAccountResponse.account:type_name -> Account - 18, // 6: GetAccountUsernameResponse.account:type_name -> Account - 18, // 7: GetAccountsResponse.accounts:type_name -> Account - 18, // 8: GetAccountsBatchResponse.accounts:type_name -> Account + 20, // 0: LoginResponse.account:type_name -> Account + 20, // 1: RegisterRequest.account:type_name -> Account + 20, // 2: RegisterResponse.account:type_name -> Account + 20, // 3: UpdateDataRequest.account:type_name -> Account + 20, // 4: UpdateDataResponse.account:type_name -> Account + 20, // 5: GetAccountResponse.account:type_name -> Account + 20, // 6: GetAccountUsernameResponse.account:type_name -> Account + 20, // 7: GetAccountsResponse.accounts:type_name -> Account + 20, // 8: GetAccountsBatchResponse.accounts:type_name -> Account 2, // 9: MobilityAccounts.Register:input_type -> RegisterRequest 4, // 10: MobilityAccounts.UpdateData:input_type -> UpdateDataRequest 6, // 11: MobilityAccounts.UpdatePhoneNumber:input_type -> UpdatePhoneNumberRequest @@ -979,19 +1065,21 @@ var file_comasvc_proto_depIdxs = []int32{ 10, // 13: MobilityAccounts.GetAccountUsername:input_type -> GetAccountUsernameRequest 12, // 14: MobilityAccounts.GetAccounts:input_type -> GetAccountsRequest 14, // 15: MobilityAccounts.GetAccountsBatch:input_type -> GetAccountsBatchRequest - 0, // 16: MobilityAccounts.Login:input_type -> LoginRequest - 16, // 17: MobilityAccounts.ChangePassword:input_type -> ChangePasswordRequest - 3, // 18: MobilityAccounts.Register:output_type -> RegisterResponse - 5, // 19: MobilityAccounts.UpdateData:output_type -> UpdateDataResponse - 7, // 20: MobilityAccounts.UpdatePhoneNumber:output_type -> UpdatePhoneNumberResponse - 9, // 21: MobilityAccounts.GetAccount:output_type -> GetAccountResponse - 11, // 22: MobilityAccounts.GetAccountUsername:output_type -> GetAccountUsernameResponse - 13, // 23: MobilityAccounts.GetAccounts:output_type -> GetAccountsResponse - 15, // 24: MobilityAccounts.GetAccountsBatch:output_type -> GetAccountsBatchResponse - 1, // 25: MobilityAccounts.Login:output_type -> LoginResponse - 17, // 26: MobilityAccounts.ChangePassword:output_type -> ChangePasswordResponse - 18, // [18:27] is the sub-list for method output_type - 9, // [9:18] is the sub-list for method input_type + 18, // 16: MobilityAccounts.DeleteAccount:input_type -> DeleteAccountRequest + 0, // 17: MobilityAccounts.Login:input_type -> LoginRequest + 16, // 18: MobilityAccounts.ChangePassword:input_type -> ChangePasswordRequest + 3, // 19: MobilityAccounts.Register:output_type -> RegisterResponse + 5, // 20: MobilityAccounts.UpdateData:output_type -> UpdateDataResponse + 7, // 21: MobilityAccounts.UpdatePhoneNumber:output_type -> UpdatePhoneNumberResponse + 9, // 22: MobilityAccounts.GetAccount:output_type -> GetAccountResponse + 11, // 23: MobilityAccounts.GetAccountUsername:output_type -> GetAccountUsernameResponse + 13, // 24: MobilityAccounts.GetAccounts:output_type -> GetAccountsResponse + 15, // 25: MobilityAccounts.GetAccountsBatch:output_type -> GetAccountsBatchResponse + 19, // 26: MobilityAccounts.DeleteAccount:output_type -> DeleteAccountResponse + 1, // 27: MobilityAccounts.Login:output_type -> LoginResponse + 17, // 28: MobilityAccounts.ChangePassword:output_type -> ChangePasswordResponse + 19, // [19:29] is the sub-list for method output_type + 9, // [9:19] is the sub-list for method input_type 9, // [9:9] is the sub-list for extension type_name 9, // [9:9] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name @@ -1009,7 +1097,7 @@ func file_comasvc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_comasvc_proto_rawDesc), len(file_comasvc_proto_rawDesc)), NumEnums: 0, - NumMessages: 18, + NumMessages: 20, NumExtensions: 0, NumServices: 1, }, diff --git a/grpcapi/comasvc.proto b/grpcapi/comasvc.proto index 2f56799..1d96799 100755 --- a/grpcapi/comasvc.proto +++ b/grpcapi/comasvc.proto @@ -14,6 +14,7 @@ service MobilityAccounts { rpc GetAccountUsername(GetAccountUsernameRequest) returns (GetAccountUsernameResponse) {} rpc GetAccounts(GetAccountsRequest) returns (GetAccountsResponse) {} rpc GetAccountsBatch(GetAccountsBatchRequest) returns (GetAccountsBatchResponse) {} + rpc DeleteAccount(DeleteAccountRequest) returns (DeleteAccountResponse) {} // Authentication functions rpc Login(LoginRequest) returns (LoginResponse) {} @@ -95,4 +96,10 @@ message ChangePasswordRequest { string password = 16; } -message ChangePasswordResponse {} \ No newline at end of file +message ChangePasswordResponse {} + +message DeleteAccountRequest { + string id = 22; +} + +message DeleteAccountResponse {} \ No newline at end of file diff --git a/grpcapi/comasvc_grpc.pb.go b/grpcapi/comasvc_grpc.pb.go index e53e6b2..cb8833f 100755 --- a/grpcapi/comasvc_grpc.pb.go +++ b/grpcapi/comasvc_grpc.pb.go @@ -28,6 +28,7 @@ const ( MobilityAccounts_GetAccountUsername_FullMethodName = "/MobilityAccounts/GetAccountUsername" MobilityAccounts_GetAccounts_FullMethodName = "/MobilityAccounts/GetAccounts" MobilityAccounts_GetAccountsBatch_FullMethodName = "/MobilityAccounts/GetAccountsBatch" + MobilityAccounts_DeleteAccount_FullMethodName = "/MobilityAccounts/DeleteAccount" MobilityAccounts_Login_FullMethodName = "/MobilityAccounts/Login" MobilityAccounts_ChangePassword_FullMethodName = "/MobilityAccounts/ChangePassword" ) @@ -43,6 +44,7 @@ type MobilityAccountsClient interface { GetAccountUsername(ctx context.Context, in *GetAccountUsernameRequest, opts ...grpc.CallOption) (*GetAccountUsernameResponse, error) GetAccounts(ctx context.Context, in *GetAccountsRequest, opts ...grpc.CallOption) (*GetAccountsResponse, error) GetAccountsBatch(ctx context.Context, in *GetAccountsBatchRequest, opts ...grpc.CallOption) (*GetAccountsBatchResponse, error) + DeleteAccount(ctx context.Context, in *DeleteAccountRequest, opts ...grpc.CallOption) (*DeleteAccountResponse, error) // Authentication functions Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*ChangePasswordResponse, error) @@ -126,6 +128,16 @@ func (c *mobilityAccountsClient) GetAccountsBatch(ctx context.Context, in *GetAc return out, nil } +func (c *mobilityAccountsClient) DeleteAccount(ctx context.Context, in *DeleteAccountRequest, opts ...grpc.CallOption) (*DeleteAccountResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteAccountResponse) + err := c.cc.Invoke(ctx, MobilityAccounts_DeleteAccount_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *mobilityAccountsClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LoginResponse) @@ -157,6 +169,7 @@ type MobilityAccountsServer interface { GetAccountUsername(context.Context, *GetAccountUsernameRequest) (*GetAccountUsernameResponse, error) GetAccounts(context.Context, *GetAccountsRequest) (*GetAccountsResponse, error) GetAccountsBatch(context.Context, *GetAccountsBatchRequest) (*GetAccountsBatchResponse, error) + DeleteAccount(context.Context, *DeleteAccountRequest) (*DeleteAccountResponse, error) // Authentication functions Login(context.Context, *LoginRequest) (*LoginResponse, error) ChangePassword(context.Context, *ChangePasswordRequest) (*ChangePasswordResponse, error) @@ -191,6 +204,9 @@ func (UnimplementedMobilityAccountsServer) GetAccounts(context.Context, *GetAcco func (UnimplementedMobilityAccountsServer) GetAccountsBatch(context.Context, *GetAccountsBatchRequest) (*GetAccountsBatchResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetAccountsBatch not implemented") } +func (UnimplementedMobilityAccountsServer) DeleteAccount(context.Context, *DeleteAccountRequest) (*DeleteAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteAccount not implemented") +} func (UnimplementedMobilityAccountsServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") } @@ -344,6 +360,24 @@ func _MobilityAccounts_GetAccountsBatch_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _MobilityAccounts_DeleteAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MobilityAccountsServer).DeleteAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MobilityAccounts_DeleteAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MobilityAccountsServer).DeleteAccount(ctx, req.(*DeleteAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _MobilityAccounts_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(LoginRequest) if err := dec(in); err != nil { @@ -415,6 +449,10 @@ var MobilityAccounts_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetAccountsBatch", Handler: _MobilityAccounts_GetAccountsBatch_Handler, }, + { + MethodName: "DeleteAccount", + Handler: _MobilityAccounts_DeleteAccount_Handler, + }, { MethodName: "Login", Handler: _MobilityAccounts_Login_Handler, diff --git a/grpcapi/grpcapi.go b/grpcapi/grpcapi.go index e4d0222..737c033 100755 --- a/grpcapi/grpcapi.go +++ b/grpcapi/grpcapi.go @@ -139,6 +139,13 @@ func (s MobilityAccountsServerImpl) ChangePassword(ctx context.Context, req *Cha } return &ChangePasswordResponse{}, nil } +func (s MobilityAccountsServerImpl) DeleteAccount(ctx context.Context, req *DeleteAccountRequest) (*DeleteAccountResponse, error) { + err := s.handler.DeleteAccount(req.Id) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to delete account: %v", err) + } + return &DeleteAccountResponse{}, nil +} func (MobilityAccountsServerImpl) mustEmbedUnimplementedMobilityAccountsServer() {} func Run(done chan error, cfg *viper.Viper, handler handlers.MobilityAccountsHandler) { diff --git a/handlers/accounts.go b/handlers/accounts.go index a08a8aa..95c2180 100755 --- a/handlers/accounts.go +++ b/handlers/accounts.go @@ -201,3 +201,7 @@ func (h MobilityAccountsHandler) ChangePassword(accountid string, newpassword st return nil } + +func (h MobilityAccountsHandler) DeleteAccount(id string) error { + return h.storage.DB.DeleteAccount(id) +} diff --git a/storage/accounts.go b/storage/accounts.go index 7f1017d..f986eb1 100755 --- a/storage/accounts.go +++ b/storage/accounts.go @@ -6,6 +6,7 @@ type Account struct { Authentication AccountAuth `json:"-" bson:"authentication"` Data map[string]any `json:"data"` Metadata map[string]any `json:"metadata"` + Deleted bool `json:"deleted" bson:"deleted"` } type AccountAuth struct { diff --git a/storage/mongodb.go b/storage/mongodb.go index 5762d91..454a413 100755 --- a/storage/mongodb.go +++ b/storage/mongodb.go @@ -193,6 +193,20 @@ func (s MongoDBStorage) UpdateAccount(account Account) error { return nil } +func (s MongoDBStorage) DeleteAccount(id string) error { + collection := s.Client.Database(s.DbName).Collection(s.Collections["users"]) + update := bson.M{ + "$set": bson.M{"deleted": true}, + "$unset": bson.M{"authentication": ""}, + } + if _, err := collection.UpdateOne(context.TODO(), bson.M{"_id": id}, update); err != nil { + log.Error().Err(err).Msg("") + return err + } + + return nil +} + func (s MongoDBStorage) Migrate() error { log.Error().Msg("no migration") return nil diff --git a/storage/postgresql.go b/storage/postgresql.go index 78cc210..a90de7b 100644 --- a/storage/postgresql.go +++ b/storage/postgresql.go @@ -420,6 +420,34 @@ func (psql PostgresqlStorage) UpdateAccount(account Account) error { return nil } +func (psql PostgresqlStorage) DeleteAccount(id string) error { + tx, err := psql.DbConnection.BeginTx(context.Background(), nil) + if err != nil { + return err + } + defer tx.Rollback() + + // Delete authentication info + req := fmt.Sprintf(`DELETE FROM %s WHERE account_id = $1`, psql.Tables["accounts_auth_local"]) + _, err = tx.Exec(req, id) + if err != nil { + return err + } + + // Mark account as deleted + req = fmt.Sprintf(`UPDATE %s SET deleted = true WHERE id = $1`, psql.Tables["accounts"]) + _, err = tx.Exec(req, id) + if err != nil { + return err + } + + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + func (psql PostgresqlStorage) Migrate() error { ctx := context.Background() driver, err := postgres.Open(psql.DbConnection) diff --git a/storage/postgresql/schema.hcl b/storage/postgresql/schema.hcl index 969aeb7..9a16e31 100644 --- a/storage/postgresql/schema.hcl +++ b/storage/postgresql/schema.hcl @@ -16,6 +16,11 @@ table "accounts" { null = true type = jsonb } + column "deleted" { + null = true + type = boolean + default = false + } primary_key { columns = [column.id] } diff --git a/storage/storage.go b/storage/storage.go index 7c71466..aa26497 100755 --- a/storage/storage.go +++ b/storage/storage.go @@ -37,6 +37,7 @@ type DBStorage interface { //TODO : remove UpdateAccount, implement UpdateAccountData and UpdateAccountLocalAuthentication UpdateAccount(account Account) error + DeleteAccount(id string) error Migrate() error }