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
|
package tvm
import (
"fmt"
)
// CheckedUserTicket is short-lived user credential.
//
// CheckedUserTicket contains only valid users.
// Details: https://wiki.yandex-team.ru/passport/tvm2/user-ticket/#chtoestvusertickete
type CheckedUserTicket struct {
// DefaultUID is default user - maybe 0
DefaultUID UID
// UIDs is array of valid users - never empty
UIDs []UID
// Env is blackbox environment which created this UserTicket - provides only tvmauth now
Env BlackboxEnv
// Scopes is array of scopes inherited from credential - never empty
Scopes []string
// DbgInfo is human readable data for debug purposes
DbgInfo string
// LogInfo is safe for logging part of ticket - it can be parsed later with `tvmknife parse_ticket -t ...`
LogInfo string
//LoginID of a user, can be empty if ticket does not contain LoginID
LoginID string
//UIDs of users in ticket with extended fields
UidsExtFieldsMap map[UID]UserExtFields
//Default user in ticket with extended fields, can be nil if there is no default uid in ticket
DefaultUIDExtFields *UserExtFields
}
func (t CheckedUserTicket) String() string {
return fmt.Sprintf("%s (%s)", t.LogInfo, t.DbgInfo)
}
// CheckScopes verify that ALL needed scopes presents in the user ticket
func (t *CheckedUserTicket) CheckScopes(scopes ...string) error {
switch {
case len(scopes) == 0:
// ok, no scopes. no checks. no rules
return nil
case len(t.Scopes) == 0:
msg := fmt.Sprintf("user ticket doesn't contain expected scopes: %s (actual: nil)", scopes)
return &TicketError{Status: TicketInvalidScopes, Msg: msg}
default:
actualScopes := make(map[string]struct{}, len(t.Scopes))
for _, s := range t.Scopes {
actualScopes[s] = struct{}{}
}
for _, s := range scopes {
if _, found := actualScopes[s]; !found {
// exit on first nonexistent scope
msg := fmt.Sprintf(
"user ticket doesn't contain one of expected scopes: %s (actual: %s)",
scopes, t.Scopes,
)
return &TicketError{Status: TicketInvalidScopes, Msg: msg}
}
}
return nil
}
}
// CheckScopesAny verify that ANY of needed scopes presents in the user ticket
func (t *CheckedUserTicket) CheckScopesAny(scopes ...string) error {
switch {
case len(scopes) == 0:
// ok, no scopes. no checks. no rules
return nil
case len(t.Scopes) == 0:
msg := fmt.Sprintf("user ticket doesn't contain any of expected scopes: %s (actual: nil)", scopes)
return &TicketError{Status: TicketInvalidScopes, Msg: msg}
default:
actualScopes := make(map[string]struct{}, len(t.Scopes))
for _, s := range t.Scopes {
actualScopes[s] = struct{}{}
}
for _, s := range scopes {
if _, found := actualScopes[s]; found {
// exit on first valid scope
return nil
}
}
msg := fmt.Sprintf(
"user ticket doesn't contain any of expected scopes: %s (actual: %s)",
scopes, t.Scopes,
)
return &TicketError{Status: TicketInvalidScopes, Msg: msg}
}
}
type CheckUserTicketOptions struct {
EnvOverride *BlackboxEnv
}
type CheckUserTicketOption func(*CheckUserTicketOptions)
func WithBlackboxOverride(env BlackboxEnv) CheckUserTicketOption {
return func(opts *CheckUserTicketOptions) {
opts.EnvOverride = &env
}
}
type UserTicketACL func(ticket *CheckedUserTicket) error
func AllowAllUserTickets() UserTicketACL {
return func(ticket *CheckedUserTicket) error {
return nil
}
}
func CheckAllUserTicketScopesPresent(scopes []string) UserTicketACL {
return func(ticket *CheckedUserTicket) error {
return ticket.CheckScopes(scopes...)
}
}
func CheckAnyUserTicketScopesPresent(scopes []string) UserTicketACL {
return func(ticket *CheckedUserTicket) error {
return ticket.CheckScopesAny(scopes...)
}
}
|