Simplify the basicauth middleware

Former-commit-id: 8d184a434c992a884c5565bc22767ef295a1575a
This commit is contained in:
kataras 2017-06-10 15:28:09 +03:00
parent 6ab00aa02f
commit 5fa9789c35
9 changed files with 136 additions and 88 deletions

View File

@ -29,6 +29,11 @@ Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.co
The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please!
# Sa, 10 June 2017 | v7.0.4
- Simplify and add a test for the [basicauth middleware](https://github.com/kataras/iris/tree/master/middleware/basicauth), no need to be
stored inside the Context anymore, developers can get the validated user(username and password) via `context.Request().BasicAuth()`. `basicauth.Config.ContextKey` was removed, just remove that field from your configuration, it's useless now.
# Sa, 10 June 2017 | v7.0.3
- New `context.Session().PeekFlash("key")` added, unlike `GetFlash` this will return the flash value but keep the message valid for the next requests too.

View File

@ -6,7 +6,7 @@ A fast, cross-platform and efficient web framework with robust set of well-desig
[![Report card](https://img.shields.io/badge/report%20card%20-a%2B-F44336.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris)
[![Support forum](https://img.shields.io/badge/support-page-ec2eb4.svg?style=flat-square)](http://support.iris-go.com)
[![Examples](https://img.shields.io/badge/howto-examples-3362c2.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples#table-of-contents)
[![Godocs](https://img.shields.io/badge/7.0.3-%20documentation-5272B4.svg?style=flat-square)](https://godoc.org/github.com/kataras/iris)
[![Godocs](https://img.shields.io/badge/7.0.4-%20documentation-5272B4.svg?style=flat-square)](https://godoc.org/github.com/kataras/iris)
[![Chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris)
[![Buy me a cup of coffee](https://img.shields.io/badge/support-%20open--source-F4A460.svg?logo=data:image%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIGZpbGw9InJnYigyMjAsMjIwLDIyMCkiIGQ9Ik04ODYuNiwzMDUuM2MtNDUuNywyMDMuMS0xODcsMzEwLjMtNDA5LjYsMzEwLjNoLTc0LjFsLTUxLjUsMzI2LjloLTYybC0zLjIsMjEuMWMtMi4xLDE0LDguNiwyNi40LDIyLjYsMjYuNGgxNTguNWMxOC44LDAsMzQuNy0xMy42LDM3LjctMzIuMmwxLjUtOGwyOS45LTE4OS4zbDEuOS0xMC4zYzIuOS0xOC42LDE4LjktMzIuMiwzNy43LTMyLjJoMjMuNWMxNTMuNSwwLDI3My43LTYyLjQsMzA4LjktMjQyLjdDOTIxLjYsNDA2LjgsOTE2LjcsMzQ4LjYsODg2LjYsMzA1LjN6Ii8%2BPHBhdGggZmlsbD0icmdiKDIyMCwyMjAsMjIwKSIgZD0iTTc5MS45LDgzLjlDNzQ2LjUsMzIuMiw2NjQuNCwxMCw1NTkuNSwxMEgyNTVjLTIxLjQsMC0zOS44LDE1LjUtNDMuMSwzNi44TDg1LDg1MWMtMi41LDE1LjksOS44LDMwLjIsMjUuOCwzMC4ySDI5OWw0Ny4zLTI5OS42bC0xLjUsOS40YzMuMi0yMS4zLDIxLjQtMzYuOCw0Mi45LTM2LjhINDc3YzE3NS41LDAsMzEzLTcxLjIsMzUzLjItMjc3LjVjMS4yLTYuMSwyLjMtMTIuMSwzLjEtMTcuOEM4NDUuMSwxODIuOCw4MzMuMiwxMzAuOCw3OTEuOSw4My45TDc5MS45LDgzLjl6Ii8%2BPC9zdmc%2B)](https://github.com/kataras/iris#buy-me-a-cup-of-coffee)
@ -394,7 +394,7 @@ Besides the fact that we have a [community chat][Chat] for questions or reports
Version
------------
Current: **7.0.3**
Current: **7.0.4**
Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever".

View File

@ -14,7 +14,6 @@ func main() {
authConfig := basicauth.Config{
Users: map[string]string{"myusername": "mypassword", "mySecondusername": "mySecondpassword"},
Realm: "Authorization Required", // defaults to "Authorization Required"
ContextKey: "user", // defaults to "user"
Expires: time.Duration(30) * time.Minute,
}
@ -23,10 +22,7 @@ func main() {
// to global app.Use(authentication) (or app.UseGlobal before the .Run)
// to routes
/*
app.Get("/mysecret", authentication, func(ctx context.Context) {
username := ctx.Values().GetString("user") // the Contextkey from the authConfig
ctx.Writef("Hello authenticated user: %s ", username)
})
app.Get("/mysecret", authentication, h)
*/
app.Get("/", func(ctx context.Context) { ctx.Redirect("/admin") })
@ -36,23 +32,22 @@ func main() {
needAuth := app.Party("/admin", authentication)
{
//http://localhost:8080/admin
needAuth.Get("/", func(ctx context.Context) {
username := ctx.Values().GetString("user") // the Contextkey from the authConfig
ctx.Writef("Hello authenticated user: %s from: %s", username, ctx.Path())
})
needAuth.Get("/", h)
// http://localhost:8080/admin/profile
needAuth.Get("/profile", func(ctx context.Context) {
username := ctx.Values().GetString("user") // the Contextkey from the authConfig
ctx.Writef("Hello authenticated user: %s from: % ", username, ctx.Path())
})
needAuth.Get("/profile", h)
// http://localhost:8080/admin/settings
needAuth.Get("/settings", func(ctx context.Context) {
username := authConfig.User(ctx) // shortcut for ctx.Values().GetString("user")
ctx.Writef("Hello authenticated user: %s from: %s", username, ctx.Path())
})
needAuth.Get("/settings", h)
}
// open http://localhost:8080/admin
app.Run(iris.Addr(":8080"))
}
func h(ctx context.Context) {
username, password, _ := ctx.Request().BasicAuth()
// third parameter it will be always true because the middleware
// makes sure for that, otherwise this handler will not be executed.
ctx.Writef("%s %s:%s", ctx.Path(), username, password)
}

View File

@ -6,26 +6,15 @@ import (
"github.com/kataras/iris/middleware/basicauth"
)
func buildApp() *iris.Application {
func newApp() *iris.Application {
app := iris.New()
authConfig := basicauth.Config{
Users: map[string]string{"myusername": "mypassword", "mySecondusername": "mySecondpassword"},
Realm: "Authorization Required", // defaults to "Authorization Required"
ContextKey: "user", // defaults to "user"
Users: map[string]string{"myusername": "mypassword"},
}
authentication := basicauth.New(authConfig)
// to global app.Use(authentication) (or app.UseGlobal before the .Run)
// to routes
/*
app.Get("/mysecret", authentication, func(ctx context.Context) {
username := ctx.Values().GetString("user") // the Contextkey from the authConfig
ctx.Writef("Hello authenticated user: %s ", username)
})
*/
app.Get("/", func(ctx context.Context) { ctx.Redirect("/admin") })
// to party
@ -33,27 +22,26 @@ func buildApp() *iris.Application {
needAuth := app.Party("/admin", authentication)
{
//http://localhost:8080/admin
needAuth.Get("/", func(ctx context.Context) {
username := ctx.Values().GetString("user") // the Contextkey from the authConfig
ctx.Writef("Hello authenticated user: %s from: %s", username, ctx.Path())
})
needAuth.Get("/", h)
// http://localhost:8080/admin/profile
needAuth.Get("/profile", func(ctx context.Context) {
username := ctx.Values().GetString("user") // the Contextkey from the authConfig
ctx.Writef("Hello authenticated user: %s from: %s", username, ctx.Path())
})
needAuth.Get("/profile", h)
// http://localhost:8080/admin/settings
needAuth.Get("/settings", func(ctx context.Context) {
username := authConfig.User(ctx) // shortcut for ctx.Values().GetString("user")
ctx.Writef("Hello authenticated user: %s from: %s", username, ctx.Path())
})
needAuth.Get("/settings", h)
}
return app
}
func h(ctx context.Context) {
username, password, _ := ctx.Request().BasicAuth()
// third parameter it will be always true because the middleware
// makes sure for that, otherwise this handler will not be executed.
ctx.Writef("%s %s:%s", ctx.Path(), username, password)
}
func main() {
app := buildApp()
app := newApp()
app.Run(iris.Addr(":8080"))
}

View File

@ -7,10 +7,10 @@ import (
"github.com/kataras/iris/httptest"
)
// $ cd _example
// $ cd $GOPATH/src/github.com/kataras/iris/_examples/intermediate/httptest
// $ go test -v
func TestNewApp(t *testing.T) {
app := buildApp()
app := newApp()
e := httptest.New(app, t)
// redirects to /admin without basic auth
@ -20,11 +20,11 @@ func TestNewApp(t *testing.T) {
// with valid basic auth
e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect().
Status(iris.StatusOK).Body().Equal("Hello authenticated user: myusername from: /admin")
Status(iris.StatusOK).Body().Equal("/admin myusername:mypassword")
e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect().
Status(iris.StatusOK).Body().Equal("Hello authenticated user: myusername from: /admin/profile")
Status(iris.StatusOK).Body().Equal("/admin/profile myusername:mypassword")
e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect().
Status(iris.StatusOK).Body().Equal("Hello authenticated user: myusername from: /admin/settings")
Status(iris.StatusOK).Body().Equal("/admin/settings myusername:mypassword")
// with invalid basic auth
e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword").

View File

@ -41,7 +41,7 @@ const (
// Version is the current version number of the Iris Web framework.
//
// Look https://github.com/kataras/iris#where-can-i-find-older-versions for older versions.
Version = "7.0.3"
Version = "7.0.4"
)
const (

View File

@ -34,13 +34,12 @@ type (
//
// New takes one parameter, the Config returns a Handler
// use: iris.Use(New(...)), iris.Get(...,New(...),...)
// New accepts basicauth.Config and returns a new Handler
// which will ask the client for basic auth (username, password),
// validate that and if valid continues to the next handler, otherwise
// throws a StatusUnauthorized http error code.
func New(c Config) context.Handler {
config := DefaultConfig()
if c.ContextKey != "" {
config.ContextKey = c.ContextKey
}
if c.Realm != "" {
config.Realm = c.Realm
}
@ -51,8 +50,10 @@ func New(c Config) context.Handler {
return b.Serve
}
// Default takes one parameter, the users returns a Handler
// use: iris.Use(Default(...)), iris.Get(...,Default(...),...)
// Default accepts only the users and returns a new Handler
// which will ask the client for basic auth (username, password),
// validate that and if valid continues to the next handler, otherwise
// throws a StatusUnauthorized http error code.
func Default(users map[string]string) context.Handler {
c := DefaultConfig()
c.Users = users
@ -101,14 +102,14 @@ func (b *basicAuthMiddleware) askForCredentials(ctx context.Context) {
// Serve the actual middleware
func (b *basicAuthMiddleware) Serve(ctx context.Context) {
if auth, found := b.findAuth(ctx.GetHeader("Authorization")); !found {
auth, found := b.findAuth(ctx.GetHeader("Authorization"))
if !found {
b.askForCredentials(ctx)
return
// don't continue to the next handler
} else {
// all ok set the context's value in order to be getable from the next handler
ctx.Values().Set(b.config.ContextKey, auth.Username)
}
// all ok
if b.expireEnabled {
if auth.logged == false {
auth.expires = time.Now().Add(b.config.Expires)
auth.logged = true
@ -118,9 +119,6 @@ func (b *basicAuthMiddleware) Serve(ctx context.Context) {
b.askForCredentials(ctx) // ask for authentication again
return
}
}
ctx.Next() // continue
}
}

View File

@ -0,0 +1,67 @@
// black-box testing
package basicauth_test
import (
"testing"
"github.com/kataras/iris"
"github.com/kataras/iris/context"
"github.com/kataras/iris/httptest"
"github.com/kataras/iris/middleware/basicauth"
)
func buildApp() *iris.Application {
app := iris.New()
authConfig := basicauth.Config{
Users: map[string]string{"myusername": "mypassword"},
}
authentication := basicauth.New(authConfig)
app.Get("/", func(ctx context.Context) { ctx.Redirect("/admin") })
// to party
needAuth := app.Party("/admin", authentication)
{
//http://localhost:8080/admin
needAuth.Get("/", h)
// http://localhost:8080/admin/profile
needAuth.Get("/profile", h)
// http://localhost:8080/admin/settings
needAuth.Get("/settings", h)
}
return app
}
func h(ctx context.Context) {
username, password, _ := ctx.Request().BasicAuth()
// third parameter it will be always true because the middleware
// makes sure for that, otherwise this handler will not be executed.
ctx.Writef("%s %s:%s", ctx.Path(), username, password)
}
func TestBasicAuth(t *testing.T) {
app := buildApp()
e := httptest.New(app, t)
// redirects to /admin without basic auth
e.GET("/").Expect().Status(iris.StatusUnauthorized)
// without basic auth
e.GET("/admin").Expect().Status(iris.StatusUnauthorized)
// with valid basic auth
e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect().
Status(iris.StatusOK).Body().Equal("/admin myusername:mypassword")
e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect().
Status(iris.StatusOK).Body().Equal("/admin/profile myusername:mypassword")
e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect().
Status(iris.StatusOK).Body().Equal("/admin/settings myusername:mypassword")
// with invalid basic auth
e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword").
Expect().Status(iris.StatusUnauthorized)
}

View File

@ -13,9 +13,6 @@ import (
const (
// DefaultBasicAuthRealm is "Authorization Required"
DefaultBasicAuthRealm = "Authorization Required"
// DefaultBasicAuthContextKey is the "auth"
// this key is used to do context.Set("user", theUsernameFromBasicAuth)
DefaultBasicAuthContextKey = "user"
)
// DefaultExpireTime zero time
@ -27,18 +24,16 @@ type Config struct {
Users map[string]string
// Realm http://tools.ietf.org/html/rfc2617#section-1.2. Default is "Authorization Required"
Realm string
// ContextKey the key for ctx.GetString(...). Default is 'user'
ContextKey string
// Expires expiration duration, default is 0 never expires
Expires time.Duration
}
// DefaultConfig returns the default configs for the BasicAuth middleware
func DefaultConfig() Config {
return Config{make(map[string]string), DefaultBasicAuthRealm, DefaultBasicAuthContextKey, 0}
return Config{make(map[string]string), DefaultBasicAuthRealm, 0}
}
// User returns the user from context key same as 'ctx.GetString("user")' but cannot be used by the developer, this is only here in order to understand how you can get the authenticated username
func (c Config) User(ctx context.Context) string {
return ctx.Values().GetString(c.ContextKey)
// User returns the user from context key same as ctx.Request().BasicAuth().
func (c Config) User(ctx context.Context) (string, string, bool) {
return ctx.Request().BasicAuth()
}