internal/control/web.go
85 lines · 2625 bytes
1package control
2
3import (
4 "errors"
5 "fmt"
6 "io"
7 "time"
8
9 "gitbay.org/gitbay/internal/protocol"
10 "gitbay.org/gitbay/internal/store"
11)
12
13func newStoredToken() (token, hash string, err error) { return store.NewToken() }
14
15func init() {
16 register(Command{Path: []string{"web", "login"},
17 Summary: "mint a one-time browser login URL",
18 Usage: "web login", Run: runWebLogin})
19 register(Command{Path: []string{"web", "sessions", "list"},
20 Summary: "list your browser sessions",
21 Usage: "web sessions list", ReadOnly: true, Run: runWebSessionsList})
22 register(Command{Path: []string{"web", "sessions", "revoke"},
23 Summary: "end a browser session, or all of them",
24 Usage: "web sessions revoke <id>|--all", Run: runWebSessionsRevoke})
25}
26
27func runWebSessionsList(c *Ctx, args []string) int {
28 if len(args) != 0 {
29 return c.usage()
30 }
31 sessions, err := c.Store.ListWebSessions(c.User.ID)
32 if err != nil {
33 return c.fail(protocol.ExitFailure, "%v", err)
34 }
35 return c.emit(sessions, func(w io.Writer) {
36 for _, s := range sessions {
37 fmt.Fprintf(w, "%s\tsince %s\tuntil %s\n", s.ID, s.CreatedAt, s.ExpiresAt)
38 }
39 })
40}
41
42func runWebSessionsRevoke(c *Ctx, args []string) int {
43 if len(args) != 1 {
44 return c.usage()
45 }
46 if args[0] == "--all" {
47 n, err := c.Store.RevokeAllWebSessions(c.User.ID)
48 if err != nil {
49 return c.fail(protocol.ExitFailure, "%v", err)
50 }
51 return c.emit(map[string]any{"revoked": n}, func(w io.Writer) {
52 fmt.Fprintf(w, "revoked %d browser sessions\n", n)
53 })
54 }
55 if err := c.Store.RevokeWebSession(c.User.ID, args[0]); err != nil {
56 if errors.Is(err, store.ErrNotFound) {
57 return c.fail(protocol.ExitNotFound, "no browser session %s on your account", args[0])
58 }
59 return c.fail(protocol.ExitFailure, "%v", err)
60 }
61 return c.emit(map[string]any{"revoked": args[0]}, func(w io.Writer) {
62 fmt.Fprintf(w, "revoked browser session %s\n", args[0])
63 })
64}
65
66func runWebLogin(c *Ctx, args []string) int {
67 if len(args) != 0 {
68 return c.usage()
69 }
70 if c.Cfg.Web.Mode != "accounts" {
71 return c.fail(protocol.ExitDenied,
72 "this instance runs the web in view-only mode (web.mode = %q); there is nothing to log in to", c.Cfg.Web.Mode)
73 }
74 token, hash, err := newStoredToken()
75 if err != nil {
76 return c.fail(protocol.ExitFailure, "%v", err)
77 }
78 if err := c.Store.CreateLoginToken(c.User.ID, hash, 5*time.Minute); err != nil {
79 return c.fail(protocol.ExitFailure, "%v", err)
80 }
81 url := c.Cfg.Server.SiteURL + "/login?token=" + token
82 return c.emit(map[string]string{"url": url, "expires_in": "5m"}, func(w io.Writer) {
83 fmt.Fprintf(w, "open within 5 minutes (single use):\n%s\n", url)
84 })
85}