chore: update auth service

pull/4784/head
Steven 1 month ago
parent 59eae4465a
commit 5821bb7a65

@ -30,15 +30,6 @@ service AuthService {
rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {delete: "/api/v1/auth/sessions/current"};
}
// SignUp creates a new user account with username and password.
// Returns the newly created user information upon successful registration.
rpc SignUp(SignUpRequest) returns (User) {
option (google.api.http) = {
post: "/api/v1/auth/signup"
body: "*"
};
}
}
message GetCurrentSessionRequest {}
@ -88,13 +79,3 @@ message SSOCredentials {
}
message DeleteSessionRequest {}
message SignUpRequest {
// The username to sign up with.
// Required field that must be unique across the system.
string username = 1 [(google.api.field_behavior) = REQUIRED];
// The password to sign up with.
// Required field that should meet security requirements.
string password = 2 [(google.api.field_behavior) = REQUIRED];
}

@ -358,62 +358,6 @@ func (*DeleteSessionRequest) Descriptor() ([]byte, []int) {
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{5}
}
type SignUpRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The username to sign up with.
// Required field that must be unique across the system.
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
// The password to sign up with.
// Required field that should meet security requirements.
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SignUpRequest) Reset() {
*x = SignUpRequest{}
mi := &file_api_v1_auth_service_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SignUpRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SignUpRequest) ProtoMessage() {}
func (x *SignUpRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_auth_service_proto_msgTypes[6]
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 SignUpRequest.ProtoReflect.Descriptor instead.
func (*SignUpRequest) Descriptor() ([]byte, []int) {
return file_api_v1_auth_service_proto_rawDescGZIP(), []int{6}
}
func (x *SignUpRequest) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *SignUpRequest) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
var File_api_v1_auth_service_proto protoreflect.FileDescriptor
const file_api_v1_auth_service_proto_rawDesc = "" +
@ -434,15 +378,11 @@ const file_api_v1_auth_service_proto_rawDesc = "" +
"\x06idp_id\x18\x01 \x01(\x05B\x03\xe0A\x02R\x05idpId\x12\x17\n" +
"\x04code\x18\x02 \x01(\tB\x03\xe0A\x02R\x04code\x12&\n" +
"\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUri\"\x16\n" +
"\x14DeleteSessionRequest\"Q\n" +
"\rSignUpRequest\x12\x1f\n" +
"\busername\x18\x01 \x01(\tB\x03\xe0A\x02R\busername\x12\x1f\n" +
"\bpassword\x18\x02 \x01(\tB\x03\xe0A\x02R\bpassword2\xbf\x03\n" +
"\x14DeleteSessionRequest2\xe4\x02\n" +
"\vAuthService\x12v\n" +
"\x11GetCurrentSession\x12&.memos.api.v1.GetCurrentSessionRequest\x1a\x12.memos.api.v1.User\"%\x82\xd3\xe4\x93\x02\x1f\x12\x1d/api/v1/auth/sessions/current\x12i\n" +
"\rCreateSession\x12\".memos.api.v1.CreateSessionRequest\x1a\x12.memos.api.v1.User\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/v1/auth/sessions\x12r\n" +
"\rDeleteSession\x12\".memos.api.v1.DeleteSessionRequest\x1a\x16.google.protobuf.Empty\"%\x82\xd3\xe4\x93\x02\x1f*\x1d/api/v1/auth/sessions/current\x12Y\n" +
"\x06SignUp\x12\x1b.memos.api.v1.SignUpRequest\x1a\x12.memos.api.v1.User\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/api/v1/auth/signupB\xa8\x01\n" +
"\rDeleteSession\x12\".memos.api.v1.DeleteSessionRequest\x1a\x16.google.protobuf.Empty\"%\x82\xd3\xe4\x93\x02\x1f*\x1d/api/v1/auth/sessions/currentB\xa8\x01\n" +
"\x10com.memos.api.v1B\x10AuthServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3"
var (
@ -457,7 +397,7 @@ func file_api_v1_auth_service_proto_rawDescGZIP() []byte {
return file_api_v1_auth_service_proto_rawDescData
}
var file_api_v1_auth_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_api_v1_auth_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_api_v1_auth_service_proto_goTypes = []any{
(*GetCurrentSessionRequest)(nil), // 0: memos.api.v1.GetCurrentSessionRequest
(*GetCurrentSessionResponse)(nil), // 1: memos.api.v1.GetCurrentSessionResponse
@ -465,24 +405,21 @@ var file_api_v1_auth_service_proto_goTypes = []any{
(*PasswordCredentials)(nil), // 3: memos.api.v1.PasswordCredentials
(*SSOCredentials)(nil), // 4: memos.api.v1.SSOCredentials
(*DeleteSessionRequest)(nil), // 5: memos.api.v1.DeleteSessionRequest
(*SignUpRequest)(nil), // 6: memos.api.v1.SignUpRequest
(*User)(nil), // 7: memos.api.v1.User
(*emptypb.Empty)(nil), // 8: google.protobuf.Empty
(*User)(nil), // 6: memos.api.v1.User
(*emptypb.Empty)(nil), // 7: google.protobuf.Empty
}
var file_api_v1_auth_service_proto_depIdxs = []int32{
7, // 0: memos.api.v1.GetCurrentSessionResponse.user:type_name -> memos.api.v1.User
6, // 0: memos.api.v1.GetCurrentSessionResponse.user:type_name -> memos.api.v1.User
3, // 1: memos.api.v1.CreateSessionRequest.password_credentials:type_name -> memos.api.v1.PasswordCredentials
4, // 2: memos.api.v1.CreateSessionRequest.sso_credentials:type_name -> memos.api.v1.SSOCredentials
0, // 3: memos.api.v1.AuthService.GetCurrentSession:input_type -> memos.api.v1.GetCurrentSessionRequest
2, // 4: memos.api.v1.AuthService.CreateSession:input_type -> memos.api.v1.CreateSessionRequest
5, // 5: memos.api.v1.AuthService.DeleteSession:input_type -> memos.api.v1.DeleteSessionRequest
6, // 6: memos.api.v1.AuthService.SignUp:input_type -> memos.api.v1.SignUpRequest
7, // 7: memos.api.v1.AuthService.GetCurrentSession:output_type -> memos.api.v1.User
7, // 8: memos.api.v1.AuthService.CreateSession:output_type -> memos.api.v1.User
8, // 9: memos.api.v1.AuthService.DeleteSession:output_type -> google.protobuf.Empty
7, // 10: memos.api.v1.AuthService.SignUp:output_type -> memos.api.v1.User
7, // [7:11] is the sub-list for method output_type
3, // [3:7] is the sub-list for method input_type
6, // 6: memos.api.v1.AuthService.GetCurrentSession:output_type -> memos.api.v1.User
6, // 7: memos.api.v1.AuthService.CreateSession:output_type -> memos.api.v1.User
7, // 8: memos.api.v1.AuthService.DeleteSession:output_type -> google.protobuf.Empty
6, // [6:9] is the sub-list for method output_type
3, // [3:6] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
@ -504,7 +441,7 @@ func file_api_v1_auth_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_auth_service_proto_rawDesc), len(file_api_v1_auth_service_proto_rawDesc)),
NumEnums: 0,
NumMessages: 7,
NumMessages: 6,
NumExtensions: 0,
NumServices: 1,
},

