diff --git a/actions/client.go b/actions/client.go new file mode 100644 index 0000000..896ae2f --- /dev/null +++ b/actions/client.go @@ -0,0 +1,340 @@ +package actions + +import ( + "bufio" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "syscall" + "time" + + "gitea.benny.dog/torjus/ezshare/certs" + "gitea.benny.dog/torjus/ezshare/config" + "gitea.benny.dog/torjus/ezshare/pb" + "gitea.benny.dog/torjus/ezshare/server" + "github.com/urfave/cli/v2" + "golang.org/x/term" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +func ActionClientGet(c *cli.Context) error { + addr := c.String("addr") + conn, err := grpc.DialContext(c.Context, addr, grpc.WithInsecure()) + if err != nil { + return err + } + defer conn.Close() + + client := pb.NewFileServiceClient(conn) + + for _, arg := range c.Args().Slice() { + req := &pb.GetFileRequest{Id: arg} + resp, err := client.GetFile(c.Context, req) + if err != nil { + return err + } + filename := resp.File.FileId + if resp.File.Metadata.OriginalFilename != "" { + filename = resp.File.Metadata.OriginalFilename + } + + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + if _, err := f.Write(resp.File.Data); err != nil { + return err + } + fmt.Printf("Wrote file '%s'\n", filename) + } + + return nil +} + +func ActionClientUpload(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() + + client := pb.NewFileServiceClient(conn) + + for _, arg := range c.Args().Slice() { + f, err := os.Open(arg) + if err != nil { + return err + } + + data, err := io.ReadAll(f) + if err != nil { + return err + } + + req := &pb.UploadFileRequest{Data: data, OriginalFilename: filepath.Base(arg)} + + resp, err := client.UploadFile(c.Context, req) + if err != nil { + return err + } + fmt.Printf("%s uploaded with id %s. Available at %s\n", arg, resp.Id, resp.FileUrl) + } + return nil +} + +func ActionClientList(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() + + client := pb.NewFileServiceClient(conn) + + resp, err := client.ListFiles(c.Context, &pb.ListFilesRequest{}) + if err != nil { + return err + } + + for _, elem := range resp.Files { + fmt.Println(elem.FileId) + } + return nil +} + +func ActionClientDelete(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() + + client := pb.NewFileServiceClient(conn) + for _, arg := range c.Args().Slice() { + _, err := client.DeleteFile(c.Context, &pb.DeleteFileRequest{Id: arg}) + if err != nil { + return fmt.Errorf("error deleting file: %w", err) + } + fmt.Printf("Deleted file %s\n", arg) + } + return nil +} + +func ActionClientLogin(c *cli.Context) error { + if err := config.CreateDefaultConfigDir(); err != nil { + return err + } + configFilePath, err := config.DefaultConfigFilePath() + if err != nil { + return err + } + + if _, err := os.Stat(configFilePath); !errors.Is(err, os.ErrNotExist) { + if err == nil { + if !c.Bool("overwrite") { + return cli.Exit("Config already exists. To overwrite run with --overwrite\n", 1) + } + } else { + return err + } + } + configDirPath := filepath.Dir(configFilePath) + clientCertPath := filepath.Join(configDirPath, "client.pem") + clientKeyPath := filepath.Join(configDirPath, "client.key") + serverCertPath := filepath.Join(configDirPath, "server.pem") + + if c.Args().Len() != 1 { + return cli.Exit("Need 1 argument", 1) + } + + // Fetch server certificate + ctx, cancel := context.WithTimeout(c.Context, 10*time.Second) + defer cancel() + certEndpoint := fmt.Sprintf("%s/%s", c.Args().First(), "server.pem") + certReq, err := http.NewRequestWithContext(ctx, http.MethodGet, certEndpoint, nil) + if err != nil { + return cli.Exit(fmt.Sprintf("unable to create http request: %s", err), 1) + } + verbosePrint(c, fmt.Sprintf("fetching cert from %s", certEndpoint)) + certResp, err := http.DefaultClient.Do(certReq) + if err != nil { + return cli.Exit(fmt.Sprintf("error fetching server cert: %s", err), 1) + } + defer certResp.Body.Close() + + serverCert, err := io.ReadAll(certResp.Body) + if err != nil { + return cli.Exit(fmt.Sprintf("error reading certificate from server: %s", err), 1) + } + + // Fetch metadata + mdEndpoint := fmt.Sprintf("%s/%s", c.Args().First(), "metadata") + mdReq, err := http.NewRequestWithContext(ctx, http.MethodGet, mdEndpoint, nil) + if err != nil { + return cli.Exit(fmt.Sprintf("unable to create http request: %s", err), 1) + } + + verbosePrint(c, fmt.Sprintf("fetching metadata from %s", mdEndpoint)) + mdResp, err := http.DefaultClient.Do(mdReq) + if err != nil { + return cli.Exit(fmt.Sprintf("error fetching server cert: %s", err), 1) + } + defer mdResp.Body.Close() + + decoder := json.NewDecoder(mdResp.Body) + var md server.MetadataResponse + if err := decoder.Decode(&md); err != nil { + return cli.Exit(fmt.Sprintf("unable to decode metadata response: %s", err), 1) + } + + scanner := bufio.NewScanner(os.Stdin) + fmt.Printf("username: ") + scanner.Scan() + username := scanner.Text() + + fmt.Printf("password: ") + passwordBytes, err := term.ReadPassword(int(syscall.Stdin)) + if err != nil { + return cli.Exit(fmt.Sprintf("unable to read password: %s", err), 1) + } + password := string(passwordBytes) + + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(serverCert) { + return cli.Exit(fmt.Sprintf("unable to use server certificate: %s", err), 1) + } + // Generate temporary self-signed cert + keyBytes, certBytes, err := certs.GenCACert() + if err != nil { + return cli.Exit(fmt.Sprintf("unable to generate self-signed certificate: %s", err), 1) + } + keyPem, err := certs.ToPEM(keyBytes, "EC PRIVATE KEY") + if err != nil { + return cli.Exit(fmt.Sprintf("unable to pem-encode key: %s", err), 1) + } + certPem, err := certs.ToPEM(certBytes, "CERTIFICATE") + if err != nil { + return cli.Exit(fmt.Sprintf("unable to pem-encode key: %s", err), 1) + } + clientCert, err := tls.X509KeyPair(certPem, keyPem) + if err != nil { + return cli.Exit(fmt.Sprintf("unable to use self-signed certificate: %s", err), 1) + } + + creds := credentials.NewTLS(&tls.Config{RootCAs: certPool, Certificates: []tls.Certificate{clientCert}}) + + verbosePrint(c, fmt.Sprintf("dialing grpc at %s", md.GRPCEndpoint)) + conn, err := grpc.DialContext(c.Context, md.GRPCEndpoint, grpc.WithTransportCredentials(creds)) + if err != nil { + return err + } + defer conn.Close() + + client := pb.NewUserServiceClient(conn) + + resp, err := client.Login(c.Context, &pb.LoginUserRequest{Username: username, Password: password}) + if err != nil { + return err + } + + // Write key to file + verbosePrint(c, fmt.Sprintf("Writing client certificate key to %s", clientKeyPath)) + keyFile, err := os.Create(clientKeyPath) + if err != nil { + return cli.Exit(fmt.Sprintf("unable create file for client key: %s", err), 1) + } + defer keyFile.Close() + if _, err := keyFile.Write(resp.ClientKey); err != nil { + return cli.Exit(fmt.Sprintf("unable write client key to file: %s", err), 1) + } + + // Write client cert to file + verbosePrint(c, fmt.Sprintf("Writing client certificate to %s", clientKeyPath)) + clientCertFile, err := os.Create(clientCertPath) + if err != nil { + return cli.Exit(fmt.Sprintf("unable create file for client cert: %s", err), 1) + } + defer clientCertFile.Close() + if _, err := clientCertFile.Write(resp.ClientCert); err != nil { + return cli.Exit(fmt.Sprintf("unable write client cert to file: %s", err), 1) + } + + // Write server cer to file + verbosePrint(c, fmt.Sprintf("Writing server certificate to %s", serverCertPath)) + serverCertFile, err := os.Create(serverCertPath) + if err != nil { + return cli.Exit(fmt.Sprintf("unable create file for client key: %s", err), 1) + } + defer serverCertFile.Close() + if _, err := serverCertFile.Write(serverCert); err != nil { + return cli.Exit(fmt.Sprintf("unable write client cert to file: %s", err), 1) + } + + // Write config + cfg := config.FromDefault() + cfg.Client.Certs.CertificateKeyPath = clientKeyPath + cfg.Client.Certs.CertificatePath = clientCertPath + cfg.Client.DefaultServer = md.GRPCEndpoint + cfg.Client.ServerCertPath = serverCertPath + + if err := cfg.ToDefaultFile(); err != nil { + return cli.Exit(fmt.Sprintf("unable write config to file: %s", err), 1) + } + + return nil +} diff --git a/actions/misc.go b/actions/misc.go new file mode 100644 index 0000000..c1ae146 --- /dev/null +++ b/actions/misc.go @@ -0,0 +1,44 @@ +package actions + +import ( + "fmt" + + "gitea.benny.dog/torjus/ezshare/certs" + "gitea.benny.dog/torjus/ezshare/config" + "github.com/urfave/cli/v2" +) + +func ActionGencerts(c *cli.Context) error { + outDir := "." + if c.IsSet("out-dir") { + outDir = c.String("out-dir") + } + if !c.IsSet("hostname") { + return fmt.Errorf("--hostname required") + } + hostname := c.String("hostname") + return certs.GenAllCerts(outDir, hostname) +} + +func ActionInitConfig(c *cli.Context) error { + defaultCfg := config.FromDefault() + return defaultCfg.ToDefaultFile() +} + +func getConfig(c *cli.Context) (*config.Config, error) { + if c.IsSet("config") { + cfgPath := c.String("config") + return config.FromFile(cfgPath) + } + cfg, err := config.FromDefaultLocations() + if err == nil { + verbosePrint(c, fmt.Sprintf("Config loaded from %s", cfg.Location())) + } + return cfg, err +} + +func verbosePrint(c *cli.Context, message string) { + if c.Bool("verbose") { + fmt.Println(message) + } +} diff --git a/actions/serve.go b/actions/serve.go new file mode 100644 index 0000000..207c529 --- /dev/null +++ b/actions/serve.go @@ -0,0 +1,222 @@ +package actions + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "log" + "net" + "net/http" + "os" + "os/signal" + "time" + + "gitea.benny.dog/torjus/ezshare/certs" + "gitea.benny.dog/torjus/ezshare/pb" + "gitea.benny.dog/torjus/ezshare/server" + "gitea.benny.dog/torjus/ezshare/server/interceptors" + "gitea.benny.dog/torjus/ezshare/store" + "github.com/google/uuid" + "github.com/urfave/cli/v2" + "golang.org/x/crypto/bcrypt" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +func ActionServe(c *cli.Context) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + // Read certificates + srvCertBytes, err := cfg.Server.GRPC.Certs.GetCertBytes() + if err != nil { + return err + } + srvKeyBytes, err := cfg.Server.GRPC.Certs.GetKeyBytes() + if err != nil { + return err + } + caCertBytes, err := cfg.Server.GRPC.CACerts.GetCertBytes() + if err != nil { + return err + } + caKeyBytes, err := cfg.Server.GRPC.CACerts.GetKeyBytes() + if err != nil { + return err + } + + // Setup file store + s, fileCloseFunc, err := cfg.Server.FileStoreConfig.GetStore() + if err != nil { + return fmt.Errorf("unable to initialize file store: %w", err) + } + defer fileCloseFunc() + + // Setup user store + userStore, userCloseFunc, err := cfg.Server.UserStoreConfig.GetStore() + if err != nil { + return fmt.Errorf("unable to initialize user store: %w", err) + } + defer userCloseFunc() + + // Create initial admin-user if neccessary + if err := initializeUsers(userStore); err != nil { + return fmt.Errorf("error initializing store: %w", err) + } + + // Setup cert store + // TODO: User proper store + certStore := store.NewMemoryStore() + + // Setup cert-service + certSvc, err := certs.NewCertService(certStore, caCertBytes, caKeyBytes) + if err != nil { + return fmt.Errorf("error initializing certificate service: %w", err) + } + + // Setup shutdown-handling + rootCtx, rootCancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer rootCancel() + + // Used to initiate grpc shutdown + grpcCtx, grpcCancel := context.WithCancel(rootCtx) + defer grpcCancel() + + // Cancelled once grpc is successfully shut down + grpcShutdownCtx, grpcShutdownCancel := context.WithCancel(context.Background()) + defer grpcShutdownCancel() + + // Start grpc server + go func() { + grpcAddr := cfg.Server.GRPC.ListenAddr + if c.IsSet("grpc-addr") { + grpcAddr = c.String("grpc-addr") + } + + // Setup file-service + grpcFileServer := server.NewGRPCFileServiceServer(s) + grpcFileServer.Hostname = cfg.Server.Hostname + if c.IsSet("hostname") { + grpcFileServer.Hostname = c.String("hostname") + } + + // Setup user-service + grpcUserServer := server.NewGRPCUserServiceServer(userStore, certSvc) + + lis, err := net.Listen("tcp", grpcAddr) + if err != nil { + log.Printf("Unable to setup grpc listener: %s\n", err) + rootCancel() + } + srvCert, err := tls.X509KeyPair(srvCertBytes, srvKeyBytes) + if err != nil { + log.Printf("Unable load server certs: %s\n", err) + rootCancel() + } + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(caCertBytes) { + log.Println("Unable to load CA cert") + rootCancel() + } + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{srvCert}, + ClientAuth: tls.RequireAnyClientCert, + ClientCAs: certPool, + } + creds := credentials.NewTLS(tlsConfig) + + grpcServer := grpc.NewServer( + grpc.Creds(creds), + grpc.ChainUnaryInterceptor(interceptors.NewAuthInterceptor(&store.MemoryStore{})), + ) + pb.RegisterFileServiceServer(grpcServer, grpcFileServer) + pb.RegisterUserServiceServer(grpcServer, grpcUserServer) + + // wait for cancel + go func() { + <-grpcCtx.Done() + grpcServer.GracefulStop() + }() + + log.Printf("Starting grpc server") + if err = grpcServer.Serve(lis); err != nil { + log.Printf("GRPC Shutdown with error: %s\n", err) + rootCancel() + } + log.Println("GRPC Shutdown") + grpcShutdownCancel() + }() + + httpCtx, httpCancel := context.WithCancel(rootCtx) + defer httpCancel() + + httpShutdownCtx, httpShutdownCancel := context.WithCancel(context.Background()) + defer httpShutdownCancel() + // Start http server + go func() { + httpAddr := ":8088" + if c.IsSet("http-addr") { + httpAddr = c.String("http-addr") + } + httpServer := server.NewHTTPSever(s, srvCertBytes, cfg.Server.GRPCEndpoint) + httpServer.Addr = httpAddr + + // wait for cancel + go func() { + <-httpCtx.Done() + timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + httpServer.Shutdown(timeoutCtx) + }() + + log.Printf("Starting http server") + if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Printf("HTTP Server shutdown with error: %s\n", err) + rootCancel() + } + log.Println("HTTP Shutdown") + httpShutdownCancel() + }() + + <-grpcShutdownCtx.Done() + <-httpShutdownCtx.Done() + return nil +} + +func initializeUsers(us store.UserStore) error { + // TODO: Logging + userIDs, err := us.ListUsers() + if err != nil { + return err + } + + if len(userIDs) != 0 { + return nil + } + + // no users, create initial admin-user + log.Printf("No users in store. Creating admin-user.") + password := uuid.Must(uuid.NewRandom()).String() + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return err + } + admin := &pb.User{ + Id: uuid.Must(uuid.NewRandom()).String(), + HashedPassword: hashedPassword, + Username: "admin", + UserRole: pb.User_ADMIN, + Active: true, + } + + if err := us.StoreUser(admin); err != nil { + return err + } + log.Printf("user created %s:%s", admin.Username, password) + + return nil +} diff --git a/certs/generate.go b/certs/generate.go index 05ae11b..21037d1 100644 --- a/certs/generate.go +++ b/certs/generate.go @@ -15,6 +15,19 @@ import ( "time" ) +func ToPEM(data []byte, pemType string) ([]byte, error) { + pemData := new(bytes.Buffer) + err := pem.Encode(pemData, &pem.Block{ + Type: pemType, + Bytes: data, + }) + if err != nil { + return nil, err + } + + return pemData.Bytes(), nil +} + func WriteCert(data []byte, filename string) error { // Convert to PEM certPEM := new(bytes.Buffer) @@ -104,7 +117,7 @@ func GenAllCerts(path, domain string) error { // Create server certs dnsNames := []string{domain} - srvKey, srvCrt, err := GenCert(caPub, caPriv, dnsNames) + srvKey, srvCrt, err := GenCert("server", caPub, caPriv, dnsNames) if err != nil { return err } @@ -115,7 +128,7 @@ func GenAllCerts(path, domain string) error { return err } - clientKey, clientCrt, err := GenCert(caPub, caPriv, []string{}) + clientKey, clientCrt, err := GenCert("client", caPub, caPriv, []string{}) if err != nil { return err } @@ -129,7 +142,7 @@ func GenAllCerts(path, domain string) error { return nil } -func GenCert(caPub, caPrivKey []byte, dnsNames []string) (priv, pub []byte, err error) { +func GenCert(cn string, caPub, caPrivKey []byte, dnsNames []string) (priv, pub []byte, err error) { // Parse ca ca, err := x509.ParseCertificate(caPub) if err != nil { @@ -144,6 +157,7 @@ func GenCert(caPub, caPrivKey []byte, dnsNames []string) (priv, pub []byte, err cert := &x509.Certificate{ SerialNumber: big.NewInt(time.Now().Unix()), Subject: pkix.Name{ + CommonName: cn, Organization: []string{"ezshare"}, Country: []string{"No"}, Locality: []string{"Oslo"}, diff --git a/cmd/ezshare.go b/cmd/ezshare.go deleted file mode 100644 index c1ad365..0000000 --- a/cmd/ezshare.go +++ /dev/null @@ -1,452 +0,0 @@ -package main - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "log" - "net" - "net/http" - "os" - "os/signal" - "path/filepath" - "time" - - "gitea.benny.dog/torjus/ezshare/certs" - "gitea.benny.dog/torjus/ezshare/config" - "gitea.benny.dog/torjus/ezshare/pb" - "gitea.benny.dog/torjus/ezshare/server" - "github.com/urfave/cli/v2" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -func main() { - app := cli.App{ - Name: "ezshare", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "config", - Usage: "Path to config-file.", - }, - &cli.BoolFlag{ - Name: "verbose", - Aliases: []string{"v"}, - Usage: "Be more verbose", - }, - }, - Commands: []*cli.Command{ - { - Name: "serve", - Usage: "Start ezshare server", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "no-grpc", - Usage: "Do not enable grpc.", - }, - &cli.BoolFlag{ - Name: "no-http", - Usage: "Do not enable http.", - }, - &cli.StringFlag{ - Name: "grpc-addr", - Usage: "Address to listen for grpc.", - }, - &cli.StringFlag{ - Name: "http-addr", - Usage: "Address to listen for http.", - }, - &cli.StringFlag{ - Name: "hostname", - Usage: "Hostname used in links", - }, - }, - Action: ActionServe, - }, - { - Name: "client", - Usage: "Client commands", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "addr", - Usage: "Address of server.", - }, - }, - Subcommands: []*cli.Command{ - { - Name: "get", - Usage: "Get file with id", - ArgsUsage: "ID [ID]..", - Action: ActionClientGet, - }, - { - Name: "upload", - Usage: "Upload file(s)", - ArgsUsage: "PATH [PATH]..", - Action: ActionClientUpload, - }, - { - Name: "delete", - Usage: "Delete file with id", - ArgsUsage: "ID [ID]..", - Action: ActionClientDelete, - }, - { - Name: "list", - Usage: "List files", - Action: ActionClientList, - }, - { - Name: "config-init", - Usage: "Initialize default config", - Action: ActionInitConfig, - }, - }, - }, - { - Name: "cert", - Usage: "Certificate commands", - Subcommands: []*cli.Command{ - { - Name: "gen-all", - Usage: "Generate CA, Server and Client certificates", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "out-dir", - Usage: "Directory where certificates will be stored.", - }, - &cli.StringFlag{ - Name: "hostname", - Usage: "Hostname used for server certificate.", - }, - }, - Action: ActionGencerts, - }, - }, - }, - }, - } - - err := app.Run(os.Args) - if err != nil { - log.Printf("Error: %s\n", err) - } - -} - -func ActionServe(c *cli.Context) error { - cfg, err := getConfig(c) - if err != nil { - return err - } - - // Read certificates - srvCertBytes, err := cfg.Server.GRPC.Certs.GetCertBytes() - if err != nil { - return err - } - srvKeyBytes, err := cfg.Server.GRPC.Certs.GetKeyBytes() - if err != nil { - return err - } - caCertBytes, err := cfg.Server.GRPC.CACerts.GetCertBytes() - if err != nil { - return err - } - - // Setup store - s, closeFunc, err := cfg.Server.StoreConfig.GetStore() - if err != nil { - return fmt.Errorf("unable to initialize store: %w", err) - } - defer closeFunc() - // Setup shutdown-handling - rootCtx, rootCancel := signal.NotifyContext(context.Background(), os.Interrupt) - defer rootCancel() - - // Used to initiate grpc shutdown - grpcCtx, grpcCancel := context.WithCancel(rootCtx) - defer grpcCancel() - - // Cancelled once grpc is successfully shut down - grpcShutdownCtx, grpcShutdownCancel := context.WithCancel(context.Background()) - defer grpcShutdownCancel() - - // Start grpc server - go func() { - grpcAddr := cfg.Server.GRPC.ListenAddr - if c.IsSet("grpc-addr") { - grpcAddr = c.String("grpc-addr") - } - - grpcFileServer := server.NewGRPCFileServiceServer(s) - grpcFileServer.Hostname = cfg.Server.Hostname - if c.IsSet("hostname") { - grpcFileServer.Hostname = c.String("hostname") - } - - lis, err := net.Listen("tcp", grpcAddr) - if err != nil { - log.Printf("Unable to setup grpc listener: %s\n", err) - rootCancel() - } - srvCert, err := tls.X509KeyPair(srvCertBytes, srvKeyBytes) - if err != nil { - log.Printf("Unable load server certs: %s\n", err) - rootCancel() - } - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(caCertBytes) { - log.Println("Unable to load CA cert") - rootCancel() - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{srvCert}, - ClientAuth: tls.RequestClientCert, - ClientCAs: certPool, - } - creds := credentials.NewTLS(tlsConfig) - - grpcServer := grpc.NewServer( - grpc.Creds(creds), - ) - pb.RegisterFileServiceServer(grpcServer, grpcFileServer) - - // wait for cancel - go func() { - <-grpcCtx.Done() - grpcServer.GracefulStop() - }() - - log.Printf("Starting grpc server") - if err = grpcServer.Serve(lis); err != nil { - log.Printf("GRPC Shutdown with error: %s\n", err) - rootCancel() - } - log.Println("GRPC Shutdown") - grpcShutdownCancel() - }() - - httpCtx, httpCancel := context.WithCancel(rootCtx) - defer httpCancel() - - httpShutdownCtx, httpShutdownCancel := context.WithCancel(context.Background()) - defer httpShutdownCancel() - // Start http server - go func() { - httpAddr := ":8088" - if c.IsSet("http-addr") { - httpAddr = c.String("http-addr") - } - httpServer := server.NewHTTPSever(s) - httpServer.Addr = httpAddr - - // wait for cancel - go func() { - <-httpCtx.Done() - timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - httpServer.Shutdown(timeoutCtx) - }() - - log.Printf("Starting http server") - if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Printf("HTTP Server shutdown with error: %s\n", err) - rootCancel() - } - log.Println("HTTP Shutdown") - httpShutdownCancel() - }() - - <-grpcShutdownCtx.Done() - <-httpShutdownCtx.Done() - return nil -} - -func ActionClientGet(c *cli.Context) error { - addr := c.String("addr") - conn, err := grpc.DialContext(c.Context, addr, grpc.WithInsecure()) - if err != nil { - return err - } - defer conn.Close() - - client := pb.NewFileServiceClient(conn) - - for _, arg := range c.Args().Slice() { - req := &pb.GetFileRequest{Id: arg} - resp, err := client.GetFile(c.Context, req) - if err != nil { - return err - } - filename := resp.File.FileId - if resp.File.Metadata.OriginalFilename != "" { - filename = resp.File.Metadata.OriginalFilename - } - - f, err := os.Create(filename) - if err != nil { - return err - } - defer f.Close() - - if _, err := f.Write(resp.File.Data); err != nil { - return err - } - fmt.Printf("Wrote file '%s'\n", filename) - } - - return nil -} - -func ActionClientUpload(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() - - client := pb.NewFileServiceClient(conn) - - for _, arg := range c.Args().Slice() { - f, err := os.Open(arg) - if err != nil { - return err - } - - data, err := io.ReadAll(f) - if err != nil { - return err - } - - req := &pb.UploadFileRequest{Data: data, OriginalFilename: filepath.Base(arg)} - - resp, err := client.UploadFile(c.Context, req) - if err != nil { - return err - } - fmt.Printf("%s uploaded with id %s. Available at %s\n", arg, resp.Id, resp.FileUrl) - } - return nil -} - -func ActionClientList(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() - - client := pb.NewFileServiceClient(conn) - - resp, err := client.ListFiles(c.Context, &pb.ListFilesRequest{}) - if err != nil { - return err - } - - for _, elem := range resp.Files { - fmt.Println(elem.FileId) - } - return nil -} - -func ActionClientDelete(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() - - client := pb.NewFileServiceClient(conn) - for _, arg := range c.Args().Slice() { - _, err := client.DeleteFile(c.Context, &pb.DeleteFileRequest{Id: arg}) - if err != nil { - return fmt.Errorf("error deleting file: %w", err) - } - fmt.Printf("Deleted file %s\n", arg) - } - return nil -} - -func ActionGencerts(c *cli.Context) error { - outDir := "." - if c.IsSet("out-dir") { - outDir = c.String("out-dir") - } - if !c.IsSet("hostname") { - return fmt.Errorf("--hostname required") - } - hostname := c.String("hostname") - return certs.GenAllCerts(outDir, hostname) -} - -func ActionInitConfig(c *cli.Context) error { - defaultCfg := config.FromDefault() - return defaultCfg.ToDefaultFile() -} - -func getConfig(c *cli.Context) (*config.Config, error) { - if c.IsSet("config") { - cfgPath := c.String("config") - return config.FromFile(cfgPath) - } - cfg, err := config.FromDefaultLocations() - if err == nil { - verbosePrint(c, fmt.Sprintf("Config loaded from %s", cfg.Location())) - } - return cfg, err -} - -func verbosePrint(c *cli.Context, message string) { - if c.Bool("verbose") { - fmt.Println(message) - } -} diff --git a/config/config.go b/config/config.go index 9928924..3dc8035 100644 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,8 @@ import ( "google.golang.org/grpc/credentials" ) +var ErrNotFound = fmt.Errorf("config not found") + type Config struct { Server *ServerConfig `toml:"Server"` Client *ClientConfig `toml:"Client"` @@ -28,19 +30,26 @@ type CertificatePaths struct { } type ServerConfig struct { - LogLevel string `toml:"LogLevel"` - Hostname string `toml:"Hostname"` - StoreConfig *ServerStoreConfig `toml:"Store"` - GRPC *ServerGRPCConfig `toml:"GRPC"` - HTTP *ServerHTTPConfig `toml:"HTTP"` + LogLevel string `toml:"LogLevel"` + Hostname string `toml:"Hostname"` + GRPCEndpoint string `toml:"GRPCEndpoint"` + UserStoreConfig *ServerUserStoreConfig `toml:"UserStore"` + FileStoreConfig *ServerFileStoreConfig `toml:"FileStore"` + GRPC *ServerGRPCConfig `toml:"GRPC"` + HTTP *ServerHTTPConfig `toml:"HTTP"` } -type ServerStoreConfig struct { +type ServerFileStoreConfig struct { Type string `toml:"Type"` FSStoreConfig *FSStoreConfig `toml:"Filesystem"` BoltStoreConfig *BoltStoreConfig `toml:"Bolt"` } +type ServerUserStoreConfig struct { + Type string `toml:"Type"` + BoltStoreConfig *BoltStoreConfig `toml:"Bolt"` +} + type BoltStoreConfig struct { Path string `toml:"Path"` } @@ -78,7 +87,9 @@ func FromDefault() *Config { ListenAddr: ":8089", }, }, - Client: &ClientConfig{}, + Client: &ClientConfig{ + Certs: &CertificatePaths{}, + }, } return cfg @@ -97,6 +108,9 @@ func FromReader(r io.Reader) (*Config, error) { func FromFile(path string) (*Config, error) { f, err := os.Open(path) if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, ErrNotFound + } return nil, fmt.Errorf("unable to open config-file: %w", err) } defer f.Close() @@ -188,7 +202,7 @@ func (cc *ClientConfig) Creds() (credentials.TransportCredentials, error) { return credentials.NewTLS(config), nil } -func (c *Config) ToDefaultFile() error { +func CreateDefaultConfigDir() error { userConfigDir, err := os.UserConfigDir() if err != nil { return err @@ -207,8 +221,25 @@ func (c *Config) ToDefaultFile() error { return fmt.Errorf("config-directory is not a directory") } } + return nil +} +func DefaultConfigFilePath() (string, error) { + userConfigDir, err := os.UserConfigDir() + if err != nil { + return "", err + } + return filepath.Join(userConfigDir, "ezshare", "ezshare.toml"), nil - configFilePath := filepath.Join(configDirPath, "ezshare.toml") +} + +func (c *Config) ToDefaultFile() error { + if err := CreateDefaultConfigDir(); err != nil { + return err + } + configFilePath, err := DefaultConfigFilePath() + if err != nil { + return err + } _, err = os.Stat(configFilePath) if err != nil { if !errors.Is(err, fs.ErrNotExist) { @@ -226,7 +257,7 @@ func (c *Config) ToDefaultFile() error { return fmt.Errorf("config-file already exists") } -func (sc *ServerStoreConfig) GetStore() (store.FileStore, func() error, error) { +func (sc *ServerFileStoreConfig) GetStore() (store.FileStore, func() error, error) { nopCloseFunc := func() error { return nil } if strings.EqualFold(sc.Type, "bolt") { s, err := store.NewBoltStore(sc.BoltStoreConfig.Path) @@ -243,6 +274,39 @@ func (sc *ServerStoreConfig) GetStore() (store.FileStore, func() error, error) { if strings.EqualFold(sc.Type, "memory") { return store.NewMemoryStore(), nopCloseFunc, nil } + if strings.EqualFold(sc.Type, "bolt") { + s, err := store.NewBoltStore(sc.BoltStoreConfig.Path) + if err != nil { + return nil, nil, err + } + closeFunc := func() error { return s.Close() } + return s, closeFunc, nil + } + + return nil, nil, fmt.Errorf("invalid store config") +} + +func (sc *ServerUserStoreConfig) GetStore() (store.UserStore, func() error, error) { + nopCloseFunc := func() error { return nil } + if strings.EqualFold(sc.Type, "bolt") { + s, err := store.NewBoltStore(sc.BoltStoreConfig.Path) + if err != nil { + return nil, nil, err + } + return s, s.Close, err + + } + if strings.EqualFold(sc.Type, "memory") { + return store.NewMemoryStore(), nopCloseFunc, nil + } + if strings.EqualFold(sc.Type, "bolt") { + s, err := store.NewBoltStore(sc.BoltStoreConfig.Path) + if err != nil { + return nil, nil, err + } + closeFunc := func() error { return s.Close() } + return s, closeFunc, nil + } return nil, nil, fmt.Errorf("invalid store config") } diff --git a/ezshare.example.toml b/ezshare.example.toml index 0b9769d..584877d 100644 --- a/ezshare.example.toml +++ b/ezshare.example.toml @@ -12,23 +12,38 @@ LogLevel = "INFO" # Required Hostname = "localhost" -# Storage configuration -[Server.Store] +# Endpoint reachable by clients +# Fetched by clients for automatic setup +# Required +GRPCEndpoint = "localhost:50051" + +# File store configuration +[Server.FileStore] # How server stores file # Must be one of: filesystem, memory, bolt # Required -Type = "filesystem" +Type = "bolt" -[Server.Store.Bolt] +[Server.FileStore.Bolt] # Where the bolt-db is stored # Required if store-type is bolt -Path = "" +Path = "/data/files.db" -[Server.Store.Filesystem] +[Server.FileStore.Filesystem] # Where files are stored # Required if store-type is filesystem Dir = "/data" +[Server.UserStore] +# What store to use for users +# Must be one of: memory, bolt +# Required +Type = "bolt" + +[Server.UserStore.Bolt] +# Path to bolt database-file +Path = "/data/users.db" + # GRPC Configuration [Server.GRPC] # Address to listen to diff --git a/go.mod b/go.mod index 5312724..f9f86e0 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,13 @@ go 1.17 require ( github.com/go-chi/chi/v5 v5.0.7 + github.com/google/go-cmp v0.5.6 github.com/google/uuid v1.3.0 github.com/pelletier/go-toml v1.9.4 github.com/urfave/cli/v2 v2.3.0 go.etcd.io/bbolt v1.3.6 + golang.org/x/crypto v0.0.0-20211202192323-5770296d904e + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 google.golang.org/grpc v1.42.0 google.golang.org/protobuf v1.27.1 ) @@ -17,7 +20,8 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect golang.org/x/net v0.0.0-20211203184738-4852103109b8 // indirect - golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect + golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/text v0.3.7 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9 // indirect ) diff --git a/go.sum b/go.sum index ae37d31..082ef6c 100644 --- a/go.sum +++ b/go.sum @@ -49,8 +49,9 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -75,6 +76,8 @@ go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -87,6 +90,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211203184738-4852103109b8 h1:PFkPt/jI9Del3hmFplBtRp8tDhSRpFu7CyRs7VmEC0M= golang.org/x/net v0.0.0-20211203184738-4852103109b8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -104,9 +108,12 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..9b074fa --- /dev/null +++ b/main.go @@ -0,0 +1,142 @@ +package main + +import ( + "log" + "os" + + "gitea.benny.dog/torjus/ezshare/actions" + "github.com/urfave/cli/v2" +) + +const Version = "v0.1.1" + +func main() { + cli.VersionFlag = &cli.BoolFlag{Name: "version"} + app := cli.App{ + Name: "ezshare", + Version: Version, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Usage: "Path to config-file.", + }, + &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Be more verbose", + }, + }, + Commands: []*cli.Command{ + { + Name: "serve", + Usage: "Start ezshare server", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "no-grpc", + Usage: "Do not enable grpc.", + }, + &cli.BoolFlag{ + Name: "no-http", + Usage: "Do not enable http.", + }, + &cli.StringFlag{ + Name: "grpc-addr", + Usage: "Address to listen for grpc.", + }, + &cli.StringFlag{ + Name: "http-addr", + Usage: "Address to listen for http.", + }, + &cli.StringFlag{ + Name: "hostname", + Usage: "Hostname used in links", + }, + }, + Action: actions.ActionServe, + }, + { + Name: "client", + Usage: "Client commands", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "addr", + Usage: "Address of server.", + }, + }, + Subcommands: []*cli.Command{ + { + Name: "get", + Usage: "Get file with id", + ArgsUsage: "ID [ID]..", + Action: actions.ActionClientGet, + }, + { + Name: "upload", + Usage: "Upload file(s)", + ArgsUsage: "PATH [PATH]..", + Action: actions.ActionClientUpload, + }, + { + Name: "delete", + Usage: "Delete file with id", + ArgsUsage: "ID [ID]..", + Action: actions.ActionClientDelete, + }, + { + Name: "list", + Usage: "List files", + Action: actions.ActionClientList, + }, + { + Name: "login", + Usage: "Login and retrieve client cert", + ArgsUsage: "[PROTO://]HOSTNAME[:PORT]", + Action: actions.ActionClientLogin, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Usage: "Username used for login.", + }, + &cli.BoolFlag{ + Name: "overwrite", + Usage: "Overwrite existing config", + }, + }, + }, + { + Name: "config-init", + Usage: "Initialize default config", + Action: actions.ActionInitConfig, + }, + }, + }, + { + Name: "cert", + Usage: "Certificate commands", + Subcommands: []*cli.Command{ + { + Name: "gen-all", + Usage: "Generate CA, Server and Client certificates", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "out-dir", + Usage: "Directory where certificates will be stored.", + }, + &cli.StringFlag{ + Name: "hostname", + Usage: "Hostname used for server certificate.", + }, + }, + Action: actions.ActionGencerts, + }, + }, + }, + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Printf("Error: %s\n", err) + } + +} diff --git a/pb/ezshare.pb.go b/pb/ezshare.pb.go index 6b33c3a..28726be 100644 --- a/pb/ezshare.pb.go +++ b/pb/ezshare.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 +// protoc-gen-go v1.27.1 // protoc v3.19.1 // source: protos/ezshare.proto @@ -21,6 +21,105 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type User_Role int32 + +const ( + User_UNKNOWN User_Role = 0 + User_VIEWONLY User_Role = 1 + User_USER User_Role = 2 + User_ADMIN User_Role = 3 + User_UNAPPROVED User_Role = 4 +) + +// Enum value maps for User_Role. +var ( + User_Role_name = map[int32]string{ + 0: "UNKNOWN", + 1: "VIEWONLY", + 2: "USER", + 3: "ADMIN", + 4: "UNAPPROVED", + } + User_Role_value = map[string]int32{ + "UNKNOWN": 0, + "VIEWONLY": 1, + "USER": 2, + "ADMIN": 3, + "UNAPPROVED": 4, + } +) + +func (x User_Role) Enum() *User_Role { + p := new(User_Role) + *p = x + return p +} + +func (x User_Role) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (User_Role) Descriptor() protoreflect.EnumDescriptor { + return file_protos_ezshare_proto_enumTypes[0].Descriptor() +} + +func (User_Role) Type() protoreflect.EnumType { + return &file_protos_ezshare_proto_enumTypes[0] +} + +func (x User_Role) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use User_Role.Descriptor instead. +func (User_Role) EnumDescriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{10, 0} +} + +///////////////////// +// Common messages // +///////////////////// +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[0] + 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 Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{0} +} + +//////////////////////// +// FILE RELATED STUFF // +//////////////////////// type File struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -34,7 +133,7 @@ type File struct { func (x *File) Reset() { *x = File{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[0] + mi := &file_protos_ezshare_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -47,7 +146,7 @@ func (x *File) String() string { func (*File) ProtoMessage() {} func (x *File) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[0] + mi := &file_protos_ezshare_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -60,7 +159,7 @@ func (x *File) ProtoReflect() protoreflect.Message { // Deprecated: Use File.ProtoReflect.Descriptor instead. func (*File) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{0} + return file_protos_ezshare_proto_rawDescGZIP(), []int{1} } func (x *File) GetFileId() string { @@ -99,7 +198,7 @@ type UploadFileRequest struct { func (x *UploadFileRequest) Reset() { *x = UploadFileRequest{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[1] + mi := &file_protos_ezshare_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -112,7 +211,7 @@ func (x *UploadFileRequest) String() string { func (*UploadFileRequest) ProtoMessage() {} func (x *UploadFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[1] + mi := &file_protos_ezshare_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -125,7 +224,7 @@ func (x *UploadFileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadFileRequest.ProtoReflect.Descriptor instead. func (*UploadFileRequest) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{1} + return file_protos_ezshare_proto_rawDescGZIP(), []int{2} } func (x *UploadFileRequest) GetData() []byte { @@ -161,7 +260,7 @@ type UploadFileResponse struct { func (x *UploadFileResponse) Reset() { *x = UploadFileResponse{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[2] + mi := &file_protos_ezshare_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -174,7 +273,7 @@ func (x *UploadFileResponse) String() string { func (*UploadFileResponse) ProtoMessage() {} func (x *UploadFileResponse) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[2] + mi := &file_protos_ezshare_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -187,7 +286,7 @@ func (x *UploadFileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadFileResponse.ProtoReflect.Descriptor instead. func (*UploadFileResponse) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{2} + return file_protos_ezshare_proto_rawDescGZIP(), []int{3} } func (x *UploadFileResponse) GetId() string { @@ -216,7 +315,7 @@ type GetFileRequest struct { func (x *GetFileRequest) Reset() { *x = GetFileRequest{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[3] + mi := &file_protos_ezshare_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -229,7 +328,7 @@ func (x *GetFileRequest) String() string { func (*GetFileRequest) ProtoMessage() {} func (x *GetFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[3] + mi := &file_protos_ezshare_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -242,7 +341,7 @@ func (x *GetFileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFileRequest.ProtoReflect.Descriptor instead. func (*GetFileRequest) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{3} + return file_protos_ezshare_proto_rawDescGZIP(), []int{4} } func (x *GetFileRequest) GetId() string { @@ -263,7 +362,7 @@ type GetFileResponse struct { func (x *GetFileResponse) Reset() { *x = GetFileResponse{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[4] + mi := &file_protos_ezshare_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -276,7 +375,7 @@ func (x *GetFileResponse) String() string { func (*GetFileResponse) ProtoMessage() {} func (x *GetFileResponse) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[4] + mi := &file_protos_ezshare_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -289,7 +388,7 @@ func (x *GetFileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFileResponse.ProtoReflect.Descriptor instead. func (*GetFileResponse) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{4} + return file_protos_ezshare_proto_rawDescGZIP(), []int{5} } func (x *GetFileResponse) GetFile() *File { @@ -311,7 +410,7 @@ type DeleteFileRequest struct { func (x *DeleteFileRequest) Reset() { *x = DeleteFileRequest{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[5] + mi := &file_protos_ezshare_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -324,7 +423,7 @@ func (x *DeleteFileRequest) String() string { func (*DeleteFileRequest) ProtoMessage() {} func (x *DeleteFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[5] + mi := &file_protos_ezshare_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -337,7 +436,7 @@ func (x *DeleteFileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteFileRequest.ProtoReflect.Descriptor instead. func (*DeleteFileRequest) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{5} + return file_protos_ezshare_proto_rawDescGZIP(), []int{6} } func (x *DeleteFileRequest) GetId() string { @@ -356,7 +455,7 @@ type DeleteFileResponse struct { func (x *DeleteFileResponse) Reset() { *x = DeleteFileResponse{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[6] + mi := &file_protos_ezshare_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -369,7 +468,7 @@ func (x *DeleteFileResponse) String() string { func (*DeleteFileResponse) ProtoMessage() {} func (x *DeleteFileResponse) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[6] + mi := &file_protos_ezshare_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -382,7 +481,7 @@ func (x *DeleteFileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteFileResponse.ProtoReflect.Descriptor instead. func (*DeleteFileResponse) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{6} + return file_protos_ezshare_proto_rawDescGZIP(), []int{7} } // List @@ -395,7 +494,7 @@ type ListFilesRequest struct { func (x *ListFilesRequest) Reset() { *x = ListFilesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[7] + mi := &file_protos_ezshare_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -408,7 +507,7 @@ func (x *ListFilesRequest) String() string { func (*ListFilesRequest) ProtoMessage() {} func (x *ListFilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[7] + mi := &file_protos_ezshare_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -421,7 +520,7 @@ func (x *ListFilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListFilesRequest.ProtoReflect.Descriptor instead. func (*ListFilesRequest) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{7} + return file_protos_ezshare_proto_rawDescGZIP(), []int{8} } type ListFilesResponse struct { @@ -435,7 +534,7 @@ type ListFilesResponse struct { func (x *ListFilesResponse) Reset() { *x = ListFilesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[8] + mi := &file_protos_ezshare_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -448,7 +547,7 @@ func (x *ListFilesResponse) String() string { func (*ListFilesResponse) ProtoMessage() {} func (x *ListFilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[8] + mi := &file_protos_ezshare_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -461,7 +560,7 @@ func (x *ListFilesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListFilesResponse.ProtoReflect.Descriptor instead. func (*ListFilesResponse) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{8} + return file_protos_ezshare_proto_rawDescGZIP(), []int{9} } func (x *ListFilesResponse) GetFiles() []*ListFilesResponse_ListFileInfo { @@ -471,6 +570,444 @@ func (x *ListFilesResponse) GetFiles() []*ListFilesResponse_ListFileInfo { return nil } +//////////////////////// +// USER RELATED STUFF // +//////////////////////// +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + HashedPassword []byte `protobuf:"bytes,3,opt,name=hashed_password,json=hashedPassword,proto3" json:"hashed_password,omitempty"` + UserRole User_Role `protobuf:"varint,4,opt,name=user_role,json=userRole,proto3,enum=ezshare.User_Role" json:"user_role,omitempty"` + Active bool `protobuf:"varint,5,opt,name=active,proto3" json:"active,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[10] + 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 User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{10} +} + +func (x *User) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *User) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *User) GetHashedPassword() []byte { + if x != nil { + return x.HashedPassword + } + return nil +} + +func (x *User) GetUserRole() User_Role { + if x != nil { + return x.UserRole + } + return User_UNKNOWN +} + +func (x *User) GetActive() bool { + if x != nil { + return x.Active + } + return false +} + +// Register +type RegisterUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *RegisterUserRequest) Reset() { + *x = RegisterUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterUserRequest) ProtoMessage() {} + +func (x *RegisterUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[11] + 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 RegisterUserRequest.ProtoReflect.Descriptor instead. +func (*RegisterUserRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{11} +} + +func (x *RegisterUserRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *RegisterUserRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type RegisterUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` +} + +func (x *RegisterUserResponse) Reset() { + *x = RegisterUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterUserResponse) ProtoMessage() {} + +func (x *RegisterUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[12] + 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 RegisterUserResponse.ProtoReflect.Descriptor instead. +func (*RegisterUserResponse) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{12} +} + +func (x *RegisterUserResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *RegisterUserResponse) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +// Login +type LoginUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *LoginUserRequest) Reset() { + *x = LoginUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LoginUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginUserRequest) ProtoMessage() {} + +func (x *LoginUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[13] + 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 LoginUserRequest.ProtoReflect.Descriptor instead. +func (*LoginUserRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{13} +} + +func (x *LoginUserRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *LoginUserRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type LoginUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClientCert []byte `protobuf:"bytes,1,opt,name=client_cert,json=clientCert,proto3" json:"client_cert,omitempty"` + ClientKey []byte `protobuf:"bytes,2,opt,name=client_key,json=clientKey,proto3" json:"client_key,omitempty"` +} + +func (x *LoginUserResponse) Reset() { + *x = LoginUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LoginUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginUserResponse) ProtoMessage() {} + +func (x *LoginUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[14] + 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 LoginUserResponse.ProtoReflect.Descriptor instead. +func (*LoginUserResponse) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{14} +} + +func (x *LoginUserResponse) GetClientCert() []byte { + if x != nil { + return x.ClientCert + } + return nil +} + +func (x *LoginUserResponse) GetClientKey() []byte { + if x != nil { + return x.ClientKey + } + return nil +} + +// List +type ListUsersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListUsersRequest) Reset() { + *x = ListUsersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListUsersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListUsersRequest) ProtoMessage() {} + +func (x *ListUsersRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[15] + 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 ListUsersRequest.ProtoReflect.Descriptor instead. +func (*ListUsersRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{15} +} + +type ListUsersResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Users []*User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` +} + +func (x *ListUsersResponse) Reset() { + *x = ListUsersResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListUsersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListUsersResponse) ProtoMessage() {} + +func (x *ListUsersResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[16] + 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 ListUsersResponse.ProtoReflect.Descriptor instead. +func (*ListUsersResponse) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{16} +} + +func (x *ListUsersResponse) GetUsers() []*User { + if x != nil { + return x.Users + } + return nil +} + +// Approve +type ApproveUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (x *ApproveUserRequest) Reset() { + *x = ApproveUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApproveUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApproveUserRequest) ProtoMessage() {} + +func (x *ApproveUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[17] + 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 ApproveUserRequest.ProtoReflect.Descriptor instead. +func (*ApproveUserRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{17} +} + +func (x *ApproveUserRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + type File_Metadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -484,7 +1021,7 @@ type File_Metadata struct { func (x *File_Metadata) Reset() { *x = File_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[9] + mi := &file_protos_ezshare_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -497,7 +1034,7 @@ func (x *File_Metadata) String() string { func (*File_Metadata) ProtoMessage() {} func (x *File_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[9] + mi := &file_protos_ezshare_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -510,7 +1047,7 @@ func (x *File_Metadata) ProtoReflect() protoreflect.Message { // Deprecated: Use File_Metadata.ProtoReflect.Descriptor instead. func (*File_Metadata) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{0, 0} + return file_protos_ezshare_proto_rawDescGZIP(), []int{1, 0} } func (x *File_Metadata) GetUploadedOn() *timestamppb.Timestamp { @@ -546,7 +1083,7 @@ type ListFilesResponse_ListFileInfo struct { func (x *ListFilesResponse_ListFileInfo) Reset() { *x = ListFilesResponse_ListFileInfo{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[10] + mi := &file_protos_ezshare_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -559,7 +1096,7 @@ func (x *ListFilesResponse_ListFileInfo) String() string { func (*ListFilesResponse_ListFileInfo) ProtoMessage() {} func (x *ListFilesResponse_ListFileInfo) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[10] + mi := &file_protos_ezshare_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -572,7 +1109,7 @@ func (x *ListFilesResponse_ListFileInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ListFilesResponse_ListFileInfo.ProtoReflect.Descriptor instead. func (*ListFilesResponse_ListFileInfo) Descriptor() ([]byte, []int) { - return file_protos_ezshare_proto_rawDescGZIP(), []int{8, 0} + return file_protos_ezshare_proto_rawDescGZIP(), []int{9, 0} } func (x *ListFilesResponse_ListFileInfo) GetFileId() string { @@ -596,80 +1133,140 @@ var file_protos_ezshare_proto_rawDesc = []byte{ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x99, 0x02, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, - 0x72, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0xaf, 0x01, 0x0a, 0x08, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x4f, 0x6e, 0x12, + 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x99, 0x02, 0x0a, 0x04, 0x46, 0x69, + 0x6c, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x32, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x1a, 0xaf, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x4f, 0x6e, 0x12, 0x39, 0x0a, + 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x4f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x8f, 0x01, 0x0a, - 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x4f, 0x6e, 0x22, 0x3f, - 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x22, - 0x20, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x22, 0x34, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xaf, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x05, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 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, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x5b, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, - 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, - 0x6c, 0x65, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, - 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 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, - 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, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x0a, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x65, 0x73, 0x4f, 0x6e, 0x22, 0x3f, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x22, 0x20, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x34, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, + 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x7a, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, + 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0xaf, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 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, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x5b, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x32, 0x0a, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x22, 0xec, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, + 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0e, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, + 0x2f, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x46, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, + 0x08, 0x56, 0x49, 0x45, 0x57, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x55, + 0x53, 0x45, 0x52, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, + 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x4e, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x04, + 0x22, 0x4d, 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, + 0x3c, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x4a, 0x0a, + 0x10, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x53, 0x0a, 0x11, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x12, + 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x38, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, + 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, } var ( @@ -684,42 +1281,63 @@ func file_protos_ezshare_proto_rawDescGZIP() []byte { return file_protos_ezshare_proto_rawDescData } -var file_protos_ezshare_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +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_goTypes = []interface{}{ - (*File)(nil), // 0: ezshare.File - (*UploadFileRequest)(nil), // 1: ezshare.UploadFileRequest - (*UploadFileResponse)(nil), // 2: ezshare.UploadFileResponse - (*GetFileRequest)(nil), // 3: ezshare.GetFileRequest - (*GetFileResponse)(nil), // 4: ezshare.GetFileResponse - (*DeleteFileRequest)(nil), // 5: ezshare.DeleteFileRequest - (*DeleteFileResponse)(nil), // 6: ezshare.DeleteFileResponse - (*ListFilesRequest)(nil), // 7: ezshare.ListFilesRequest - (*ListFilesResponse)(nil), // 8: ezshare.ListFilesResponse - (*File_Metadata)(nil), // 9: ezshare.File.Metadata - (*ListFilesResponse_ListFileInfo)(nil), // 10: ezshare.ListFilesResponse.ListFileInfo - (*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp + (User_Role)(0), // 0: ezshare.User.Role + (*Empty)(nil), // 1: ezshare.Empty + (*File)(nil), // 2: ezshare.File + (*UploadFileRequest)(nil), // 3: ezshare.UploadFileRequest + (*UploadFileResponse)(nil), // 4: ezshare.UploadFileResponse + (*GetFileRequest)(nil), // 5: ezshare.GetFileRequest + (*GetFileResponse)(nil), // 6: ezshare.GetFileResponse + (*DeleteFileRequest)(nil), // 7: ezshare.DeleteFileRequest + (*DeleteFileResponse)(nil), // 8: ezshare.DeleteFileResponse + (*ListFilesRequest)(nil), // 9: ezshare.ListFilesRequest + (*ListFilesResponse)(nil), // 10: ezshare.ListFilesResponse + (*User)(nil), // 11: ezshare.User + (*RegisterUserRequest)(nil), // 12: ezshare.RegisterUserRequest + (*RegisterUserResponse)(nil), // 13: ezshare.RegisterUserResponse + (*LoginUserRequest)(nil), // 14: ezshare.LoginUserRequest + (*LoginUserResponse)(nil), // 15: ezshare.LoginUserResponse + (*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 } var file_protos_ezshare_proto_depIdxs = []int32{ - 9, // 0: ezshare.File.metadata:type_name -> ezshare.File.Metadata - 11, // 1: ezshare.UploadFileRequest.expires_on:type_name -> google.protobuf.Timestamp - 0, // 2: ezshare.GetFileResponse.file:type_name -> ezshare.File - 10, // 3: ezshare.ListFilesResponse.files:type_name -> ezshare.ListFilesResponse.ListFileInfo - 11, // 4: ezshare.File.Metadata.uploaded_on:type_name -> google.protobuf.Timestamp - 11, // 5: ezshare.File.Metadata.expires_on:type_name -> google.protobuf.Timestamp - 9, // 6: ezshare.ListFilesResponse.ListFileInfo.metadata:type_name -> ezshare.File.Metadata - 1, // 7: ezshare.FileService.UploadFile:input_type -> ezshare.UploadFileRequest - 3, // 8: ezshare.FileService.GetFile:input_type -> ezshare.GetFileRequest - 5, // 9: ezshare.FileService.DeleteFile:input_type -> ezshare.DeleteFileRequest - 7, // 10: ezshare.FileService.ListFiles:input_type -> ezshare.ListFilesRequest - 2, // 11: ezshare.FileService.UploadFile:output_type -> ezshare.UploadFileResponse - 4, // 12: ezshare.FileService.GetFile:output_type -> ezshare.GetFileResponse - 6, // 13: ezshare.FileService.DeleteFile:output_type -> ezshare.DeleteFileResponse - 8, // 14: ezshare.FileService.ListFiles:output_type -> ezshare.ListFilesResponse - 11, // [11:15] is the sub-list for method output_type - 7, // [7:11] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 19, // 0: ezshare.File.metadata:type_name -> ezshare.File.Metadata + 21, // 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 + 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 + 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 + 9, // 12: ezshare.FileService.ListFiles:input_type -> ezshare.ListFilesRequest + 12, // 13: ezshare.UserService.Register:input_type -> ezshare.RegisterUserRequest + 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 + 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 } func init() { file_protos_ezshare_proto_init() } @@ -729,7 +1347,7 @@ func file_protos_ezshare_proto_init() { } if !protoimpl.UnsafeEnabled { file_protos_ezshare_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*File); i { + switch v := v.(*Empty); i { case 0: return &v.state case 1: @@ -741,7 +1359,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadFileRequest); i { + switch v := v.(*File); i { case 0: return &v.state case 1: @@ -753,7 +1371,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadFileResponse); i { + switch v := v.(*UploadFileRequest); i { case 0: return &v.state case 1: @@ -765,7 +1383,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFileRequest); i { + switch v := v.(*UploadFileResponse); i { case 0: return &v.state case 1: @@ -777,7 +1395,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFileResponse); i { + switch v := v.(*GetFileRequest); i { case 0: return &v.state case 1: @@ -789,7 +1407,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteFileRequest); i { + switch v := v.(*GetFileResponse); i { case 0: return &v.state case 1: @@ -801,7 +1419,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteFileResponse); i { + switch v := v.(*DeleteFileRequest); i { case 0: return &v.state case 1: @@ -813,7 +1431,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListFilesRequest); i { + switch v := v.(*DeleteFileResponse); i { case 0: return &v.state case 1: @@ -825,7 +1443,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListFilesResponse); i { + switch v := v.(*ListFilesRequest); i { case 0: return &v.state case 1: @@ -837,7 +1455,7 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*File_Metadata); i { + switch v := v.(*ListFilesResponse); i { case 0: return &v.state case 1: @@ -849,6 +1467,114 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LoginUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LoginUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListUsersRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListUsersResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApproveUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[18].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[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListFilesResponse_ListFileInfo); i { case 0: return &v.state @@ -866,13 +1592,14 @@ func file_protos_ezshare_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_protos_ezshare_proto_rawDesc, - NumEnums: 0, - NumMessages: 11, + NumEnums: 1, + NumMessages: 20, NumExtensions: 0, - NumServices: 1, + NumServices: 2, }, GoTypes: file_protos_ezshare_proto_goTypes, DependencyIndexes: file_protos_ezshare_proto_depIdxs, + EnumInfos: file_protos_ezshare_proto_enumTypes, MessageInfos: file_protos_ezshare_proto_msgTypes, }.Build() File_protos_ezshare_proto = out.File diff --git a/pb/ezshare_grpc.pb.go b/pb/ezshare_grpc.pb.go index 6dad9ba..4133765 100644 --- a/pb/ezshare_grpc.pb.go +++ b/pb/ezshare_grpc.pb.go @@ -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", +} diff --git a/protos/ezshare.proto b/protos/ezshare.proto index 35de631..53d52d5 100644 --- a/protos/ezshare.proto +++ b/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,61 @@ service FileService { rpc GetFile(GetFileRequest) returns (GetFileResponse) {} rpc DeleteFile(DeleteFileRequest) returns (DeleteFileResponse) {} rpc ListFiles(ListFilesRequest) returns (ListFilesResponse) {} -} \ No newline at end of file +} + +//////////////////////// +// USER RELATED STUFF // +//////////////////////// +message User { + string id = 1; + string username = 2; + bytes hashed_password = 3; + enum Role { + UNKNOWN = 0; + VIEWONLY = 1; + USER = 2; + ADMIN = 3; + UNAPPROVED = 4; + } + 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 { + string username = 1; + string password = 2; +} +message LoginUserResponse { + bytes client_cert = 1; + bytes client_key = 2; +} + +// 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) {} +} diff --git a/server/grpc.go b/server/fileservice.go similarity index 100% rename from server/grpc.go rename to server/fileservice.go diff --git a/server/http.go b/server/http.go index d57c9f1..29eb47c 100644 --- a/server/http.go +++ b/server/http.go @@ -10,28 +10,49 @@ import ( ) type HTTPServer struct { - store store.FileStore + store store.FileStore + serverGRPCCert []byte + grpcEndpoint string http.Server } -func NewHTTPSever(store store.FileStore) *HTTPServer { +type MetadataResponse struct { + GRPCEndpoint string `json:"grpc_endpoint"` +} + +func NewHTTPSever(store store.FileStore, certBytes []byte, grpcEndpoint string) *HTTPServer { srv := &HTTPServer{ - store: store, + store: store, + serverGRPCCert: certBytes, + grpcEndpoint: grpcEndpoint, } r := chi.NewRouter() + r.Get("/server.pem", srv.ServerCertHandler) + r.Get("/metadata", srv.MetadataHandler) r.Get("/files/{id}", srv.FileHandler) srv.Handler = r return srv } +func (s *HTTPServer) ServerCertHandler(w http.ResponseWriter, r *http.Request) { + w.Write(s.serverGRPCCert) +} +func (s *HTTPServer) MetadataHandler(w http.ResponseWriter, r *http.Request) { + md := &MetadataResponse{ + GRPCEndpoint: s.grpcEndpoint, + } + encoder := json.NewEncoder(w) + encoder.Encode(md) +} + func (s *HTTPServer) FileHandler(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") f, err := s.store.GetFile(id) if err != nil { - if err == store.ErrNoSuchFile { + if err == store.ErrNoSuchItem { WriteErrorResponse(w, http.StatusNotFound, "file not found") return } diff --git a/server/interceptors/auth.go b/server/interceptors/auth.go new file mode 100644 index 0000000..848f1bd --- /dev/null +++ b/server/interceptors/auth.go @@ -0,0 +1,55 @@ +package interceptors + +import ( + "context" + "fmt" + + "gitea.benny.dog/torjus/ezshare/pb" + "gitea.benny.dog/torjus/ezshare/store" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/peer" +) + +type ContextKey string + +var ContextKeyRole ContextKey = "role" + +func NewAuthInterceptor(s store.UserStore) grpc.UnaryServerInterceptor { + 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] + + id := cert.Subject.CommonName + + user, err := s.GetUser(id) + if err == nil { + newCtx := context.WithValue(ctx, ContextKeyRole, user.UserRole) + return handler(newCtx, req) + } + } + } + } + + newCtx := context.WithValue(ctx, ContextKeyRole, pb.User_UNKNOWN) + + return handler(newCtx, req) + } +} + +func RoleFromContext(ctx context.Context) pb.User_Role { + value := ctx.Value(ContextKeyRole) + if value == nil { + return pb.User_UNKNOWN + } + role, ok := value.(pb.User_Role) + if ok { + return role + } + return pb.User_UNKNOWN +} diff --git a/server/userservice.go b/server/userservice.go new file mode 100644 index 0000000..a75d438 --- /dev/null +++ b/server/userservice.go @@ -0,0 +1,86 @@ +package server + +import ( + "context" + "fmt" + + "gitea.benny.dog/torjus/ezshare/certs" + "gitea.benny.dog/torjus/ezshare/pb" + "gitea.benny.dog/torjus/ezshare/store" + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type GRPCUserServiceServer struct { + store store.UserStore + certService *certs.CertService + pb.UnimplementedUserServiceServer +} + +func NewGRPCUserServiceServer(store store.UserStore, certSvc *certs.CertService) *GRPCUserServiceServer { + return &GRPCUserServiceServer{store: store, certService: certSvc} +} +func (s *GRPCUserServiceServer) Register(ctx context.Context, req *pb.RegisterUserRequest) (*pb.RegisterUserResponse, error) { + // Check if user already exists + if _, err := s.store.GetUserByUsername(req.Username); err != store.ErrNoSuchItem { + return nil, status.Error(codes.AlreadyExists, "user already exists") + } + + pw, err := hashPassword(req.Password) + if err != nil { + return nil, fmt.Errorf("unable to hash password: %w", err) + } + user := &pb.User{ + Id: uuid.Must(uuid.NewRandom()).String(), + Username: req.Username, + HashedPassword: pw, + UserRole: pb.User_USER, + Active: true, + } + + if err := s.store.StoreUser(user); err != nil { + return nil, status.Error(codes.Internal, fmt.Sprintf("unable to store user: %s", err)) + } + + return &pb.RegisterUserResponse{Id: user.Id, Token: ""}, nil +} + +func (s *GRPCUserServiceServer) Login(_ context.Context, req *pb.LoginUserRequest) (*pb.LoginUserResponse, error) { + user, err := s.store.GetUserByUsername(req.Username) + if err != nil { + if err == store.ErrNoSuchItem { + return nil, status.Error(codes.NotFound, "no such user") + } + return nil, status.Error(codes.Internal, "error getting user from store") + } + + if err := bcrypt.CompareHashAndPassword(user.HashedPassword, []byte(req.Password)); err != nil { + return nil, status.Error(codes.Unauthenticated, "wrong username and or password") + } + + cert, key, err := s.certService.NewClient(user.Id) + if err != nil { + return nil, status.Error(codes.Internal, "unable to generate client certificate") + } + + resp := &pb.LoginUserResponse{ + ClientCert: cert, + ClientKey: key, + } + + return resp, nil +} + +func (s *GRPCUserServiceServer) List(_ context.Context, _ *pb.ListUsersRequest) (*pb.ListUsersResponse, error) { + return nil, status.Error(codes.Unimplemented, "not yet implemented") +} + +func (s *GRPCUserServiceServer) Approve(_ context.Context, _ *pb.ApproveUserRequest) (*pb.Empty, error) { + return nil, status.Error(codes.Unimplemented, "not yet implemented") +} + +func hashPassword(password string) ([]byte, error) { + return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) +} diff --git a/store/bolt.go b/store/bolt.go index d1be574..e1f1b75 100644 --- a/store/bolt.go +++ b/store/bolt.go @@ -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 { @@ -62,7 +65,7 @@ func (s *BoltStore) GetFile(id string) (*pb.File, error) { return nil, fmt.Errorf("error getting file: %w", err) } if data == nil { - return nil, ErrNoSuchFile + return nil, ErrNoSuchItem } err := proto.Unmarshal(data, &file) @@ -95,7 +98,7 @@ func (s *BoltStore) DeleteFile(id string) error { bkt := t.Bucket(bktKey) data := bkt.Get([]byte(id)) if data == nil { - return ErrNoSuchFile + return ErrNoSuchItem } return bkt.Delete([]byte(id)) }) @@ -137,7 +140,7 @@ func (s *BoltStore) GetCertificate(id string) (*x509.Certificate, error) { return nil, err } if raw == nil { - return nil, ErrNoSuchFile + return nil, ErrNoSuchItem } cert, err := x509.ParseCertificate(raw) @@ -188,14 +191,83 @@ 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 } + +var _ UserStore = &BoltStore{} + +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 +} + +func (s *BoltStore) GetUserByUsername(username string) (*pb.User, error) { + var user pb.User + err := s.db.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket(bktKeyUsers) + c := bkt.Cursor() + + for k, v := c.First(); k != nil; c.Next() { + err := proto.Unmarshal(v, &user) + if err != nil { + // TODO: Log that db has invalid user + continue + } + if user.Username == username { + return nil + } + } + return nil + }) + if err != nil { + return nil, err + } + + if user.Username == username { + return &user, nil + } + return nil, ErrNoSuchItem +} diff --git a/store/bolt_test.go b/store/bolt_test.go index 5c32414..b969bce 100644 --- a/store/bolt_test.go +++ b/store/bolt_test.go @@ -1,3 +1,4 @@ +//nolint:staticcheck package store_test import ( @@ -10,19 +11,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) } diff --git a/store/filesystem.go b/store/filesystem.go index 6401207..0953adc 100644 --- a/store/filesystem.go +++ b/store/filesystem.go @@ -27,7 +27,7 @@ func (s *FileSystemStore) GetFile(id string) (*pb.File, error) { f, err := os.Open(path) if err != nil { if os.IsNotExist(os.ErrNotExist) { - return nil, ErrNoSuchFile + return nil, ErrNoSuchItem } return nil, fmt.Errorf("unable to open file: %w", err) } diff --git a/store/memory.go b/store/memory.go index f544885..4d24976 100644 --- a/store/memory.go +++ b/store/memory.go @@ -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() @@ -37,7 +43,7 @@ func (s *MemoryStore) GetFile(id string) (*pb.File, error) { return file, nil } - return nil, ErrNoSuchFile + return nil, ErrNoSuchItem } func (s *MemoryStore) StoreFile(file *pb.File) (string, error) { @@ -56,7 +62,7 @@ func (s *MemoryStore) DeleteFile(id string) error { s.filesLock.Lock() defer s.filesLock.Unlock() if _, ok := s.files[id]; !ok { - return ErrNoSuchFile + return ErrNoSuchItem } delete(s.files, id) @@ -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() @@ -83,7 +95,7 @@ func (s *MemoryStore) GetCertificate(id string) (*x509.Certificate, error) { data, ok := s.certs[id] if !ok { // TODO: Make separate error, or rename error - return nil, ErrNoSuchFile + return nil, ErrNoSuchItem } return x509.ParseCertificate(data) @@ -106,7 +118,7 @@ func (s *MemoryStore) GetKey(id string) (*ecdsa.PrivateKey, error) { defer s.keyLock.RUnlock() data, ok := s.keys[id] if !ok { - return nil, ErrNoSuchFile + return nil, ErrNoSuchItem } return x509.ParseECPrivateKey(data) @@ -134,3 +146,50 @@ 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, ErrNoSuchItem + } + 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 +} + +func (s *MemoryStore) GetUserByUsername(username string) (*pb.User, error) { + s.usersLock.RLock() + defer s.usersLock.RUnlock() + for _, user := range s.users { + if user.Username == username { + return user, nil + } + } + return nil, ErrNoSuchItem +} diff --git a/store/memory_test.go b/store/memory_test.go index 4e742ae..287051c 100644 --- a/store/memory_test.go +++ b/store/memory_test.go @@ -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) +} diff --git a/store/store.go b/store/store.go index f771ada..e5687b5 100644 --- a/store/store.go +++ b/store/store.go @@ -8,7 +8,7 @@ import ( "gitea.benny.dog/torjus/ezshare/pb" ) -var ErrNoSuchFile = fmt.Errorf("no such file") +var ErrNoSuchItem = fmt.Errorf("no such item") type FileStore interface { GetFile(id string) (*pb.File, error) @@ -24,3 +24,10 @@ 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) + GetUserByUsername(username string) (*pb.User, error) + ListUsers() ([]string, error) +} diff --git a/store/store_test.go b/store/store_test.go index bf138d2..6c3e99a 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -10,6 +10,10 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "google.golang.org/protobuf/testing/protocmp" + "gitea.benny.dog/torjus/ezshare/pb" "gitea.benny.dog/torjus/ezshare/store" "google.golang.org/protobuf/types/known/timestamppb" @@ -64,7 +68,7 @@ func doFileStoreTest(s store.FileStore, t *testing.T) { t.Fatalf("Unable to delete file: %s", err) } - if _, err := s.GetFile(id); err != store.ErrNoSuchFile { + if _, err := s.GetFile(id); err != store.ErrNoSuchItem { t.Fatalf("Getting deleted file returned wrong error: %s", err) } }) @@ -140,3 +144,48 @@ 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 diff := cmp.Diff(user, retrieved, protocmp.Transform()); diff != "" { + t.Errorf("User retrieved by name difference:\n%v", diff) + } + + named, err := s.GetUserByUsername(user.Username) + if err != nil { + t.Fatalf("Retrieving user by username returned error: %s", err) + } + + if diff := cmp.Diff(user, named, protocmp.Transform()); diff != "" { + t.Errorf("User retrieved by name difference:\n%v", diff) + } + + 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") + } + }) +} diff --git a/version.go b/version.go deleted file mode 100644 index 99d250a..0000000 --- a/version.go +++ /dev/null @@ -1,3 +0,0 @@ -package ezshare - -const Version = "v0.1.1"