This repository has been archived by the owner on Dec 26, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
/
lock.go
135 lines (124 loc) · 3.39 KB
/
lock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package workspace
import (
"github.com/leg100/otf/internal"
"github.com/leg100/otf/internal/http/html/paths"
"github.com/leg100/otf/internal/rbac"
)
const (
UserLock LockKind = iota
RunLock
)
type (
// Lock is a workspace Lock, which blocks runs from running and prevents state from being
// uploaded.
//
// https://developer.hashicorp.com/terraform/cloud-docs/workspaces/settings#locking
Lock struct {
id string // ID of entity holding lock
LockKind // kind of entity holding lock
}
// kind of entity holding a lock
LockKind int
LockButton struct {
State string // locked or unlocked
Text string // button text
Tooltip string // button tooltip
Disabled bool // button greyed out or not
Message string // message accompanying button
Action string // form URL
}
)
// Locked determines whether workspace is locked.
func (ws *Workspace) Locked() bool {
// a nil receiver means the lock is unlocked
return ws.Lock != nil
}
// Enlock locks the workspace
func (ws *Workspace) Enlock(id string, kind LockKind) error {
if ws.Lock == nil {
ws.Lock = &Lock{
id: id,
LockKind: kind,
}
return nil
}
// a run can replace another run holding a lock
if kind == RunLock {
ws.Lock.id = id
return nil
}
return ErrWorkspaceAlreadyLocked
}
// Unlock the workspace.
func (ws *Workspace) Unlock(id string, kind LockKind, force bool) error {
if ws.Lock == nil {
return ErrWorkspaceAlreadyUnlocked
}
if force {
ws.Lock = nil
return nil
}
// user can unlock their own lock
if ws.Lock.LockKind == UserLock && kind == UserLock && ws.Lock.id == id {
ws.Lock = nil
return nil
}
// run can unlock its own lock
if ws.Lock.LockKind == RunLock && kind == RunLock && ws.Lock.id == id {
ws.Lock = nil
return nil
}
// determine error message to return
if ws.Lock.LockKind == RunLock {
return ErrWorkspaceLockedByRun
}
return ErrWorkspaceLockedByDifferentUser
}
// lockButtonHelper helps the UI determine the button to display for
// locking/unlocking the workspace.
func lockButtonHelper(ws *Workspace, policy internal.WorkspacePolicy, user internal.Subject) LockButton {
var btn LockButton
if ws.Locked() {
btn.State = "locked"
btn.Text = "Unlock"
btn.Action = paths.UnlockWorkspace(ws.ID)
// A user needs at least the unlock permission
if !user.CanAccessWorkspace(rbac.UnlockWorkspaceAction, policy) {
btn.Tooltip = "insufficient permissions"
btn.Disabled = true
return btn
}
// Determine message to show
switch ws.Lock.LockKind {
case UserLock, RunLock:
btn.Message = "locked by: " + ws.Lock.id
default:
btn.Message = "locked by unknown entity: " + ws.Lock.id
}
// also show message as button tooltip
btn.Tooltip = btn.Message
// A user can unlock their own lock
if ws.Lock.LockKind == UserLock && ws.Lock.id == user.String() {
return btn
}
// User is going to need the force unlock permission
if user.CanAccessWorkspace(rbac.ForceUnlockWorkspaceAction, policy) {
btn.Text = "Force unlock"
btn.Action = paths.ForceUnlockWorkspace(ws.ID)
return btn
}
// User cannot unlock
btn.Disabled = true
return btn
} else {
btn.State = "unlocked"
btn.Text = "Lock"
btn.Action = paths.LockWorkspace(ws.ID)
// User needs at least the lock permission
if !user.CanAccessWorkspace(rbac.LockWorkspaceAction, policy) {
btn.Disabled = true
btn.Tooltip = "insufficient permissions"
}
return btn
}
}