aboutsummaryrefslogtreecommitdiffstats
path: root/library/go/yandex/tvm/user_ticket.go
blob: d745c9e508bc52917731307db32dfec03ea52277 (plain) (blame)
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...)
	}
}