| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package auth | |
| 6 | |
| 7 import ( | |
| 8 "fmt" | |
| 9 "net/http" | |
| 10 | |
| 11 "golang.org/x/net/context" | |
| 12 | |
| 13 "github.com/luci/luci-go/common/errors" | |
| 14 "github.com/luci/luci-go/common/logging" | |
| 15 | |
| 16 "github.com/luci/luci-go/server/auth/identity" | |
| 17 "github.com/luci/luci-go/server/router" | |
| 18 ) | |
| 19 | |
| 20 type authenticatorKey int | |
| 21 | |
| 22 // SetAuthenticator injects copy of Authenticator (list of auth methods) into | |
| 23 // the context to use by default in LoginURL, LogoutURL and Authenticate. | |
| 24 // Usually installed into the context by some base middleware. | |
| 25 func SetAuthenticator(c context.Context, a Authenticator) context.Context { | |
| 26 return context.WithValue(c, authenticatorKey(0), append(Authenticator(ni
l), a...)) | |
| 27 } | |
| 28 | |
| 29 // Use is a middleware that simply puts given Authenticator into the context. | |
| 30 func Use(a Authenticator) router.Middleware { | |
| 31 return func(c *router.Context, next router.Handler) { | |
| 32 c.Context = SetAuthenticator(c.Context, a) | |
| 33 next(c) | |
| 34 } | |
| 35 } | |
| 36 | |
| 37 // getAuthenticator extracts instance of Authenticator (list of auth methods) | |
| 38 // from the context. Returns nil if no authenticator is set. | |
| 39 func getAuthenticator(c context.Context) Authenticator { | |
| 40 if a, ok := c.Value(authenticatorKey(0)).(Authenticator); ok { | |
| 41 return a | |
| 42 } | |
| 43 return nil | |
| 44 } | |
| 45 | |
| 46 // LoginURL returns a URL that, when visited, prompts the user to sign in, | |
| 47 // then redirects the user to the URL specified by dest. It is wrapper around | |
| 48 // LoginURL method of Authenticator in the context. | |
| 49 func LoginURL(c context.Context, dest string) (string, error) { | |
| 50 return getAuthenticator(c).LoginURL(c, dest) | |
| 51 } | |
| 52 | |
| 53 // LogoutURL returns a URL that, when visited, signs the user out, | |
| 54 // then redirects the user to the URL specified by dest. It is wrapper around | |
| 55 // LogoutURL method of Authenticator in the context. | |
| 56 func LogoutURL(c context.Context, dest string) (string, error) { | |
| 57 return getAuthenticator(c).LogoutURL(c, dest) | |
| 58 } | |
| 59 | |
| 60 // Authenticate is a middleware that performs authentication (using Authenticato
r | |
| 61 // in the context) and calls next handler. | |
| 62 func Authenticate(c *router.Context, next router.Handler) { | |
| 63 a := getAuthenticator(c.Context) | |
| 64 if a == nil { | |
| 65 replyError(c.Context, c.Writer, 500, "Authentication middleware
is not configured") | |
| 66 return | |
| 67 } | |
| 68 ctx, err := a.Authenticate(c.Context, c.Request) | |
| 69 switch { | |
| 70 case errors.IsTransient(err): | |
| 71 replyError(c.Context, c.Writer, 500, fmt.Sprintf("Transient erro
r during authentication - %s", err)) | |
| 72 case err != nil: | |
| 73 replyError(c.Context, c.Writer, 401, fmt.Sprintf("Authentication
error - %s", err)) | |
| 74 default: | |
| 75 c.Context = ctx | |
| 76 next(c) | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 // Autologin is a middleware that redirects the user to login page if the user | |
| 81 // is not signed in yet or authentication methods do not recognize user | |
| 82 // credentials. Uses Authenticator instance in the context. | |
| 83 func Autologin(c *router.Context, next router.Handler) { | |
| 84 a := getAuthenticator(c.Context) | |
| 85 if a == nil { | |
| 86 replyError(c.Context, c.Writer, 500, "Authentication middleware
is not configured") | |
| 87 return | |
| 88 } | |
| 89 ctx, err := a.Authenticate(c.Context, c.Request) | |
| 90 | |
| 91 switch { | |
| 92 case errors.IsTransient(err): | |
| 93 replyError(c.Context, c.Writer, 500, fmt.Sprintf("Transient erro
r during authentication - %s", err)) | |
| 94 | |
| 95 case err != nil: | |
| 96 replyError(c.Context, c.Writer, 401, fmt.Sprintf("Authentication
error - %s", err)) | |
| 97 | |
| 98 case CurrentIdentity(ctx).Kind() == identity.Anonymous: | |
| 99 dest := c.Request.RequestURI | |
| 100 if dest == "" { | |
| 101 // Make r.URL relative. | |
| 102 destURL := *c.Request.URL | |
| 103 destURL.Host = "" | |
| 104 destURL.Scheme = "" | |
| 105 dest = destURL.String() | |
| 106 } | |
| 107 url, err := a.LoginURL(c.Context, dest) | |
| 108 if err != nil { | |
| 109 if errors.IsTransient(err) { | |
| 110 replyError(c.Context, c.Writer, 500, fmt.Sprintf
("Transient error during authentication - %s", err)) | |
| 111 } else { | |
| 112 replyError(c.Context, c.Writer, 401, fmt.Sprintf
("Authentication error - %s", err)) | |
| 113 } | |
| 114 return | |
| 115 } | |
| 116 http.Redirect(c.Writer, c.Request, url, 302) | |
| 117 | |
| 118 default: | |
| 119 c.Context = ctx | |
| 120 next(c) | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 // replyError logs the error and writes it to ResponseWriter. | |
| 125 func replyError(c context.Context, rw http.ResponseWriter, code int, msg string)
{ | |
| 126 logging.Errorf(c, "HTTP %d: %s", code, msg) | |
| 127 http.Error(rw, msg, code) | |
| 128 } | |
| OLD | NEW |