// Copyright (c) 2020, Andreas Schneider // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package main import ( "fmt" "os" "path" uuid "github.com/satori/go.uuid" ) type CmdShare struct { CmdList CmdShareList `cmd:"" name:"list" help:"List all shares."` CmdCreate CmdShareCreate `cmd:"" name:"create" help:"Create a new share."` CmdDelete CmdShareDelete `cmd:"" name:"delete" help:"Delete a share."` CmdAddUser CmdShareAddUser `cmd:"" name:"add-user" help:"Add user to share."` CmdAddLogin CmdShareAddLogin `cmd:"" name:"add-login" help:"Add login to share."` CmdRemoveUser CmdShareRemoveUser `cmd:"" name:"remove-user" help:"Remove user from share."` CmdRemoveLogin CmdShareRemoveLogin `cmd:"" name:"remove-login" help:"Remove login from share."` } type CmdShareList struct{} func (cmd *CmdShareList) Run(app *app) error { shares, err := app.shareStore.GetShares() if err != nil { return err } for _, share := range shares { fmt.Printf("* %s (%s)\n", share.UUID.String(), share.Name) shareUsers, err := app.shareStore.GetShareUsers(share) if err != nil { fmt.Printf(" !! Users cannot be listed: %v\n", err) continue } for _, shareUser := range shareUsers { fmt.Printf(" * User: %s (%s)\n", shareUser.Username, shareUser.Role) shareLogins, err := app.shareStore.GetShareLogins(share, shareUser.Username) if err != nil { fmt.Printf(" !! Logins cannot be listed: %v\n", err) continue } for _, shareLogin := range shareLogins { info := "" if shareLogin.ReadOnly { info += " (readonly)" } fmt.Printf(" * Login: %s%s\n", shareLogin.LoginName, info) } } } return nil } type CmdShareCreate struct { Name string `name:"name" help:"Name of the share." required:""` } func (cmd *CmdShareCreate) Run(app *app) error { share, err := app.shareStore.CreateShare() if err != nil { return err } share.Name = cmd.Name if err := app.shareStore.UpdateShareAttributes(share); err != nil { // Best effort cleanup. _ = app.shareStore.RemoveShare(share.UUID) return fmt.Errorf("cannot set share attributes: %w", err) } if err := os.MkdirAll(path.Join(app.DataDirectory, share.UUID.String()), 0750); err != nil { // Best effort cleanup. _ = app.shareStore.RemoveShare(share.UUID) return fmt.Errorf("cannot create data dir: %w", err) } fmt.Printf("Share created: %s\n", share.UUID.String()) return nil } type ShareIdentifier struct { UUID uuid.UUID `arg:"" name:"id" help:"ID of the share to be deleted." required:""` } type CmdShareDelete struct { ShareIdentifier } func (cmd *CmdShareDelete) Run(app *app) error { if err := os.RemoveAll(path.Join(app.DataDirectory, cmd.UUID.String())); err != nil { return fmt.Errorf("cannot remove data directory: %w", err) } if err := app.shareStore.RemoveShare(cmd.UUID); err != nil { return fmt.Errorf("cannot remove share: %w", err) } return nil } type CmdShareAddUser struct { ShareIdentifier Username string `arg:"" name:"username" help:"Username of the user to add."` Role ShareRole `name:"role" help:"Role of the user (reader/writer/admin)" default:"writer"` } func (cmd *CmdShareAddUser) Run(app *app) error { if _, err := app.userStore.GetUser(cmd.Username); err != nil { return err } share := Share{UUID: cmd.UUID} return app.shareStore.AddUserToShare(share, cmd.Username, cmd.Role) } type CmdShareAddLogin struct { ShareIdentifier Username string `arg:"" name:"username" help:"Username of the user to add the login for."` LoginName string `arg:"" name:"loginname" help:"Name of the login. Must be unique."` ReadOnly bool `name:"readonly" help:"If set, the login can only read."` PasswordParam } func (cmd *CmdShareAddLogin) Run(app *app) error { if _, err := app.userStore.GetUser(cmd.Username); err != nil { return err } password, err := cmd.acquirePassword() if err != nil { return fmt.Errorf("cannot acquire password for login: %w", err) } share := Share{UUID: cmd.UUID} login := Login{ LoginName: cmd.LoginName, Password: password, ReadOnly: cmd.ReadOnly, } return app.shareStore.AddLogin(share, cmd.Username, login) } type CmdShareRemoveUser struct { ShareIdentifier Username string `arg:"" name:"username" help:"Username of the user to remove."` } func (cmd *CmdShareRemoveUser) Run(app *app) error { share := Share{UUID: cmd.UUID} return app.shareStore.RemoveUserFromShare(share, cmd.Username) } type CmdShareRemoveLogin struct { ShareIdentifier Username string `arg:"" name:"username" help:"Username of the user to remove the login for."` LoginName string `arg:"" name:"loginname" help:"Name of the login to remove."` } func (cmd *CmdShareRemoveLogin) Run(app *app) error { share := Share{UUID: cmd.UUID} return app.shareStore.RemoveLogin(share, cmd.Username, cmd.LoginName) }