@ -104,33 +104,6 @@ func local_request_AuthService_DeleteSession_0(ctx context.Context, marshaler ru
return msg, metadata, err
}
func request_AuthService_SignUp_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SignUpRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.SignUp(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_AuthService_SignUp_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq SignUpRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.SignUp(ctx, &protoReq)
return msg, metadata, err
}
// RegisterAuthServiceHandlerServer registers the http handlers for service AuthService to "mux".
// UnaryRPC :call AuthServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -197,26 +170,6 @@ func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
}
forward_AuthService_DeleteSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_AuthService_SignUp_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AuthService/SignUp", runtime.WithHTTPPathPattern("/api/v1/auth/signup"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_AuthService_SignUp_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_SignUp_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -308,23 +261,6 @@ func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
}
forward_AuthService_DeleteSession_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_AuthService_SignUp_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AuthService/SignUp", runtime.WithHTTPPathPattern("/api/v1/auth/signup"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AuthService_SignUp_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_AuthService_SignUp_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -332,12 +268,10 @@ var (
pattern_AuthService_GetCurrentSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "auth", "sessions", "current"}, ""))
pattern_AuthService_CreateSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "sessions"}, ""))
pattern_AuthService_DeleteSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"api", "v1", "auth", "sessions", "current"}, ""))
pattern_AuthService_SignUp_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "auth", "signup"}, ""))
)
var (
forward_AuthService_GetCurrentSession_0 = runtime.ForwardResponseMessage
forward_AuthService_CreateSession_0 = runtime.ForwardResponseMessage
forward_AuthService_DeleteSession_0 = runtime.ForwardResponseMessage
forward_AuthService_SignUp_0 = runtime.ForwardResponseMessage
)

