Add user store
This commit is contained in:
parent
fa32f76a61
commit
be230233dc
1211
pb/ezshare.pb.go
1211
pb/ezshare.pb.go
File diff suppressed because it is too large
Load Diff
@ -207,3 +207,197 @@ var FileService_ServiceDesc = grpc.ServiceDesc{
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "protos/ezshare.proto",
|
||||
}
|
||||
|
||||
// UserServiceClient is the client API for UserService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type UserServiceClient interface {
|
||||
Register(ctx context.Context, in *RegisterUserRequest, opts ...grpc.CallOption) (*RegisterUserResponse, error)
|
||||
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)
|
||||
}
|
||||
|
||||
type userServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
|
||||
return &userServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *userServiceClient) Register(ctx context.Context, in *RegisterUserRequest, opts ...grpc.CallOption) (*RegisterUserResponse, error) {
|
||||
out := new(RegisterUserResponse)
|
||||
err := c.cc.Invoke(ctx, "/ezshare.UserService/Register", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) Login(ctx context.Context, in *LoginUserRequest, opts ...grpc.CallOption) (*LoginUserResponse, error) {
|
||||
out := new(LoginUserResponse)
|
||||
err := c.cc.Invoke(ctx, "/ezshare.UserService/Login", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) List(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) {
|
||||
out := new(ListUsersResponse)
|
||||
err := c.cc.Invoke(ctx, "/ezshare.UserService/List", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userServiceClient) Approve(ctx context.Context, in *ApproveUserRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/ezshare.UserService/Approve", 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
|
||||
type UserServiceServer interface {
|
||||
Register(context.Context, *RegisterUserRequest) (*RegisterUserResponse, error)
|
||||
Login(context.Context, *LoginUserRequest) (*LoginUserResponse, error)
|
||||
List(context.Context, *ListUsersRequest) (*ListUsersResponse, error)
|
||||
Approve(context.Context, *ApproveUserRequest) (*Empty, error)
|
||||
mustEmbedUnimplementedUserServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedUserServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedUserServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceServer) Register(context.Context, *RegisterUserRequest) (*RegisterUserResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Register not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) Login(context.Context, *LoginUserRequest) (*LoginUserResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) List(context.Context, *ListUsersRequest) (*ListUsersResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) Approve(context.Context, *ApproveUserRequest) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Approve not implemented")
|
||||
}
|
||||
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
|
||||
|
||||
// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to UserServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeUserServiceServer interface {
|
||||
mustEmbedUnimplementedUserServiceServer()
|
||||
}
|
||||
|
||||
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
|
||||
s.RegisterService(&UserService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _UserService_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RegisterUserRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).Register(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ezshare.UserService/Register",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).Register(ctx, req.(*RegisterUserRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LoginUserRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).Login(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ezshare.UserService/Login",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).Login(ctx, req.(*LoginUserRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListUsersRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).List(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ezshare.UserService/List",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).List(ctx, req.(*ListUsersRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserService_Approve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApproveUserRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserServiceServer).Approve(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ezshare.UserService/Approve",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserServiceServer).Approve(ctx, req.(*ApproveUserRequest))
|
||||
}
|
||||
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)
|
||||
var UserService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ezshare.UserService",
|
||||
HandlerType: (*UserServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Register",
|
||||
Handler: _UserService_Register_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Login",
|
||||
Handler: _UserService_Login_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "List",
|
||||
Handler: _UserService_List_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Approve",
|
||||
Handler: _UserService_Approve_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "protos/ezshare.proto",
|
||||
}
|
||||
|
@ -1,11 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "gitea.benny.dog/torjus/ezshare/pb";
|
||||
package ezshare;
|
||||
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "gitea.benny.dog/torjus/ezshare/pb";
|
||||
|
||||
/////////////////////
|
||||
// Common messages //
|
||||
/////////////////////
|
||||
message Empty {}
|
||||
|
||||
////////////////////////
|
||||
// FILE RELATED STUFF //
|
||||
////////////////////////
|
||||
message File {
|
||||
string file_id = 1;
|
||||
bytes data = 2;
|
||||
@ -62,4 +69,71 @@ service FileService {
|
||||
rpc GetFile(GetFileRequest) returns (GetFileResponse) {}
|
||||
rpc DeleteFile(DeleteFileRequest) returns (DeleteFileResponse) {}
|
||||
rpc ListFiles(ListFilesRequest) returns (ListFilesResponse) {}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
// USER RELATED STUFF //
|
||||
////////////////////////
|
||||
message User {
|
||||
string id = 1;
|
||||
string username = 2;
|
||||
bytes hashed_password = 3;
|
||||
enum Role {
|
||||
UNAPPROVED = 0;
|
||||
VIEWONLY = 1;
|
||||
USER = 2;
|
||||
ADMIN = 3;
|
||||
}
|
||||
Role user_role = 4;
|
||||
bool active = 5;
|
||||
}
|
||||
|
||||
// Register
|
||||
message RegisterUserRequest {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
}
|
||||
message RegisterUserResponse {
|
||||
string id = 1;
|
||||
string token = 2;
|
||||
}
|
||||
|
||||
// Login
|
||||
message LoginUserRequest {
|
||||
message UserPasswordLogin {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
}
|
||||
message TokenLogin {
|
||||
string token = 1;
|
||||
}
|
||||
|
||||
oneof requested_login {
|
||||
TokenLogin with_token = 1;
|
||||
UserPasswordLogin with_password = 2;
|
||||
}
|
||||
}
|
||||
message LoginUserResponse {
|
||||
bytes server_cert = 1;
|
||||
bytes client_cert = 2;
|
||||
bytes client_key = 3;
|
||||
}
|
||||
|
||||
// List
|
||||
message ListUsersRequest {
|
||||
}
|
||||
message ListUsersResponse {
|
||||
repeated User users = 1;
|
||||
}
|
||||
|
||||
// Approve
|
||||
message ApproveUserRequest {
|
||||
string user_id = 1;
|
||||
}
|
||||
|
||||
service UserService {
|
||||
rpc Register(RegisterUserRequest) returns (RegisterUserResponse) {}
|
||||
rpc Login(LoginUserRequest) returns (LoginUserResponse) {}
|
||||
rpc List(ListUsersRequest) returns (ListUsersResponse) {}
|
||||
rpc Approve(ApproveUserRequest) returns (Empty) {}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ type BoltStore struct {
|
||||
var bktKey = []byte("files")
|
||||
var bktKeyCerts = []byte("certs")
|
||||
var bktKeyKeys = []byte("keys")
|
||||
var bktKeyUsers = []byte("users")
|
||||
|
||||
func NewBoltStore(path string) (*BoltStore, error) {
|
||||
s := &BoltStore{}
|
||||
@ -38,7 +39,9 @@ func NewBoltStore(path string) (*BoltStore, error) {
|
||||
if _, err := t.CreateBucketIfNotExists(bktKeyKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := t.CreateBucketIfNotExists(bktKeyUsers); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@ -188,14 +191,53 @@ func (s *BoltStore) ListCertificates() ([]string, error) {
|
||||
var ids []string
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(bktKeyCerts)
|
||||
bkt.ForEach(func(k, v []byte) error {
|
||||
return bkt.ForEach(func(k, v []byte) error {
|
||||
ids = append(ids, string(k))
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (s *BoltStore) StoreUser(user *pb.User) error {
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(bktKeyUsers)
|
||||
data, err := proto.Marshal(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bkt.Put([]byte(user.Id), data)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BoltStore) GetUser(id string) (*pb.User, error) {
|
||||
var data []byte
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(bktKeyUsers)
|
||||
data = bkt.Get([]byte(id))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var user pb.User
|
||||
err = proto.Unmarshal(data, &user)
|
||||
return &user, err
|
||||
}
|
||||
|
||||
func (s *BoltStore) ListUsers() ([]string, error) {
|
||||
var ids []string
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(bktKeyUsers)
|
||||
return bkt.ForEach(func(k, _ []byte) error {
|
||||
ids = append(ids, string(k))
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return ids, err
|
||||
}
|
||||
|
@ -10,19 +10,29 @@ import (
|
||||
func TestBoltStore(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "boltstore.db")
|
||||
s, err := store.NewBoltStore(path)
|
||||
defer s.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening store: %s", err)
|
||||
}
|
||||
doFileStoreTest(s, t)
|
||||
_ = s.Close()
|
||||
}
|
||||
|
||||
func TestBoltCertificateStore(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "boltstore.db")
|
||||
s, err := store.NewBoltStore(path)
|
||||
defer s.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening store: %s", err)
|
||||
}
|
||||
doCertificateStoreTest(s, t)
|
||||
_ = s.Close()
|
||||
}
|
||||
|
||||
func TestBoltUserStore(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "boltstore.db")
|
||||
s, err := store.NewBoltStore(path)
|
||||
defer s.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening store: %s", err)
|
||||
}
|
||||
doUserStoreTests(s, t)
|
||||
}
|
||||
|
@ -9,9 +9,6 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var _ FileStore = &MemoryStore{}
|
||||
var _ CertificateStore = &MemoryStore{}
|
||||
|
||||
type MemoryStore struct {
|
||||
filesLock sync.RWMutex
|
||||
files map[string]*pb.File
|
||||
@ -19,6 +16,8 @@ type MemoryStore struct {
|
||||
certs map[string][]byte
|
||||
keyLock sync.RWMutex
|
||||
keys map[string][]byte
|
||||
usersLock sync.RWMutex
|
||||
users map[string]*pb.User
|
||||
}
|
||||
|
||||
func NewMemoryStore() *MemoryStore {
|
||||
@ -26,9 +25,16 @@ func NewMemoryStore() *MemoryStore {
|
||||
files: make(map[string]*pb.File),
|
||||
certs: make(map[string][]byte),
|
||||
keys: make(map[string][]byte),
|
||||
users: make(map[string]*pb.User),
|
||||
}
|
||||
}
|
||||
|
||||
///////////////
|
||||
// FileStore //
|
||||
///////////////
|
||||
|
||||
var _ FileStore = &MemoryStore{}
|
||||
|
||||
func (s *MemoryStore) GetFile(id string) (*pb.File, error) {
|
||||
s.filesLock.RLock()
|
||||
defer s.filesLock.RUnlock()
|
||||
@ -76,6 +82,12 @@ func (s *MemoryStore) ListFiles() ([]*pb.ListFilesResponse_ListFileInfo, error)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// CertificateStore //
|
||||
//////////////////////
|
||||
|
||||
var _ CertificateStore = &MemoryStore{}
|
||||
|
||||
func (s *MemoryStore) GetCertificate(id string) (*x509.Certificate, error) {
|
||||
s.certLock.Lock()
|
||||
defer s.certLock.Unlock()
|
||||
@ -134,3 +146,39 @@ func (s *MemoryStore) ListCertificates() ([]string, error) {
|
||||
}
|
||||
return certIDs, nil
|
||||
}
|
||||
|
||||
///////////////
|
||||
// UserStore //
|
||||
///////////////
|
||||
|
||||
var _ UserStore = &MemoryStore{}
|
||||
|
||||
func (s *MemoryStore) StoreUser(user *pb.User) error {
|
||||
s.usersLock.Lock()
|
||||
defer s.usersLock.Unlock()
|
||||
|
||||
s.users[user.Id] = user
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryStore) GetUser(id string) (*pb.User, error) {
|
||||
s.usersLock.RLock()
|
||||
defer s.usersLock.RUnlock()
|
||||
user, ok := s.users[id]
|
||||
if !ok {
|
||||
// TODO: Update error
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *MemoryStore) ListUsers() ([]string, error) {
|
||||
s.usersLock.RLock()
|
||||
defer s.usersLock.RUnlock()
|
||||
|
||||
var ids []string
|
||||
for id := range s.users {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
@ -14,3 +14,8 @@ func TestMemoryCertificateStore(t *testing.T) {
|
||||
s := store.NewMemoryStore()
|
||||
doCertificateStoreTest(s, t)
|
||||
}
|
||||
|
||||
func TestMemoryUserStore(t *testing.T) {
|
||||
s := store.NewMemoryStore()
|
||||
doUserStoreTests(s, t)
|
||||
}
|
||||
|
@ -24,3 +24,9 @@ type CertificateStore interface {
|
||||
StoreKey(id string, key *ecdsa.PrivateKey) error
|
||||
ListCertificates() ([]string, error)
|
||||
}
|
||||
|
||||
type UserStore interface {
|
||||
StoreUser(user *pb.User) error
|
||||
GetUser(id string) (*pb.User, error)
|
||||
ListUsers() ([]string, error)
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
@ -140,3 +142,39 @@ func doCertificateStoreTest(s store.CertificateStore, t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func doUserStoreTests(s store.UserStore, t *testing.T) {
|
||||
t.Run("Basics", func(t *testing.T) {
|
||||
// Store user
|
||||
user := &pb.User{
|
||||
Id: uuid.Must(uuid.NewRandom()).String(),
|
||||
Username: "testuser",
|
||||
UserRole: pb.User_USER,
|
||||
Active: true,
|
||||
}
|
||||
|
||||
if err := s.StoreUser(user); err != nil {
|
||||
t.Fatalf("Error storing user: %s", err)
|
||||
}
|
||||
|
||||
retrieved, err := s.GetUser(user.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("Retriving user returned error: %s", err)
|
||||
}
|
||||
|
||||
if !proto.Equal(user, retrieved) {
|
||||
t.Fatalf("Retrieved user does not match original")
|
||||
}
|
||||
|
||||
list, err := s.ListUsers()
|
||||
if err != nil {
|
||||
t.Fatalf("Error listing users")
|
||||
}
|
||||
if len(list) != 1 {
|
||||
t.Fatalf("User list wrong length")
|
||||
}
|
||||
if list[0] != user.Id {
|
||||
t.Fatalf("User list has wrong id")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user