2020-10-18 14:34:36 +02:00
|
|
|
// 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 <organization> 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 <COPYRIGHT HOLDER> 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.
|
|
|
|
|
2020-10-18 10:33:29 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
|
|
|
|
uuid "github.com/satori/go.uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
type CmdShare struct {
|
2020-10-18 15:19:16 +02:00
|
|
|
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."`
|
2020-10-18 10:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2020-10-18 15:15:32 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2020-10-18 10:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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: %v", 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: %v", 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: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := app.shareStore.RemoveShare(cmd.UUID); err != nil {
|
|
|
|
return fmt.Errorf("cannot remove share: %v", 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."`
|
2020-10-18 15:19:16 +02:00
|
|
|
LoginName string `arg:"" name:"loginname" help:"Name of the login. Must be unique."`
|
2020-10-18 10:33:29 +02:00
|
|
|
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)
|
|
|
|
}
|
2020-10-18 15:19:16 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|