1
6 package authremote
7
8 import (
9 "context"
10 "fmt"
11 apb "golang.conradwood.net/apis/auth"
12 ge "golang.conradwood.net/apis/goeasyops"
13 "golang.conradwood.net/go-easyops/auth"
14 "golang.conradwood.net/go-easyops/cache"
15 "golang.conradwood.net/go-easyops/client"
16 "golang.conradwood.net/go-easyops/cmdline"
17 "golang.conradwood.net/go-easyops/common"
18 "golang.conradwood.net/go-easyops/ctx"
19 "golang.conradwood.net/go-easyops/tokens"
20 "sync"
21 "time"
22 )
23
24 var (
25 userbyemailcache = cache.NewResolvingCache("userbyemail", time.Duration(60)*time.Second, 9999)
26 userbytokencache = cache.NewResolvingCache("userbytoken", time.Duration(60)*time.Second, 9999)
27 authServer apb.AuthenticationServiceClient
28 authServerLock sync.Mutex
29 authManager apb.AuthManagerServiceClient
30 authManagerLock sync.Mutex
31 contextRetrieved = false
32 lastUser *apb.SignedUser
33 lastService *apb.SignedUser
34 )
35
36 func init() {
37 common.AddRegistryChangeReceiver(registry_changed)
38 }
39 func registry_changed() {
40 authManager = nil
41 authServer = nil
42 }
43 func Context() context.Context {
44 client.GetSignatureFromAuth()
45 return ContextWithTimeout(cmdline.DefaultTimeout())
46 }
47
48
52 func NewContextWithRouting(kv map[string]string, fallback bool) context.Context {
53 return DerivedContextWithRouting(Context(), kv, fallback)
54 }
55
56
60 func DerivedContextWithRouting(cv context.Context, kv map[string]string, fallback bool) context.Context {
61 if cv == nil {
62 panic("cannot derive context from nil context")
63 }
64 cri := &ge.CTXRoutingTags{Tags: kv, FallbackToPlain: fallback}
65
66 if cmdline.ContextWithBuilder() {
67 _, s := GetLocalUsers()
68 if s == nil {
69 s = auth.GetSignedService(cv)
70 }
71 cb := ctx.NewContextBuilder()
72 cb.WithUser(auth.GetSignedUser(cv))
73 cb.WithCreatorService(s)
74 cb.WithCallingService(s)
75 cb.WithRoutingTags(cri)
76
77 cb.WithParentContext(cv)
78 nctx := cb.ContextWithAutoCancel()
79 if auth.GetSignedService(nctx) == nil && s != nil {
80 fmt.Printf("[go-easyops] context: %s\n", ctx.Context2String(nctx))
81 fmt.Printf("[go-easyops] Localstate: %#v\n", ctx.GetLocalState(nctx))
82 fmt.Printf("[go-easyops] WARNING derived context (v=%d) includes no service, but should\n", cmdline.GetContextBuilderVersion())
83
84 }
85 if nctx == nil {
86 panic("no context")
87 }
88 return nctx
89
90 }
91 panic("deprecated codepath")
92 }
93
94
97 func NewContextWithRoutingTags(rt *ge.CTXRoutingTags) context.Context {
98 return ContextWithTimeoutAndTags(cmdline.DefaultTimeout(), rt)
99 }
100
101
109 func ContextWithTimeout(t time.Duration) context.Context {
110 return ContextWithTimeoutAndTags(t, nil)
111 }
112
113
114 func GetLocalUsers() (*apb.SignedUser, *apb.SignedUser) {
115 client.GetSignatureFromAuth()
116 if cmdline.DebugAuth() {
117 fmt.Printf("[go-easyops] debugauth, contextretrieved=%v, localuser=%s,gotsig=%v\n", contextRetrieved, auth.SignedDescription(lastUser), client.GotSig())
118 }
119 if !client.GotSig() {
120 if cmdline.DebugAuth() {
121 fmt.Printf("[go-easyops] debugauth no local users, we do not yet have a signature\n")
122 }
123 return nil, nil
124 }
125 if !contextRetrieved {
126 utok := tokens.GetUserTokenParameter()
127
128 lastUser = SignedGetByToken(context_background(), utok)
129 lastService = SignedGetByToken(context_background(), tokens.GetServiceTokenParameter())
130 lu := common.VerifySignedUser(lastUser)
131 if lastUser != nil && lu == nil {
132 fmt.Printf("[go-easyops] Warning - local user signature invalid\n")
133 return nil, nil
134 }
135 if lu != nil {
136 if lu.ServiceAccount {
137 fmt.Printf("[go-easyops] Error - local user resolved to a service account\n")
138 panic("invalid user configuration")
139 }
140 }
141 if lastService != nil && common.VerifySignedUser(lastService) == nil {
142 fmt.Printf("[go-easyops] Warning - local service signature invalid\n")
143 return nil, nil
144 }
145 contextRetrieved = true
146 }
147 return lastUser, lastService
148 }
149
150
153 func ContextWithTimeoutAndTags(t time.Duration, rt *ge.CTXRoutingTags) context.Context {
154 if cmdline.IsStandalone() {
155 return standalone_ContextWithTimeoutAndTags(t, rt)
156 }
157 sctx := cmdline.GetEnvContext()
158 if sctx != "" {
159 if ctx.IsSerialisedByBuilder([]byte(sctx)) {
160 ctx, err := ctx.DeserialiseContextWithTimeout(t, []byte(sctx))
161 if err != nil {
162 fmt.Printf("[go-easyops] weird context GE_CTX (%s)\n", err)
163 } else {
164 return ctx
165 }
166
167 }
168
169 res, err := auth.RecreateContextWithTimeout(t, []byte(sctx))
170 if err == nil {
171 return res
172 } else {
173 fmt.Printf("[go-easyops] invalid context in environment variable GE_CTX\n")
174 }
175 }
176 if cmdline.ContextWithBuilder() {
177 u, s := GetLocalUsers()
178 cb := ctx.NewContextBuilder()
179 cb.WithUser(u)
180 cb.WithCreatorService(s)
181 cb.WithCallingService(s)
182 cb.WithRoutingTags(rt)
183 cb.WithTimeout(t)
184 return cb.ContextWithAutoCancel()
185 }
186
187 panic("[go-easyops] DEPRECATED CONTEXT creation!\n")
188 }
189
190 func GetAuthManagerClient() apb.AuthManagerServiceClient {
191 managerClient()
192 return authManager
193 }
194
195
196 func GetAuthenticationServiceClient() apb.AuthenticationServiceClient {
197 return GetAuthClient()
198 }
199
200
201 func GetAuthenticationService() apb.AuthenticationServiceClient {
202 return GetAuthClient()
203 }
204
205 func GetAuthClient() apb.AuthenticationServiceClient {
206 authClient()
207 return authServer
208 }
209
210
211
212
213 func ContextForUser(user *apb.User) (context.Context, error) {
214 return ContextForUserWithTimeout(user, 0)
215 }
216 func ContextForUserWithTimeout(user *apb.User, secs uint64) (context.Context, error) {
217 if user == nil {
218 return nil, fmt.Errorf("Missing user")
219 }
220
221 if cmdline.ContextWithBuilder() {
222 su, err := GetSignedUserByID(Context(), user.ID)
223 if err != nil {
224 return nil, err
225 }
226 cb := ctx.NewContextBuilder()
227 cb.WithTimeout(time.Duration(secs) * time.Second)
228 cb.WithUser(su)
229 _, svc := GetLocalUsers()
230 cb.WithCreatorService(svc)
231 cb.WithCallingService(svc)
232 return cb.ContextWithAutoCancel(), nil
233 }
234 panic("obsolete codepath")
235 }
236
237
238
239
240 func ContextForUserID(userid string) (context.Context, error) {
241 return ContextForUserIDWithTimeout(userid, 0)
242 }
243 func ContextForUserIDWithTimeout(userid string, to time.Duration) (context.Context, error) {
244 if userid == "" || userid == "0" {
245 return nil, fmt.Errorf("Missing userid")
246 }
247 if cmdline.ContextWithBuilder() {
248 su, err := GetSignedUserByID(Context(), userid)
249 if err != nil {
250 return nil, err
251 }
252 cb := ctx.NewContextBuilder()
253 cb.WithTimeout(to)
254 cb.WithUser(su)
255 _, svc := GetLocalUsers()
256 cb.WithCreatorService(svc)
257 cb.WithCallingService(svc)
258 return cb.ContextWithAutoCancel(), nil
259 }
260 panic("obsolete codepath")
261
262 }
263 func GetUserByID(ctx context.Context, userid string) (*apb.User, error) {
264 if userid == "" {
265 return nil, fmt.Errorf("[go-easyops] No userid provided")
266 }
267 return usercache_GetUserByID(ctx, userid)
268 }
269
270 func GetSignedUserByID(ctx context.Context, userid string) (*apb.SignedUser, error) {
271 if userid == "" {
272 return nil, fmt.Errorf("[go-easyops] No userid provided")
273 }
274 return usercache_GetSignedUserByID(ctx, userid)
275 }
276
277 func GetUserByEmail(ctx context.Context, email string) (*apb.User, error) {
278 if email == "" {
279 return nil, fmt.Errorf("[go-easyops] No email provided")
280 }
281 o, err := userbyemailcache.Retrieve(email, func(k string) (interface{}, error) {
282 managerClient()
283 res, err := authManager.GetUserByEmail(ctx, &apb.ByEmailRequest{Email: k})
284 return res, err
285 })
286 if err != nil {
287 return nil, err
288 }
289 return o.(*apb.User), nil
290 }
291 func WhoAmI() *apb.User {
292 tok := tokens.GetUserTokenParameter()
293 return GetByToken(context_background(), tok)
294 }
295 func GetByToken(ctx context.Context, token string) *apb.User {
296 if token == "" {
297 return nil
298 }
299 authClient()
300 ar, err := authServer.GetByToken(ctx, &apb.AuthenticateTokenRequest{Token: token})
301 if err != nil {
302 return nil
303 }
304 if !ar.Valid {
305 return nil
306 }
307 if !ar.User.Active {
308 return nil
309 }
310 return ar.User
311 }
312 func SignedGetByEmail(ctx context.Context, email string) *apb.SignedUser {
313 if email == "" {
314 return nil
315 }
316 managerClient()
317 req := &apb.ByEmailRequest{Email: email}
318 su, err := authManager.SignedGetUserByEmail(ctx, req)
319 if err != nil {
320 return nil
321 }
322 u := common.VerifySignedUser(su)
323 if u == nil {
324 return nil
325 }
326 if !u.Active {
327 return nil
328 }
329 return su
330 }
331 func SignedGetByToken(ctx context.Context, token string) *apb.SignedUser {
332 if token == "" {
333
334 return nil
335 }
336 su, err := userbytokencache.Retrieve(token, func(k string) (interface{}, error) {
337 authClient()
338 if cmdline.DebugAuth() {
339 fmt.Printf("[go-easyops] getting user for token \"%s\"...\n", k[:5])
340 }
341 ar, err := authServer.SignedGetByToken(ctx, &apb.AuthenticateTokenRequest{Token: k})
342 if err != nil {
343 if cmdline.DebugAuth() {
344 fmt.Printf("[go-easyops] getting user for token \"%s\" failed: %s\n", k, err)
345 }
346 return nil, err
347 }
348 if !ar.Valid {
349 if cmdline.DebugAuth() {
350 fmt.Printf("[go-easyops] getting user for token \"%s\" invalid", k[:5])
351 }
352 return nil, fmt.Errorf("user not valid")
353 }
354 u := common.VerifySignedUser(ar.User)
355 if !u.Active {
356 if cmdline.DebugAuth() {
357 fmt.Printf("[go-easyops] getting user for token \"%s\" inactive", k[:5])
358 }
359 return nil, fmt.Errorf("user not active")
360 }
361 if cmdline.DebugAuth() {
362 fmt.Printf("[go-easyops] getting user for token \"%s\" resulted in \"%s\"", k[:5], ar.User)
363 }
364 return ar.User, nil
365 })
366 if err != nil {
367 return nil
368 }
369 if su == nil {
370 return nil
371 }
372 return su.(*apb.SignedUser)
373 }
374
375 func authClient() {
376 if authServer == nil {
377 authServerLock.Lock()
378 defer authServerLock.Unlock()
379 if authServer != nil {
380 return
381 }
382 authServer = apb.NewAuthenticationServiceClient(client.Connect("auth.AuthenticationService"))
383 }
384 }
385 func managerClient() {
386 if authManager == nil {
387 authManagerLock.Lock()
388 defer authManagerLock.Unlock()
389 if authManager != nil {
390 return
391 }
392 authManager = apb.NewAuthManagerServiceClient(client.Connect("auth.AuthManagerService"))
393 }
394 }
395
396 func context_background() context.Context {
397 cb := ctx.NewContextBuilder()
398 return cb.ContextWithAutoCancel()
399 }
400
View as plain text