diff --git a/dclish/dclish.go b/dclish/dclish.go index c0cb79be4e8269919e00afcb85e4cec0228a45e9..9f2096ef37fac3bb2e59b30a4f1cca1dc657a326 100644 --- a/dclish/dclish.go +++ b/dclish/dclish.go @@ -14,7 +14,8 @@ type ActionFunc func(*Command) error // a given command. type CompleterFunc func() []string -// Flag is a flag for a command. +// Flag is a flag for a command. In the future setting a type would make +// things a little easier. type Flag struct { OptArg bool Value string @@ -26,7 +27,9 @@ type Flag struct { // Flags is the list of flags. type Flags map[string]*Flag -// Command contains the definition of a command, it's flags and subcommands. +// Command contains the definition of a command, its flags, subcommands +// and a completer function for the arguments. The number of args can +// be limited. type Command struct { Flags Flags Args []string @@ -194,7 +197,6 @@ func (c Commands) run(words []string) error { } return cmd.Action(cmd) } - // TODO: need to clean this up. args := words[1:] for i := range args { if strings.HasPrefix(args[i], "/") { diff --git a/repl/accounts.go b/repl/accounts.go index 6d6abeb8a84465e6bfc0312de272d9024575ae0f..09f0b73a7e7a22a5bddedf1539d5fb75e360adf9 100644 --- a/repl/accounts.go +++ b/repl/accounts.go @@ -58,7 +58,6 @@ func ActionUserList(_ *dclish.Command) error { fmt.Printf("ERROR: Failed to list users (%s).\n", err) return nil } - // TODO: nicer output for user. for _, u := range userlist { fmt.Printf("%s\n", u) } diff --git a/repl/command.go b/repl/command.go index ecfff20d048f2db9c7935c748514d2ccbdb5a89e..150894217ea57eabe1dee204a556d119607c2f0a 100644 --- a/repl/command.go +++ b/repl/command.go @@ -1130,19 +1130,29 @@ characteristics of the BULLETIN Utility. The following options are available: ACCESS ALWAYS BRIEF DEFAULT_EXPIRE - EXPIRE_LIMIT FOLDER NOALWAYS NOBRIEF - NOPROMPT_EXPIRE NOREADNEW NOSHOWNEW NOSYSTEM - PROMPT_EXPIRE READNEW SHOWNEW SYSTEM + EXPIRE_LIMIT FOLDER NOACCESS NOALWAYS + NOBRIEF NOPROMPT_EXPIRE NOREADNEW NOSHOWNEW + NOSYSTEM PROMPT_EXPIRE READNEW SHOWNEW + SYSTEM `, Action: ActionSet, Commands: dclish.Commands{ + "NOACCESS": { + Description: `This removes access for users. + + Format: + SET NOACCESS id-name [folder-name]`, + MinArgs: 1, + MaxArgs: 2, + Action: ActionSetNoaccess, + }, "ACCESS": { Description: `Controls access to a private folder. A private folder can only be selected by users who have been granted access. Only the owner of that folder is allowed to grant access. Format: - SET [NO]ACCESS id-name [folder-name] + SET ACCESS id-name [folder-name] The id-name can be one or more ids from the system Rights Database for which access is being modified. It can also be a file name which @@ -1169,7 +1179,9 @@ messages, and thus will not be able to set any login flags. (NOTE: If such a user selects such a folder and then uses SET ACCESS to grant him or herself access, the user must reselect the folder in order for the new access to take affect in order to be able to set login flags.)`, - Action: ActionSetAccess, + MinArgs: 0, + MaxArgs: 2, + Action: ActionSetAccess, Flags: dclish.Flags{ "/ALL": { Description: ` Specifies that access to the folder is granted to all users. If /READ diff --git a/repl/set.go b/repl/set.go index dc094688498f800d932b6cada4598376fe43a1ea..249452d674ae376a24fe325e8d4d0c80a0c6ff34 100644 --- a/repl/set.go +++ b/repl/set.go @@ -60,9 +60,92 @@ func ActionSet(cmd *dclish.Command) error { return nil } +// ActionSetNoaccess handles the `SET ACCESS` command. +func ActionSetNoaccess(cmd *dclish.Command) error { + ctx := storage.Context() + login := cmd.Args[0] + folder := this.Folder + if len(cmd.Args) == 2 { + folder = folders.FindFolder(cmd.Args[1]) + if folder.Name == "" { + return errors.New("Folder not found") + } + } + if this.User.Admin == 0 || folder.Owner != this.User.Login { + return errors.New("Must be an admin or folder owner") + } + this.Q.DeleteFolderAccess(ctx, + storage.DeleteFolderAccessParams{ + Login: login, + Folder: folder.Name, + }) + return nil +} + // ActionSetAccess handles the `SET ACCESS` command. -func ActionSetAccess(_ *dclish.Command) error { - fmt.Println("TODO: implement ActionSetAccess.") +func ActionSetAccess(cmd *dclish.Command) error { + ctx := storage.Context() + optAll := cmd.Flags["/ALL"].Set + if optAll { + if cmd.Flags["/ALL"].Value != "true" { + return errors.New("Flag '/NOALL' not recognised") + } + } + optRead := cmd.Flags["/READ"].Set + if optRead { + if cmd.Flags["/READ"].Value != "true" { + return errors.New("Flag '/READ' not recognised") + } + } + if optAll { + if len(cmd.Args) > 1 { + return errors.New("Too many arguments for /ALL") + } + folder := this.Folder + if len(cmd.Args) == 1 { + folder = folders.FindFolder(cmd.Args[0]) + if folder.Name == "" { + return errors.New("Folder not found") + } + } + if this.User.Admin == 0 || folder.Owner != this.User.Login { + return errors.New("Must be an admin or folder owner") + } + visibility := int64(2) + if optRead { + visibility = 1 + } + this.Q.UpdateFolderVisibility(ctx, + storage.UpdateFolderVisibilityParams{ + Visibility: visibility, + Name: folder.Name, + }) + return nil + } + if len(cmd.Args) == 0 { + return errors.New("Must supply a user login to set access") + } + login := cmd.Args[0] + folder := this.Folder + if len(cmd.Args) == 2 { + folder = folders.FindFolder(cmd.Args[1]) + if folder.Name == "" { + return errors.New("Folder not found") + } + } + if this.User.Admin == 0 || folder.Owner != this.User.Login { + return errors.New("Must be an admin or folder owner") + } + visibility := int64(2) + if optRead { + visibility = 1 + } + this.Q.UpdateFolderAccess(ctx, + storage.UpdateFolderAccessParams{ + Login: login, + Folder: folder.Name, + Visibility: visibility, + }) return nil } @@ -167,7 +250,6 @@ func ActionSetSystem(_ *dclish.Command) error { if this.User.Admin == 0 { return errors.New("You are not an admin") } - // TODO: parse flags and args. ctx := storage.Context() this.Q.UpdateFolderSystem(ctx, storage.UpdateFolderSystemParams{ System: 1, @@ -181,7 +263,10 @@ func ActionSetNosystem(_ *dclish.Command) error { if this.User.Admin == 0 { return errors.New("You are not an admin") } - // TODO: parse flags and args. + if this.Folder.Name == "GENERAL" { + fmt.Println("Can't remove SYSTEM from the GENERAL folder.") + return nil + } ctx := storage.Context() this.Q.UpdateFolderSystem(ctx, storage.UpdateFolderSystemParams{ System: 1, diff --git a/repl/show.go b/repl/show.go index d4a6b00aedede0bd6f40cf410c6a043a0c991e28..e3852ee0ced4e831f4fb53c06941ef946c7d7636 100644 --- a/repl/show.go +++ b/repl/show.go @@ -35,7 +35,8 @@ func ActionShowFlags(_ *dclish.Command) error { return nil } -// ActionShowFolder handles the `SHOW FOLDER` command. +// ActionShowFolder handles the `SHOW FOLDER` command. This is based on +// `SHOW_FOLDER` in bulletin5.for. func ActionShowFolder(cmd *dclish.Command) error { folder := this.Folder if len(cmd.Args) == 1 { @@ -75,7 +76,6 @@ func ActionShowFolder(cmd *dclish.Command) error { if folder.System != 0 { fmt.Println(" SYSTEM has been set.") } - // TODO: Review SHOW_FOLDER in bulletin5.for. if folder.Always != 0 { fmt.Println(" ALWAYS has been set.") } diff --git a/storage/migrations/1_create_table.up.sql b/storage/migrations/1_create_table.up.sql index e81b14847c4291f4f1a2a05330cd71f046e8ef97..7798aadd59917d3a440deeebcd6891fbe9b03fd5 100644 --- a/storage/migrations/1_create_table.up.sql +++ b/storage/migrations/1_create_table.up.sql @@ -146,6 +146,8 @@ CREATE TABLE folder_access ( ON DELETE CASCADE ON UPDATE CASCADE NOT NULL, folder VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE NOT NULL, + --- read-only=1, read-write=2 + visibility INT DEFAULT 1 NOT NULL, PRIMARY KEY (login, folder) ) WITHOUT ROWID; diff --git a/storage/models.go b/storage/models.go index fd2c0cddcdf5ec49d091aeec0a36c4a81b03708a..54058ee2f639a0b75497866d71a77c609aee6cd7 100644 --- a/storage/models.go +++ b/storage/models.go @@ -29,8 +29,9 @@ type Folder struct { } type FolderAccess struct { - Login string - Folder string + Login string + Folder string + Visibility int64 } type FolderConfig struct { diff --git a/storage/queries/standard.sql b/storage/queries/standard.sql index 2b475b6b2706cee72497ae76499b065a31630cf0..34ef417b515bec542bcf41a65555c3c7485a317e 100644 --- a/storage/queries/standard.sql +++ b/storage/queries/standard.sql @@ -55,14 +55,16 @@ SELECT * FROM marks WHERE folder = ? AND login = ? AND msgid = ?; -- name: DeleteMark :exec DELETE FROM marks WHERE folder = ? AND login = ? AND msgid = ?; --- name: AddFolderAccess :exec -INSERT INTO folder_access (login, folder) VALUES (?, ?); +-- name: UpdateFolderAccess :exec +INSERT INTO folder_access (login, folder, visibility) VALUES (?1, ?2, ?3) + ON CONFLICT(login, folder) DO UPDATE + SET visibility = ?3; -- name: ListFolderAccess :many -SELECT * FROM folder_access; +SELECT * FROM folder_access WHERE folder = ?; -- name: GetFolderAccess :one -SELECT * FROM folder_access WHERE login = ? AND folder = ?; +SELECT visibility FROM folder_access WHERE login = ? AND folder = ?; -- name: DeleteFolderAccess :exec DELETE FROM folder_access WHERE login = ? AND folder = ?; diff --git a/storage/standard.sql.go b/storage/standard.sql.go index 47ad5d41b7fa94c3ae4566384091d7fe3539c55c..f26c1e2e621c1c04a7507bd1518341ed442578a1 100644 --- a/storage/standard.sql.go +++ b/storage/standard.sql.go @@ -18,20 +18,6 @@ func (q *Queries) AddFolder(ctx context.Context, name string) error { return err } -const addFolderAccess = `-- name: AddFolderAccess :exec -INSERT INTO folder_access (login, folder) VALUES (?, ?) -` - -type AddFolderAccessParams struct { - Login string - Folder string -} - -func (q *Queries) AddFolderAccess(ctx context.Context, arg AddFolderAccessParams) error { - _, err := q.db.ExecContext(ctx, addFolderAccess, arg.Login, arg.Folder) - return err -} - const addFolderConfig = `-- name: AddFolderConfig :exec INSERT INTO folder_configs (login, folder) VALUES (?, ?) ` @@ -203,7 +189,7 @@ func (q *Queries) GetFolder(ctx context.Context, name string) (Folder, error) { } const getFolderAccess = `-- name: GetFolderAccess :one -SELECT login, folder FROM folder_access WHERE login = ? AND folder = ? +SELECT visibility FROM folder_access WHERE login = ? AND folder = ? ` type GetFolderAccessParams struct { @@ -211,11 +197,11 @@ type GetFolderAccessParams struct { Folder string } -func (q *Queries) GetFolderAccess(ctx context.Context, arg GetFolderAccessParams) (FolderAccess, error) { +func (q *Queries) GetFolderAccess(ctx context.Context, arg GetFolderAccessParams) (int64, error) { row := q.db.QueryRowContext(ctx, getFolderAccess, arg.Login, arg.Folder) - var i FolderAccess - err := row.Scan(&i.Login, &i.Folder) - return i, err + var visibility int64 + err := row.Scan(&visibility) + return visibility, err } const getFolderConfig = `-- name: GetFolderConfig :one @@ -318,11 +304,11 @@ func (q *Queries) GetSystem(ctx context.Context) (System, error) { } const listFolderAccess = `-- name: ListFolderAccess :many -SELECT login, folder FROM folder_access +SELECT login, folder, visibility FROM folder_access WHERE folder = ? ` -func (q *Queries) ListFolderAccess(ctx context.Context) ([]FolderAccess, error) { - rows, err := q.db.QueryContext(ctx, listFolderAccess) +func (q *Queries) ListFolderAccess(ctx context.Context, folder string) ([]FolderAccess, error) { + rows, err := q.db.QueryContext(ctx, listFolderAccess, folder) if err != nil { return nil, err } @@ -330,7 +316,7 @@ func (q *Queries) ListFolderAccess(ctx context.Context) ([]FolderAccess, error) var items []FolderAccess for rows.Next() { var i FolderAccess - if err := rows.Scan(&i.Login, &i.Folder); err != nil { + if err := rows.Scan(&i.Login, &i.Folder, &i.Visibility); err != nil { return nil, err } items = append(items, i) @@ -600,3 +586,20 @@ func (q *Queries) SetSystem(ctx context.Context, arg SetSystemParams) error { _, err := q.db.ExecContext(ctx, setSystem, arg.Name, arg.DefaultExpire, arg.ExpireLimit) return err } + +const updateFolderAccess = `-- name: UpdateFolderAccess :exec +INSERT INTO folder_access (login, folder, visibility) VALUES (?1, ?2, ?3) + ON CONFLICT(login, folder) DO UPDATE + SET visibility = ?3 +` + +type UpdateFolderAccessParams struct { + Login string + Folder string + Visibility int64 +} + +func (q *Queries) UpdateFolderAccess(ctx context.Context, arg UpdateFolderAccessParams) error { + _, err := q.db.ExecContext(ctx, updateFolderAccess, arg.Login, arg.Folder, arg.Visibility) + return err +}