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