From 6f91ac3d2d8b24e004cd71bac971bfe348aecefa Mon Sep 17 00:00:00 2001 From: = Date: Mon, 6 Dec 2021 06:53:49 +0100 Subject: [PATCH] Allow clients to change password --- actions/client.go | 44 ++++++++ actions/serve.go | 4 +- main.go | 6 + pb/ezshare.pb.go | 213 +++++++++++++++++++++++++----------- pb/ezshare_grpc.pb.go | 36 ++++++ protos/ezshare.proto | 8 ++ server/interceptors/auth.go | 17 ++- server/userservice.go | 26 +++++ 8 files changed, 284 insertions(+), 70 deletions(-) diff --git a/actions/client.go b/actions/client.go index 896ae2f..e8ca2ab 100644 --- a/actions/client.go +++ b/actions/client.go @@ -338,3 +338,47 @@ func ActionClientLogin(c *cli.Context) error { return nil } + +func ActionClientChangePassword(c *cli.Context) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + addr := cfg.Client.DefaultServer + if c.IsSet("addr") { + addr = c.String("addr") + } + + clientCreds, err := cfg.Client.Creds() + if err != nil { + return err + } + conn, err := grpc.DialContext(c.Context, addr, grpc.WithTransportCredentials(clientCreds)) + + if err != nil { + return err + } + defer conn.Close() + + fmt.Printf("current password: ") + oldPasswordBytes, err := term.ReadPassword(int(syscall.Stdin)) + if err != nil { + return cli.Exit(fmt.Sprintf("unable to read password: %s", err), 1) + } + fmt.Println() + oldPassword := string(oldPasswordBytes) + fmt.Printf("new password: ") + newPasswordBytes, err := term.ReadPassword(int(syscall.Stdin)) + if err != nil { + return cli.Exit(fmt.Sprintf("unable to read password: %s", err), 1) + } + fmt.Println() + newPassword := string(newPasswordBytes) + + client := pb.NewUserServiceClient(conn) + if _, err := client.ChangePassword(c.Context, &pb.ChangePasswordRequest{OldPassword: oldPassword, NewPassword: newPassword}); err != nil { + return cli.Exit(fmt.Sprintf("unable to change password: %s", err), 1) + } + return nil +} diff --git a/actions/serve.go b/actions/serve.go index 207c529..afb6f9c 100644 --- a/actions/serve.go +++ b/actions/serve.go @@ -130,7 +130,7 @@ func ActionServe(c *cli.Context) error { grpcServer := grpc.NewServer( grpc.Creds(creds), - grpc.ChainUnaryInterceptor(interceptors.NewAuthInterceptor(&store.MemoryStore{})), + grpc.ChainUnaryInterceptor(interceptors.NewAuthInterceptor(userStore)), ) pb.RegisterFileServiceServer(grpcServer, grpcFileServer) pb.RegisterUserServiceServer(grpcServer, grpcUserServer) @@ -216,7 +216,7 @@ func initializeUsers(us store.UserStore) error { if err := us.StoreUser(admin); err != nil { return err } - log.Printf("user created %s:%s", admin.Username, password) + log.Printf("user created with id %s:%s", admin.Username, password) return nil } diff --git a/main.go b/main.go index 9b074fa..766d337 100644 --- a/main.go +++ b/main.go @@ -104,6 +104,12 @@ func main() { }, }, { + Name: "change-password", + Usage: "Change password", + Action: actions.ActionClientChangePassword, + }, + { + // TODO: Remove Name: "config-init", Usage: "Initialize default config", Action: actions.ActionInitConfig, diff --git a/pb/ezshare.pb.go b/pb/ezshare.pb.go index 28726be..a5a2bc9 100644 --- a/pb/ezshare.pb.go +++ b/pb/ezshare.pb.go @@ -1008,6 +1008,62 @@ func (x *ApproveUserRequest) GetUserId() string { return "" } +// Change password +type ChangePasswordRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldPassword string `protobuf:"bytes,1,opt,name=old_password,json=oldPassword,proto3" json:"old_password,omitempty"` + NewPassword string `protobuf:"bytes,2,opt,name=new_password,json=newPassword,proto3" json:"new_password,omitempty"` +} + +func (x *ChangePasswordRequest) Reset() { + *x = ChangePasswordRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangePasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangePasswordRequest) ProtoMessage() {} + +func (x *ChangePasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangePasswordRequest.ProtoReflect.Descriptor instead. +func (*ChangePasswordRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{18} +} + +func (x *ChangePasswordRequest) GetOldPassword() string { + if x != nil { + return x.OldPassword + } + return "" +} + +func (x *ChangePasswordRequest) GetNewPassword() string { + if x != nil { + return x.NewPassword + } + return "" +} + type File_Metadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1021,7 +1077,7 @@ type File_Metadata struct { func (x *File_Metadata) Reset() { *x = File_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[18] + mi := &file_protos_ezshare_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1034,7 +1090,7 @@ func (x *File_Metadata) String() string { func (*File_Metadata) ProtoMessage() {} func (x *File_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[18] + mi := &file_protos_ezshare_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1083,7 +1139,7 @@ type ListFilesResponse_ListFileInfo struct { func (x *ListFilesResponse_ListFileInfo) Reset() { *x = ListFilesResponse_ListFileInfo{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[19] + mi := &file_protos_ezshare_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1096,7 +1152,7 @@ func (x *ListFilesResponse_ListFileInfo) String() string { func (*ListFilesResponse_ListFileInfo) ProtoMessage() {} func (x *ListFilesResponse_ListFileInfo) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[19] + mi := &file_protos_ezshare_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1227,46 +1283,56 @@ var file_protos_ezshare_proto_rawDesc = []byte{ 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x2d, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x32, 0xa5, 0x02, 0x0a, 0x0b, - 0x46, 0x69, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, - 0x61, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, - 0x17, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, - 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, - 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, - 0x09, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, - 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x32, 0x95, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, - 0x1c, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, - 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, - 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, - 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x3f, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, - 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x38, 0x0a, 0x07, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x12, 0x1b, 0x2e, 0x65, - 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x65, 0x7a, 0x73, 0x68, - 0x61, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x23, 0x5a, 0x21, 0x67, - 0x69, 0x74, 0x65, 0x61, 0x2e, 0x62, 0x65, 0x6e, 0x6e, 0x79, 0x2e, 0x64, 0x6f, 0x67, 0x2f, 0x74, - 0x6f, 0x72, 0x6a, 0x75, 0x73, 0x2f, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x5d, 0x0a, 0x15, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x6c, 0x64, 0x50, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, + 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x32, 0xa5, 0x02, 0x0a, 0x0b, 0x46, + 0x69, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x17, + 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x12, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, + 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, 0x68, + 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x32, 0xd9, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x49, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x1c, + 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x65, + 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, + 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x3f, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x38, 0x0a, 0x07, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x12, 0x1b, 0x2e, 0x65, 0x7a, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0e, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x65, + 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x65, + 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x23, + 0x5a, 0x21, 0x67, 0x69, 0x74, 0x65, 0x61, 0x2e, 0x62, 0x65, 0x6e, 0x6e, 0x79, 0x2e, 0x64, 0x6f, + 0x67, 0x2f, 0x74, 0x6f, 0x72, 0x6a, 0x75, 0x73, 0x2f, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1282,7 +1348,7 @@ func file_protos_ezshare_proto_rawDescGZIP() []byte { } var file_protos_ezshare_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_protos_ezshare_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_protos_ezshare_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_protos_ezshare_proto_goTypes = []interface{}{ (User_Role)(0), // 0: ezshare.User.Role (*Empty)(nil), // 1: ezshare.Empty @@ -1303,20 +1369,21 @@ var file_protos_ezshare_proto_goTypes = []interface{}{ (*ListUsersRequest)(nil), // 16: ezshare.ListUsersRequest (*ListUsersResponse)(nil), // 17: ezshare.ListUsersResponse (*ApproveUserRequest)(nil), // 18: ezshare.ApproveUserRequest - (*File_Metadata)(nil), // 19: ezshare.File.Metadata - (*ListFilesResponse_ListFileInfo)(nil), // 20: ezshare.ListFilesResponse.ListFileInfo - (*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp + (*ChangePasswordRequest)(nil), // 19: ezshare.ChangePasswordRequest + (*File_Metadata)(nil), // 20: ezshare.File.Metadata + (*ListFilesResponse_ListFileInfo)(nil), // 21: ezshare.ListFilesResponse.ListFileInfo + (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp } var file_protos_ezshare_proto_depIdxs = []int32{ - 19, // 0: ezshare.File.metadata:type_name -> ezshare.File.Metadata - 21, // 1: ezshare.UploadFileRequest.expires_on:type_name -> google.protobuf.Timestamp + 20, // 0: ezshare.File.metadata:type_name -> ezshare.File.Metadata + 22, // 1: ezshare.UploadFileRequest.expires_on:type_name -> google.protobuf.Timestamp 2, // 2: ezshare.GetFileResponse.file:type_name -> ezshare.File - 20, // 3: ezshare.ListFilesResponse.files:type_name -> ezshare.ListFilesResponse.ListFileInfo + 21, // 3: ezshare.ListFilesResponse.files:type_name -> ezshare.ListFilesResponse.ListFileInfo 0, // 4: ezshare.User.user_role:type_name -> ezshare.User.Role 11, // 5: ezshare.ListUsersResponse.users:type_name -> ezshare.User - 21, // 6: ezshare.File.Metadata.uploaded_on:type_name -> google.protobuf.Timestamp - 21, // 7: ezshare.File.Metadata.expires_on:type_name -> google.protobuf.Timestamp - 19, // 8: ezshare.ListFilesResponse.ListFileInfo.metadata:type_name -> ezshare.File.Metadata + 22, // 6: ezshare.File.Metadata.uploaded_on:type_name -> google.protobuf.Timestamp + 22, // 7: ezshare.File.Metadata.expires_on:type_name -> google.protobuf.Timestamp + 20, // 8: ezshare.ListFilesResponse.ListFileInfo.metadata:type_name -> ezshare.File.Metadata 3, // 9: ezshare.FileService.UploadFile:input_type -> ezshare.UploadFileRequest 5, // 10: ezshare.FileService.GetFile:input_type -> ezshare.GetFileRequest 7, // 11: ezshare.FileService.DeleteFile:input_type -> ezshare.DeleteFileRequest @@ -1325,16 +1392,18 @@ var file_protos_ezshare_proto_depIdxs = []int32{ 14, // 14: ezshare.UserService.Login:input_type -> ezshare.LoginUserRequest 16, // 15: ezshare.UserService.List:input_type -> ezshare.ListUsersRequest 18, // 16: ezshare.UserService.Approve:input_type -> ezshare.ApproveUserRequest - 4, // 17: ezshare.FileService.UploadFile:output_type -> ezshare.UploadFileResponse - 6, // 18: ezshare.FileService.GetFile:output_type -> ezshare.GetFileResponse - 8, // 19: ezshare.FileService.DeleteFile:output_type -> ezshare.DeleteFileResponse - 10, // 20: ezshare.FileService.ListFiles:output_type -> ezshare.ListFilesResponse - 13, // 21: ezshare.UserService.Register:output_type -> ezshare.RegisterUserResponse - 15, // 22: ezshare.UserService.Login:output_type -> ezshare.LoginUserResponse - 17, // 23: ezshare.UserService.List:output_type -> ezshare.ListUsersResponse - 1, // 24: ezshare.UserService.Approve:output_type -> ezshare.Empty - 17, // [17:25] is the sub-list for method output_type - 9, // [9:17] is the sub-list for method input_type + 19, // 17: ezshare.UserService.ChangePassword:input_type -> ezshare.ChangePasswordRequest + 4, // 18: ezshare.FileService.UploadFile:output_type -> ezshare.UploadFileResponse + 6, // 19: ezshare.FileService.GetFile:output_type -> ezshare.GetFileResponse + 8, // 20: ezshare.FileService.DeleteFile:output_type -> ezshare.DeleteFileResponse + 10, // 21: ezshare.FileService.ListFiles:output_type -> ezshare.ListFilesResponse + 13, // 22: ezshare.UserService.Register:output_type -> ezshare.RegisterUserResponse + 15, // 23: ezshare.UserService.Login:output_type -> ezshare.LoginUserResponse + 17, // 24: ezshare.UserService.List:output_type -> ezshare.ListUsersResponse + 1, // 25: ezshare.UserService.Approve:output_type -> ezshare.Empty + 1, // 26: ezshare.UserService.ChangePassword:output_type -> ezshare.Empty + 18, // [18:27] is the sub-list for method output_type + 9, // [9:18] 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 @@ -1563,7 +1632,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*File_Metadata); i { + switch v := v.(*ChangePasswordRequest); i { case 0: return &v.state case 1: @@ -1575,6 +1644,18 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*File_Metadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListFilesResponse_ListFileInfo); i { case 0: return &v.state @@ -1593,7 +1674,7 @@ func file_protos_ezshare_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_protos_ezshare_proto_rawDesc, NumEnums: 1, - NumMessages: 20, + NumMessages: 21, NumExtensions: 0, NumServices: 2, }, diff --git a/pb/ezshare_grpc.pb.go b/pb/ezshare_grpc.pb.go index 4133765..304fb02 100644 --- a/pb/ezshare_grpc.pb.go +++ b/pb/ezshare_grpc.pb.go @@ -216,6 +216,7 @@ type UserServiceClient interface { Login(ctx context.Context, in *LoginUserRequest, opts ...grpc.CallOption) (*LoginUserResponse, error) List(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) Approve(ctx context.Context, in *ApproveUserRequest, opts ...grpc.CallOption) (*Empty, error) + ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*Empty, error) } type userServiceClient struct { @@ -262,6 +263,15 @@ func (c *userServiceClient) Approve(ctx context.Context, in *ApproveUserRequest, return out, nil } +func (c *userServiceClient) ChangePassword(ctx context.Context, in *ChangePasswordRequest, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, "/ezshare.UserService/ChangePassword", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // UserServiceServer is the server API for UserService service. // All implementations must embed UnimplementedUserServiceServer // for forward compatibility @@ -270,6 +280,7 @@ type UserServiceServer interface { Login(context.Context, *LoginUserRequest) (*LoginUserResponse, error) List(context.Context, *ListUsersRequest) (*ListUsersResponse, error) Approve(context.Context, *ApproveUserRequest) (*Empty, error) + ChangePassword(context.Context, *ChangePasswordRequest) (*Empty, error) mustEmbedUnimplementedUserServiceServer() } @@ -289,6 +300,9 @@ func (UnimplementedUserServiceServer) List(context.Context, *ListUsersRequest) ( func (UnimplementedUserServiceServer) Approve(context.Context, *ApproveUserRequest) (*Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Approve not implemented") } +func (UnimplementedUserServiceServer) ChangePassword(context.Context, *ChangePasswordRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangePassword not implemented") +} func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} // UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service. @@ -374,6 +388,24 @@ func _UserService_Approve_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _UserService_ChangePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChangePasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).ChangePassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ezshare.UserService/ChangePassword", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).ChangePassword(ctx, req.(*ChangePasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + // UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -397,6 +429,10 @@ var UserService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Approve", Handler: _UserService_Approve_Handler, }, + { + MethodName: "ChangePassword", + Handler: _UserService_ChangePassword_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "protos/ezshare.proto", diff --git a/protos/ezshare.proto b/protos/ezshare.proto index 53d52d5..427dd70 100644 --- a/protos/ezshare.proto +++ b/protos/ezshare.proto @@ -121,9 +121,17 @@ message ApproveUserRequest { string user_id = 1; } + +// Change password +message ChangePasswordRequest { + string old_password = 1; + string new_password = 2; +} + service UserService { rpc Register(RegisterUserRequest) returns (RegisterUserResponse) {} rpc Login(LoginUserRequest) returns (LoginUserResponse) {} rpc List(ListUsersRequest) returns (ListUsersResponse) {} rpc Approve(ApproveUserRequest) returns (Empty) {} + rpc ChangePassword(ChangePasswordRequest) returns (Empty) {} } diff --git a/server/interceptors/auth.go b/server/interceptors/auth.go index 848f1bd..d8f3fab 100644 --- a/server/interceptors/auth.go +++ b/server/interceptors/auth.go @@ -2,7 +2,6 @@ package interceptors import ( "context" - "fmt" "gitea.benny.dog/torjus/ezshare/pb" "gitea.benny.dog/torjus/ezshare/store" @@ -14,14 +13,15 @@ import ( type ContextKey string var ContextKeyRole ContextKey = "role" +var ContextKeyUserID ContextKey = "userid" func NewAuthInterceptor(s store.UserStore) grpc.UnaryServerInterceptor { + // TODO: Verify that cert is signed by our ca return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { p, ok := peer.FromContext(ctx) if ok { tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo) if ok { - fmt.Printf("%+v\n", tlsInfo.State.PeerCertificates[0].Subject.CommonName) if len(tlsInfo.State.PeerCertificates) == 1 { cert := tlsInfo.State.PeerCertificates[0] @@ -30,6 +30,7 @@ func NewAuthInterceptor(s store.UserStore) grpc.UnaryServerInterceptor { user, err := s.GetUser(id) if err == nil { newCtx := context.WithValue(ctx, ContextKeyRole, user.UserRole) + newCtx = context.WithValue(newCtx, ContextKeyUserID, user.Id) return handler(newCtx, req) } } @@ -53,3 +54,15 @@ func RoleFromContext(ctx context.Context) pb.User_Role { } return pb.User_UNKNOWN } + +func UserIDFromContext(ctx context.Context) string { + value := ctx.Value(ContextKeyUserID) + if value == nil { + return "" + } + id, ok := value.(string) + if ok { + return id + } + return "" +} diff --git a/server/userservice.go b/server/userservice.go index a75d438..c8d1f29 100644 --- a/server/userservice.go +++ b/server/userservice.go @@ -6,6 +6,7 @@ import ( "gitea.benny.dog/torjus/ezshare/certs" "gitea.benny.dog/torjus/ezshare/pb" + "gitea.benny.dog/torjus/ezshare/server/interceptors" "gitea.benny.dog/torjus/ezshare/store" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" @@ -81,6 +82,31 @@ func (s *GRPCUserServiceServer) Approve(_ context.Context, _ *pb.ApproveUserRequ return nil, status.Error(codes.Unimplemented, "not yet implemented") } +func (s *GRPCUserServiceServer) ChangePassword(ctx context.Context, req *pb.ChangePasswordRequest) (*pb.Empty, error) { + // Get ID from ctx + userID := interceptors.UserIDFromContext(ctx) + if userID == "" { + return nil, status.Error(codes.Unauthenticated, "not authenticated") + } + user, err := s.store.GetUser(userID) + if err != nil { + return nil, status.Error(codes.Unauthenticated, "user not found") + } + + if err := bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(req.OldPassword)); err != nil { + return nil, status.Error(codes.Unauthenticated, "wrong password") + } + newPasswordHash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) + if err != nil { + return nil, status.Error(codes.Internal, "unable to hash new password") + } + user.HashedPassword = newPasswordHash + if err := s.store.StoreUser(user); err != nil { + return nil, status.Error(codes.Internal, "unable to store new password") + } + return &pb.Empty{}, nil +} + func hashPassword(password string) ([]byte, error) { return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) }