@ -23,7 +23,6 @@ const (
AuthService_GetCurrentSession_FullMethodName = "/memos.api.v1.AuthService/GetCurrentSession"
AuthService_CreateSession_FullMethodName = "/memos.api.v1.AuthService/CreateSession"
AuthService_DeleteSession_FullMethodName = "/memos.api.v1.AuthService/DeleteSession"
AuthService_SignUp_FullMethodName = "/memos.api.v1.AuthService/SignUp"
)
// AuthServiceClient is the client API for AuthService service.
@ -39,9 +38,6 @@ type AuthServiceClient interface {
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(ctx context.Context, in *DeleteSessionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// SignUp creates a new user account with username and password.
// Returns the newly created user information upon successful registration.
SignUp(ctx context.Context, in *SignUpRequest, opts ...grpc.CallOption) (*User, error)
}
type authServiceClient struct {
@ -82,16 +78,6 @@ func (c *authServiceClient) DeleteSession(ctx context.Context, in *DeleteSession
return out, nil
}
func (c *authServiceClient) SignUp(ctx context.Context, in *SignUpRequest, opts ...grpc.CallOption) (*User, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(User)
err := c.cc.Invoke(ctx, AuthService_SignUp_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility.
@ -105,9 +91,6 @@ type AuthServiceServer interface {
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error)
// SignUp creates a new user account with username and password.
// Returns the newly created user information upon successful registration.
SignUp(context.Context, *SignUpRequest) (*User, error)
mustEmbedUnimplementedAuthServiceServer()
}
@ -127,9 +110,6 @@ func (UnimplementedAuthServiceServer) CreateSession(context.Context, *CreateSess
func (UnimplementedAuthServiceServer) DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteSession not implemented")
}
func (UnimplementedAuthServiceServer) SignUp(context.Context, *SignUpRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method SignUp not implemented")
}
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
func (UnimplementedAuthServiceServer) testEmbeddedByValue() {}
@ -205,24 +185,6 @@ func _AuthService_DeleteSession_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _AuthService_SignUp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SignUpRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).SignUp(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_SignUp_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).SignUp(ctx, req.(*SignUpRequest))
}
return interceptor(ctx, in, info, handler)
}
// AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -242,10 +204,6 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteSession",
Handler: _AuthService_DeleteSession_Handler,
},
{
MethodName: "SignUp",
Handler: _AuthService_SignUp_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v1/auth_service.proto",

@ -188,29 +188,6 @@ paths:
$ref: '#/definitions/googlerpcStatus'
tags:
- AuthService
/api/v1/auth/signup:
post:
summary: |-
SignUp creates a new user account with username and password.
Returns the newly created user information upon successful registration.
operationId: AuthService_SignUp
responses:
"200":
description: A successful response.
schema:
$ref: '#/definitions/v1User'
default:
description: An unexpected error response.
schema:
$ref: '#/definitions/googlerpcStatus'
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/v1SignUpRequest'
tags:
- AuthService
/api/v1/identityProviders:
get:
summary: ListIdentityProviders lists identity providers.
@ -4097,22 +4074,6 @@ definitions:
type: integer
format: int32
description: The total count of matching users.
v1SignUpRequest:
type: object
properties:
username:
type: string
description: |-
The username to sign up with.
Required field that must be unique across the system.
password:
type: string
description: |-
The password to sign up with.
Required field that should meet security requirements.
required:
- username
- password
v1SpoilerNode:
type: object
properties:

@ -7,7 +7,7 @@ var authenticationAllowlistMethods = map[string]bool{
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": true,
"/memos.api.v1.AuthService/CreateSession": true,
"/memos.api.v1.AuthService/GetCurrentSession": true,
"/memos.api.v1.AuthService/SignUp": true,
"/memos.api.v1.UserService/CreateUser": true,
"/memos.api.v1.UserService/GetUser": true,
"/memos.api.v1.UserService/GetUserAvatar": true,
"/memos.api.v1.UserService/GetUserStats": true,

@ -17,7 +17,6 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/internal/base"
"github.com/usememos/memos/internal/util"
"github.com/usememos/memos/plugin/idp"
"github.com/usememos/memos/plugin/idp/oauth2"
@ -206,54 +205,6 @@ func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTim
return nil
}
func (s *APIV1Service) SignUp(ctx context.Context, request *v1pb.SignUpRequest) (*v1pb.User, error) {
workspaceGeneralSetting, err := s.Store.GetWorkspaceGeneralSetting(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace general setting, error: %v", err)
}
if workspaceGeneralSetting.DisallowUserRegistration {
return nil, status.Errorf(codes.PermissionDenied, "sign up is not allowed")
}
passwordHash, err := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to generate password hash, error: %v", err)
}
create := &store.User{
Username: request.Username,
Nickname: request.Username,
PasswordHash: string(passwordHash),
}
if !base.UIDMatcher.MatchString(strings.ToLower(create.Username)) {
return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", create.Username)
}
hostUserType := store.RoleHost
existedHostUsers, err := s.Store.ListUsers(ctx, &store.FindUser{
Role: &hostUserType,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to list host users, error: %v", err)
}
if len(existedHostUsers) == 0 {
// Change the default role to host if there is no host user.
create.Role = store.RoleHost
} else {
create.Role = store.RoleUser
}
user, err := s.Store.CreateUser(ctx, create)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create user, error: %v", err)
}
if err := s.doSignIn(ctx, user, time.Now().Add(AccessTokenDuration)); err != nil {
return nil, status.Errorf(codes.Internal, "failed to sign in, error: %v", err)
}
return convertUserFromStore(user), nil
}
func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionRequest) (*emptypb.Empty, error) {
user, err := s.GetCurrentUser(ctx)
if err != nil {

@ -144,15 +144,36 @@ func (s *APIV1Service) GetUserAvatar(ctx context.Context, request *v1pb.GetUserA
}
func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserRequest) (*v1pb.User, error) {
currentUser, err := s.GetCurrentUser(ctx)
// Check if there are any existing host users (for first-time setup detection)
hostUserType := store.RoleHost
existedHostUsers, err := s.Store.ListUsers(ctx, &store.FindUser{
Role: &hostUserType,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
}
if currentUser.Role != store.RoleHost {
return nil, status.Errorf(codes.PermissionDenied, "permission denied")
return nil, status.Errorf(codes.Internal, "failed to list host users: %v", err)
}
// Determine the role to assign and check permissions
var roleToAssign store.Role
if len(existedHostUsers) == 0 {
// First-time setup: create the first user as HOST (no authentication required)
roleToAssign = store.RoleHost
} else {
// Regular user creation: allow unauthenticated creation of normal users
// But if authenticated, check if user has HOST permission for any role
currentUser, err := s.GetCurrentUser(ctx)
if err == nil && currentUser != nil && currentUser.Role == store.RoleHost {
// Authenticated HOST user can create users with any role specified in request
if request.User.Role != v1pb.User_ROLE_UNSPECIFIED {
roleToAssign = convertUserRoleToStore(request.User.Role)
} else {
roleToAssign = store.RoleUser
}
} else {
// Unauthenticated or non-HOST users can only create normal users
roleToAssign = store.RoleUser
}
}
// TODO: Handle request_id for idempotency
// TODO: Handle user_id field if provided
if !base.UIDMatcher.MatchString(strings.ToLower(request.User.Username)) {
return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", request.User.Username)
@ -165,7 +186,7 @@ func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserR
Username: request.User.Username,
Email: request.User.Email,
DisplayName: request.User.DisplayName,
Role: request.User.Role,
Role: convertUserRoleFromStore(roleToAssign),
}, nil
}
@ -176,7 +197,7 @@ func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserR
user, err := s.Store.CreateUser(ctx, &store.User{
Username: request.User.Username,
Role: convertUserRoleToStore(request.User.Role),
Role: roleToAssign,
Email: request.User.Email,
Nickname: request.User.DisplayName,
PasswordHash: string(passwordHash),

@ -6,11 +6,12 @@ import { useState } from "react";
import { toast } from "react-hot-toast";
import { Link } from "react-router-dom";
import AuthFooter from "@/components/AuthFooter";
import { authServiceClient } from "@/grpcweb";
import { authServiceClient, userServiceClient } from "@/grpcweb";
import useLoading from "@/hooks/useLoading";
import useNavigateTo from "@/hooks/useNavigateTo";
import { workspaceStore } from "@/store/v2";
import { initialUserStore } from "@/store/v2/user";
import { User, User_Role } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n";
const SignUp = observer(() => {
@ -47,7 +48,15 @@ const SignUp = observer(() => {
try {
actionBtnLoadingState.setLoading();
await authServiceClient.signUp({ username, password });
const user = User.fromPartial({
username,
password,
role: User_Role.USER,
});
await userServiceClient.createUser({ user });
await authServiceClient.createSession({
passwordCredentials: { username, password },
});
await initialUserStore();
navigateTo("/");
} catch (error: any) {

@ -68,19 +68,6 @@ export interface SSOCredentials {
export interface DeleteSessionRequest {
}
export interface SignUpRequest {
/**
* The username to sign up with.
* Required field that must be unique across the system.
*/
username: string;
/**
* The password to sign up with.
* Required field that should meet security requirements.
*/
password: string;
}
function createBaseGetCurrentSessionRequest(): GetCurrentSessionRequest {
return {};
}
@ -397,64 +384,6 @@ export const DeleteSessionRequest: MessageFns<DeleteSessionRequest> = {
},
};
function createBaseSignUpRequest(): SignUpRequest {
return { username: "", password: "" };
}
export const SignUpRequest: MessageFns<SignUpRequest> = {
encode(message: SignUpRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.username !== "") {
writer.uint32(10).string(message.username);
}
if (message.password !== "") {
writer.uint32(18).string(message.password);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): SignUpRequest {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseSignUpRequest();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.username = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.password = reader.string();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
create(base?: DeepPartial<SignUpRequest>): SignUpRequest {
return SignUpRequest.fromPartial(base ?? {});
},
fromPartial(object: DeepPartial<SignUpRequest>): SignUpRequest {
const message = createBaseSignUpRequest();
message.username = object.username ?? "";
message.password = object.password ?? "";
return message;
},
};
export type AuthServiceDefinition = typeof AuthServiceDefinition;
export const AuthServiceDefinition = {
name: "AuthService",
@ -608,50 +537,6 @@ export const AuthServiceDefinition = {
},
},
},
/**
* SignUp creates a new user account with username and password.
* Returns the newly created user information upon successful registration.
*/
signUp: {
name: "SignUp",
requestType: SignUpRequest,
requestStream: false,
responseType: User,
responseStream: false,
options: {
_unknownFields: {
578365826: [
new Uint8Array([
24,
58,
1,
42,
34,
19,
47,
97,
112,
105,
47,
118,
49,
47,
97,
117,
116,
104,
47,
115,
105,
103,
110,
117,
112,
]),
],
},
},
},
},
} as const;

Loading…
Cancel
Save