mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
add context partial user helper and accept a generic interface on SetUser - the same method now returns an error if the given value does not complete at least one method of the User interface
This commit is contained in:
parent
8eea0296a7
commit
3d59d19de6
|
@ -56,7 +56,9 @@ func h(ctx iris.Context) {
|
||||||
// makes sure for that, otherwise this handler will not be executed.
|
// makes sure for that, otherwise this handler will not be executed.
|
||||||
// OR:
|
// OR:
|
||||||
user := ctx.User()
|
user := ctx.User()
|
||||||
ctx.Writef("%s %s:%s", ctx.Path(), user.GetUsername(), user.GetPassword())
|
username, _ := user.GetUsername()
|
||||||
|
password, _ := user.GetPassword
|
||||||
|
ctx.Writef("%s %s:%s", ctx.Path(), username, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logout(ctx iris.Context) {
|
func logout(ctx iris.Context) {
|
||||||
|
|
|
@ -26,14 +26,14 @@ func main() {
|
||||||
|
|
||||||
signer := jwt.NewSigner(jwt.HS256, sigKey, 10*time.Minute)
|
signer := jwt.NewSigner(jwt.HS256, sigKey, 10*time.Minute)
|
||||||
// Enable payload encryption with:
|
// Enable payload encryption with:
|
||||||
// signer.WithGCM(encKey, nil)
|
// signer.WithEncryption(encKey, nil)
|
||||||
app.Get("/", generateToken(signer))
|
app.Get("/", generateToken(signer))
|
||||||
|
|
||||||
verifier := jwt.NewVerifier(jwt.HS256, sigKey)
|
verifier := jwt.NewVerifier(jwt.HS256, sigKey)
|
||||||
// Enable server-side token block feature (even before its expiration time):
|
// Enable server-side token block feature (even before its expiration time):
|
||||||
verifier.WithDefaultBlocklist()
|
verifier.WithDefaultBlocklist()
|
||||||
// Enable payload decryption with:
|
// Enable payload decryption with:
|
||||||
// verifier.WithGCM(encKey, nil)
|
// verifier.WithDecryption(encKey, nil)
|
||||||
verifyMiddleware := verifier.Verify(func() interface{} {
|
verifyMiddleware := verifier.Verify(func() interface{} {
|
||||||
return new(fooClaims)
|
return new(fooClaims)
|
||||||
})
|
})
|
||||||
|
|
|
@ -28,6 +28,21 @@ type UserClaims struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetID implements the partial context user's ID interface.
|
||||||
|
// Note that if claims were a map then the claims value converted to UserClaims
|
||||||
|
// and no need to implement any method.
|
||||||
|
//
|
||||||
|
// This is useful when multiple auth methods are used (e.g. basic auth, jwt)
|
||||||
|
// but they all share a couple of methods.
|
||||||
|
func (u *UserClaims) GetID() string {
|
||||||
|
return u.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsername implements the partial context user's Username interface.
|
||||||
|
func (u *UserClaims) GetUsername() string {
|
||||||
|
return u.Username
|
||||||
|
}
|
||||||
|
|
||||||
// Validate completes the middleware's custom ClaimsValidator.
|
// Validate completes the middleware's custom ClaimsValidator.
|
||||||
// It will not accept a token which its claims missing the username field
|
// It will not accept a token which its claims missing the username field
|
||||||
// (useful to not accept refresh tokens generated by the same algorithm).
|
// (useful to not accept refresh tokens generated by the same algorithm).
|
||||||
|
@ -58,8 +73,15 @@ func main() {
|
||||||
protectedAPI.Use(verifyMiddleware)
|
protectedAPI.Use(verifyMiddleware)
|
||||||
|
|
||||||
protectedAPI.Get("/", func(ctx iris.Context) {
|
protectedAPI.Get("/", func(ctx iris.Context) {
|
||||||
claims := jwt.Get(ctx).(*UserClaims)
|
// Access the claims through: jwt.Get:
|
||||||
ctx.Writef("Username: %s\n", claims.Username)
|
// claims := jwt.Get(ctx).(*UserClaims)
|
||||||
|
// ctx.Writef("Username: %s\n", claims.Username)
|
||||||
|
//
|
||||||
|
// OR through context's user (if at least one method was implement by our UserClaims):
|
||||||
|
user := ctx.User()
|
||||||
|
id, _ := user.GetID()
|
||||||
|
username, _ := user.GetUsername()
|
||||||
|
ctx.Writef("ID: %s\nUsername: %s\n", id, username)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5340,12 +5340,34 @@ func (ctx *Context) Logout(args ...interface{}) error {
|
||||||
|
|
||||||
const userContextKey = "iris.user"
|
const userContextKey = "iris.user"
|
||||||
|
|
||||||
// SetUser sets a User for this request.
|
// SetUser sets a value as a User for this request.
|
||||||
// It's used by auth middlewares as a common
|
// It's used by auth middlewares as a common
|
||||||
// method to provide user information to the
|
// method to provide user information to the
|
||||||
// next handlers in the chain.
|
// next handlers in the chain
|
||||||
func (ctx *Context) SetUser(u User) {
|
// Look the `User` method to retrieve it.
|
||||||
|
func (ctx *Context) SetUser(i interface{}) error {
|
||||||
|
if i == nil {
|
||||||
|
ctx.values.Remove(userContextKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
u, ok := i.(User)
|
||||||
|
if !ok {
|
||||||
|
if m, ok := i.(Map); ok { // it's a map, convert it to a User.
|
||||||
|
u = UserMap(m)
|
||||||
|
} else {
|
||||||
|
// It's a structure, wrap it and let
|
||||||
|
// runtime decide the features.
|
||||||
|
p := newUserPartial(i)
|
||||||
|
if p == nil {
|
||||||
|
return ErrNotSupported
|
||||||
|
}
|
||||||
|
u = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.values.Set(userContextKey, u)
|
ctx.values.Set(userContextKey, u)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// User returns the registered User of this request.
|
// User returns the registered User of this request.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -27,30 +28,33 @@ var ErrNotSupported = errors.New("not supported")
|
||||||
// To make optional some of the fields you can just embed the User interface
|
// To make optional some of the fields you can just embed the User interface
|
||||||
// and implement whatever methods you want to support.
|
// and implement whatever methods you want to support.
|
||||||
//
|
//
|
||||||
// There are two builtin implementations of the User interface:
|
// There are three builtin implementations of the User interface:
|
||||||
// - SimpleUser (type-safe)
|
// - SimpleUser
|
||||||
// - UserMap (wraps a map[string]interface{})
|
// - UserMap (a wrapper by SetUser)
|
||||||
|
// - UserPartial (a wrapper by SetUser)
|
||||||
type User interface {
|
type User interface {
|
||||||
// GetAuthorization should return the authorization method,
|
// GetAuthorization should return the authorization method,
|
||||||
// e.g. Basic Authentication.
|
// e.g. Basic Authentication.
|
||||||
GetAuthorization() string
|
GetAuthorization() (string, error)
|
||||||
// GetAuthorizedAt should return the exact time the
|
// GetAuthorizedAt should return the exact time the
|
||||||
// client has been authorized for the "first" time.
|
// client has been authorized for the "first" time.
|
||||||
GetAuthorizedAt() time.Time
|
GetAuthorizedAt() (time.Time, error)
|
||||||
|
// GetID should return the ID of the User.
|
||||||
|
GetID() (string, error)
|
||||||
// GetUsername should return the name of the User.
|
// GetUsername should return the name of the User.
|
||||||
GetUsername() string
|
GetUsername() (string, error)
|
||||||
// GetPassword should return the encoded or raw password
|
// GetPassword should return the encoded or raw password
|
||||||
// (depends on the implementation) of the User.
|
// (depends on the implementation) of the User.
|
||||||
GetPassword() string
|
GetPassword() (string, error)
|
||||||
// GetEmail should return the e-mail of the User.
|
// GetEmail should return the e-mail of the User.
|
||||||
GetEmail() string
|
GetEmail() (string, error)
|
||||||
// GetRoles should optionally return the specific user's roles.
|
// GetRoles should optionally return the specific user's roles.
|
||||||
// Returns `ErrNotSupported` if this method is not
|
// Returns `ErrNotSupported` if this method is not
|
||||||
// implemented by the User implementation.
|
// implemented by the User implementation.
|
||||||
GetRoles() ([]string, error)
|
GetRoles() ([]string, error)
|
||||||
// GetToken should optionally return a token used
|
// GetToken should optionally return a token used
|
||||||
// to authorize this User.
|
// to authorize this User.
|
||||||
GetToken() (string, error)
|
GetToken() ([]byte, error)
|
||||||
// GetField should optionally return a dynamic field
|
// GetField should optionally return a dynamic field
|
||||||
// based on its key. Useful for custom user fields.
|
// based on its key. Useful for custom user fields.
|
||||||
// Keep in mind that these fields are encoded as a separate JSON key.
|
// Keep in mind that these fields are encoded as a separate JSON key.
|
||||||
|
@ -63,6 +67,7 @@ add a Raw() interface{} to return the underline User implementation too.
|
||||||
The advandages of the above idea is that we don't have to add new methods
|
The advandages of the above idea is that we don't have to add new methods
|
||||||
for each of the builtin features and we can keep the (assumed) struct small.
|
for each of the builtin features and we can keep the (assumed) struct small.
|
||||||
But we dont as it has many disadvantages, unless is requested.
|
But we dont as it has many disadvantages, unless is requested.
|
||||||
|
^ UPDATE: this is done through UserPartial.
|
||||||
|
|
||||||
The disadvantage of the current implementation is that the developer MUST
|
The disadvantage of the current implementation is that the developer MUST
|
||||||
complete the whole interface in order to be a valid User and if we add
|
complete the whole interface in order to be a valid User and if we add
|
||||||
|
@ -72,88 +77,51 @@ We kind of by-pass this disadvantage by providing a SimpleUser which can be embe
|
||||||
to the end-developer's custom implementations.
|
to the end-developer's custom implementations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// FeaturedUser optional interface that a User can implement.
|
|
||||||
type FeaturedUser interface {
|
|
||||||
User
|
|
||||||
// GetFeatures should optionally return a list of features
|
|
||||||
// the User implementation offers.
|
|
||||||
GetFeatures() []UserFeature
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserFeature a type which represents a user's optional feature.
|
|
||||||
// See `HasUserFeature` function for more.
|
|
||||||
type UserFeature uint32
|
|
||||||
|
|
||||||
// The list of standard UserFeatures.
|
|
||||||
const (
|
|
||||||
AuthorizedAtFeature UserFeature = iota
|
|
||||||
UsernameFeature
|
|
||||||
PasswordFeature
|
|
||||||
EmailFeature
|
|
||||||
RolesFeature
|
|
||||||
TokenFeature
|
|
||||||
FieldsFeature
|
|
||||||
)
|
|
||||||
|
|
||||||
// HasUserFeature reports whether the "u" User
|
|
||||||
// implements a specific "feature" User Feature.
|
|
||||||
//
|
|
||||||
// It returns ErrNotSupported if a user does not implement
|
|
||||||
// the FeaturedUser interface.
|
|
||||||
func HasUserFeature(user User, feature UserFeature) (bool, error) {
|
|
||||||
if u, ok := user.(FeaturedUser); ok {
|
|
||||||
for _, f := range u.GetFeatures() {
|
|
||||||
if f == feature {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, ErrNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimpleUser is a simple implementation of the User interface.
|
// SimpleUser is a simple implementation of the User interface.
|
||||||
type SimpleUser struct {
|
type SimpleUser struct {
|
||||||
Authorization string `json:"authorization"`
|
Authorization string `json:"authorization,omitempty"`
|
||||||
AuthorizedAt time.Time `json:"authorized_at"`
|
AuthorizedAt time.Time `json:"authorized_at,omitempty"`
|
||||||
Username string `json:"username,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Password string `json:"-"`
|
Username string `json:"username,omitempty"`
|
||||||
Email string `json:"email,omitempty"`
|
Password string `json:"-"`
|
||||||
Roles []string `json:"roles,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
Features []UserFeature `json:"features,omitempty"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
Token string `json:"token,omitempty"`
|
Token json.RawMessage `json:"token,omitempty"`
|
||||||
Fields Map `json:"fields,omitempty"`
|
Fields Map `json:"fields,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ FeaturedUser = (*SimpleUser)(nil)
|
var _ User = (*SimpleUser)(nil)
|
||||||
|
|
||||||
// GetAuthorization returns the authorization method,
|
// GetAuthorization returns the authorization method,
|
||||||
// e.g. Basic Authentication.
|
// e.g. Basic Authentication.
|
||||||
func (u *SimpleUser) GetAuthorization() string {
|
func (u *SimpleUser) GetAuthorization() (string, error) {
|
||||||
return u.Authorization
|
return u.Authorization, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthorizedAt returns the exact time the
|
// GetAuthorizedAt returns the exact time the
|
||||||
// client has been authorized for the "first" time.
|
// client has been authorized for the "first" time.
|
||||||
func (u *SimpleUser) GetAuthorizedAt() time.Time {
|
func (u *SimpleUser) GetAuthorizedAt() (time.Time, error) {
|
||||||
return u.AuthorizedAt
|
return u.AuthorizedAt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID returns the ID of the User.
|
||||||
|
func (u *SimpleUser) GetID() (string, error) {
|
||||||
|
return u.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsername returns the name of the User.
|
// GetUsername returns the name of the User.
|
||||||
func (u *SimpleUser) GetUsername() string {
|
func (u *SimpleUser) GetUsername() (string, error) {
|
||||||
return u.Username
|
return u.Username, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPassword returns the raw password of the User.
|
// GetPassword returns the raw password of the User.
|
||||||
func (u *SimpleUser) GetPassword() string {
|
func (u *SimpleUser) GetPassword() (string, error) {
|
||||||
return u.Password
|
return u.Password, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEmail returns the e-mail of the User.
|
// GetEmail returns the e-mail of (string,error) User.
|
||||||
func (u *SimpleUser) GetEmail() string {
|
func (u *SimpleUser) GetEmail() (string, error) {
|
||||||
return u.Email
|
return u.Email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoles returns the specific user's roles.
|
// GetRoles returns the specific user's roles.
|
||||||
|
@ -171,9 +139,9 @@ func (u *SimpleUser) GetRoles() ([]string, error) {
|
||||||
//
|
//
|
||||||
// The implementation can change that behavior.
|
// The implementation can change that behavior.
|
||||||
// Returns with `ErrNotSupported` if the Token field is empty.
|
// Returns with `ErrNotSupported` if the Token field is empty.
|
||||||
func (u *SimpleUser) GetToken() (string, error) {
|
func (u *SimpleUser) GetToken() ([]byte, error) {
|
||||||
if u.Token == "" {
|
if len(u.Token) == 0 {
|
||||||
return "", ErrNotSupported
|
return nil, ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.Token, nil
|
return u.Token, nil
|
||||||
|
@ -189,104 +157,66 @@ func (u *SimpleUser) GetField(key string) (interface{}, error) {
|
||||||
return u.Fields[key], nil
|
return u.Fields[key], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeatures returns a list of features
|
|
||||||
// this User implementation offers.
|
|
||||||
func (u *SimpleUser) GetFeatures() []UserFeature {
|
|
||||||
if u.Features != nil {
|
|
||||||
return u.Features
|
|
||||||
}
|
|
||||||
|
|
||||||
var features []UserFeature
|
|
||||||
|
|
||||||
if !u.AuthorizedAt.IsZero() {
|
|
||||||
features = append(features, AuthorizedAtFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Username != "" {
|
|
||||||
features = append(features, UsernameFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Password != "" {
|
|
||||||
features = append(features, PasswordFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Email != "" {
|
|
||||||
features = append(features, EmailFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Roles != nil {
|
|
||||||
features = append(features, RolesFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Fields != nil {
|
|
||||||
features = append(features, FieldsFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
return features
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserMap can be used to convert a common map[string]interface{} to a User.
|
// UserMap can be used to convert a common map[string]interface{} to a User.
|
||||||
// Usage:
|
// Usage:
|
||||||
// user := map[string]interface{}{
|
// user := map[string]interface{}{
|
||||||
// "username": "kataras",
|
// "username": "kataras",
|
||||||
// "age" : 27,
|
// "age" : 27,
|
||||||
// }
|
// }
|
||||||
// ctx.SetUser(UserMap(user))
|
// ctx.SetUser(user)
|
||||||
// OR
|
// OR
|
||||||
// user := UserMap{"key": "value",...}
|
// user := UserStruct{....}
|
||||||
// ctx.SetUser(user)
|
// ctx.SetUser(user)
|
||||||
// [...]
|
// [...]
|
||||||
// username := ctx.User().GetUsername()
|
// username, err := ctx.User().GetUsername()
|
||||||
// age := ctx.User().GetField("age").(int)
|
// field,err := ctx.User().GetField("age")
|
||||||
|
// age := field.(int)
|
||||||
// OR cast it:
|
// OR cast it:
|
||||||
// user := ctx.User().(UserMap)
|
// user := ctx.User().(UserMap)
|
||||||
// username := user["username"].(string)
|
// username := user["username"].(string)
|
||||||
// age := user["age"].(int)
|
// age := user["age"].(int)
|
||||||
type UserMap Map
|
type UserMap Map
|
||||||
|
|
||||||
var _ FeaturedUser = UserMap{}
|
var _ User = UserMap{}
|
||||||
|
|
||||||
// GetAuthorization returns the authorization or Authorization value of the map.
|
// GetAuthorization returns the authorization or Authorization value of the map.
|
||||||
func (u UserMap) GetAuthorization() string {
|
func (u UserMap) GetAuthorization() (string, error) {
|
||||||
return u.str("authorization")
|
return u.str("authorization")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthorizedAt returns the authorized_at or Authorized_At value of the map.
|
// GetAuthorizedAt returns the authorized_at or Authorized_At value of the map.
|
||||||
func (u UserMap) GetAuthorizedAt() time.Time {
|
func (u UserMap) GetAuthorizedAt() (time.Time, error) {
|
||||||
return u.time("authorized_at")
|
return u.time("authorized_at")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetID returns the id or Id or ID value of the map.
|
||||||
|
func (u UserMap) GetID() (string, error) {
|
||||||
|
return u.str("id")
|
||||||
|
}
|
||||||
|
|
||||||
// GetUsername returns the username or Username value of the map.
|
// GetUsername returns the username or Username value of the map.
|
||||||
func (u UserMap) GetUsername() string {
|
func (u UserMap) GetUsername() (string, error) {
|
||||||
return u.str("username")
|
return u.str("username")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPassword returns the password or Password value of the map.
|
// GetPassword returns the password or Password value of the map.
|
||||||
func (u UserMap) GetPassword() string {
|
func (u UserMap) GetPassword() (string, error) {
|
||||||
return u.str("password")
|
return u.str("password")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEmail returns the email or Email value of the map.
|
// GetEmail returns the email or Email value of the map.
|
||||||
func (u UserMap) GetEmail() string {
|
func (u UserMap) GetEmail() (string, error) {
|
||||||
return u.str("email")
|
return u.str("email")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoles returns the roles or Roles value of the map.
|
// GetRoles returns the roles or Roles value of the map.
|
||||||
func (u UserMap) GetRoles() ([]string, error) {
|
func (u UserMap) GetRoles() ([]string, error) {
|
||||||
if s := u.strSlice("roles"); s != nil {
|
return u.strSlice("roles")
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ErrNotSupported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken returns the roles or Roles value of the map.
|
// GetToken returns the roles or Roles value of the map.
|
||||||
func (u UserMap) GetToken() (string, error) {
|
func (u UserMap) GetToken() ([]byte, error) {
|
||||||
if s := u.str("token"); s != "" {
|
return u.bytes("token")
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", ErrNotSupported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetField returns the raw map's value based on its "key".
|
// GetField returns the raw map's value based on its "key".
|
||||||
|
@ -295,41 +225,6 @@ func (u UserMap) GetField(key string) (interface{}, error) {
|
||||||
return u[key], nil
|
return u[key], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFeatures returns a list of features
|
|
||||||
// this map offers.
|
|
||||||
func (u UserMap) GetFeatures() []UserFeature {
|
|
||||||
if v := u.val("features"); v != nil { // if already contain features.
|
|
||||||
if features, ok := v.([]UserFeature); ok {
|
|
||||||
return features
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// else try to resolve from map values.
|
|
||||||
features := []UserFeature{FieldsFeature}
|
|
||||||
|
|
||||||
if !u.GetAuthorizedAt().IsZero() {
|
|
||||||
features = append(features, AuthorizedAtFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.GetUsername() != "" {
|
|
||||||
features = append(features, UsernameFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.GetPassword() != "" {
|
|
||||||
features = append(features, PasswordFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.GetEmail() != "" {
|
|
||||||
features = append(features, EmailFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
if roles, err := u.GetRoles(); err == nil && roles != nil {
|
|
||||||
features = append(features, RolesFeature)
|
|
||||||
}
|
|
||||||
|
|
||||||
return features
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u UserMap) val(key string) interface{} {
|
func (u UserMap) val(key string) interface{} {
|
||||||
isTitle := unicode.IsTitle(rune(key[0])) // if starts with uppercase.
|
isTitle := unicode.IsTitle(rune(key[0])) // if starts with uppercase.
|
||||||
if isTitle {
|
if isTitle {
|
||||||
|
@ -339,34 +234,248 @@ func (u UserMap) val(key string) interface{} {
|
||||||
return u[key]
|
return u[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u UserMap) str(key string) string {
|
func (u UserMap) bytes(key string) ([]byte, error) {
|
||||||
|
if v := u.val(key); v != nil {
|
||||||
|
switch s := v.(type) {
|
||||||
|
case []byte:
|
||||||
|
return s, nil
|
||||||
|
case string:
|
||||||
|
return []byte(s), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UserMap) str(key string) (string, error) {
|
||||||
if v := u.val(key); v != nil {
|
if v := u.val(key); v != nil {
|
||||||
if s, ok := v.(string); ok {
|
if s, ok := v.(string); ok {
|
||||||
return s
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// exists or not we don't care, if it's invalid type we don't fill it.
|
// exists or not we don't care, if it's invalid type we don't fill it.
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return "", ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u UserMap) strSlice(key string) []string {
|
func (u UserMap) strSlice(key string) ([]string, error) {
|
||||||
if v := u.val(key); v != nil {
|
if v := u.val(key); v != nil {
|
||||||
if s, ok := v.([]string); ok {
|
if s, ok := v.([]string); ok {
|
||||||
return s
|
return s, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u UserMap) time(key string) time.Time {
|
func (u UserMap) time(key string) (time.Time, error) {
|
||||||
if v := u.val(key); v != nil {
|
if v := u.val(key); v != nil {
|
||||||
if t, ok := v.(time.Time); ok {
|
if t, ok := v.(time.Time); ok {
|
||||||
return t
|
return t, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}
|
return time.Time{}, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
userGetAuthorization interface {
|
||||||
|
GetAuthorization() string
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetAuthorizedAt interface {
|
||||||
|
GetAuthorizedAt() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetID interface {
|
||||||
|
GetID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetUsername interface {
|
||||||
|
GetUsername() string
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetPassword interface {
|
||||||
|
GetPassword() string
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetEmail interface {
|
||||||
|
GetEmail() string
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetRoles interface {
|
||||||
|
GetRoles() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetToken interface {
|
||||||
|
GetToken() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
userGetField interface {
|
||||||
|
GetField(string) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserPartial is a User.
|
||||||
|
// It's a helper which wraps a struct value that
|
||||||
|
// may or may not complete the whole User interface.
|
||||||
|
UserPartial struct {
|
||||||
|
Raw interface{}
|
||||||
|
userGetAuthorization
|
||||||
|
userGetAuthorizedAt
|
||||||
|
userGetID
|
||||||
|
userGetUsername
|
||||||
|
userGetPassword
|
||||||
|
userGetEmail
|
||||||
|
userGetRoles
|
||||||
|
userGetToken
|
||||||
|
userGetField
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ User = (*UserPartial)(nil)
|
||||||
|
|
||||||
|
func newUserPartial(i interface{}) *UserPartial {
|
||||||
|
containsAtLeastOneMethod := false
|
||||||
|
p := &UserPartial{Raw: i}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetAuthorization); ok {
|
||||||
|
p.userGetAuthorization = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetAuthorizedAt); ok {
|
||||||
|
p.userGetAuthorizedAt = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetID); ok {
|
||||||
|
p.userGetID = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetUsername); ok {
|
||||||
|
p.userGetUsername = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetPassword); ok {
|
||||||
|
p.userGetPassword = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetEmail); ok {
|
||||||
|
p.userGetEmail = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetRoles); ok {
|
||||||
|
p.userGetRoles = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetToken); ok {
|
||||||
|
p.userGetToken = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if u, ok := i.(userGetField); ok {
|
||||||
|
p.userGetField = u
|
||||||
|
containsAtLeastOneMethod = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !containsAtLeastOneMethod {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorization should return the authorization method,
|
||||||
|
// e.g. Basic Authentication.
|
||||||
|
func (u *UserPartial) GetAuthorization() (string, error) {
|
||||||
|
if v := u.userGetAuthorization; v != nil {
|
||||||
|
return v.GetAuthorization(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorizedAt should return the exact time the
|
||||||
|
// client has been authorized for the "first" time.
|
||||||
|
func (u *UserPartial) GetAuthorizedAt() (time.Time, error) {
|
||||||
|
if v := u.userGetAuthorizedAt; v != nil {
|
||||||
|
return v.GetAuthorizedAt(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Time{}, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID should return the ID of the User.
|
||||||
|
func (u *UserPartial) GetID() (string, error) {
|
||||||
|
if v := u.userGetID; v != nil {
|
||||||
|
return v.GetID(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsername should return the name of the User.
|
||||||
|
func (u *UserPartial) GetUsername() (string, error) {
|
||||||
|
if v := u.userGetUsername; v != nil {
|
||||||
|
return v.GetUsername(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPassword should return the encoded or raw password
|
||||||
|
// (depends on the implementation) of the User.
|
||||||
|
func (u *UserPartial) GetPassword() (string, error) {
|
||||||
|
if v := u.userGetPassword; v != nil {
|
||||||
|
return v.GetPassword(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEmail should return the e-mail of the User.
|
||||||
|
func (u *UserPartial) GetEmail() (string, error) {
|
||||||
|
if v := u.userGetEmail; v != nil {
|
||||||
|
return v.GetEmail(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoles should optionally return the specific user's roles.
|
||||||
|
// Returns `ErrNotSupported` if this method is not
|
||||||
|
// implemented by the User implementation.
|
||||||
|
func (u *UserPartial) GetRoles() ([]string, error) {
|
||||||
|
if v := u.userGetRoles; v != nil {
|
||||||
|
return v.GetRoles(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetToken should optionally return a token used
|
||||||
|
// to authorize this User.
|
||||||
|
func (u *UserPartial) GetToken() ([]byte, error) {
|
||||||
|
if v := u.userGetToken; v != nil {
|
||||||
|
return v.GetToken(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetField should optionally return a dynamic field
|
||||||
|
// based on its key. Useful for custom user fields.
|
||||||
|
// Keep in mind that these fields are encoded as a separate JSON key.
|
||||||
|
func (u *UserPartial) GetField(key string) (interface{}, error) {
|
||||||
|
if v := u.userGetField; v != nil {
|
||||||
|
return v.GetField(key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,11 @@ func TestBasicAuthUseRouter(t *testing.T) {
|
||||||
|
|
||||||
app.Get("/user_string", func(ctx iris.Context) {
|
app.Get("/user_string", func(ctx iris.Context) {
|
||||||
user := ctx.User()
|
user := ctx.User()
|
||||||
ctx.Writef("%s\n%s\n%s", user.GetAuthorization(), user.GetUsername(), user.GetPassword())
|
|
||||||
|
authorization, _ := user.GetAuthorization()
|
||||||
|
username, _ := user.GetUsername()
|
||||||
|
password, _ := user.GetPassword()
|
||||||
|
ctx.Writef("%s\n%s\n%s", authorization, username, password)
|
||||||
})
|
})
|
||||||
|
|
||||||
app.Get("/", func(ctx iris.Context) {
|
app.Get("/", func(ctx iris.Context) {
|
||||||
|
|
|
@ -24,8 +24,8 @@ func NewSigner(signatureAlg Alg, signatureKey interface{}, maxAge time.Duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithGCM enables AES-GCM payload decryption.
|
// WithEncryption enables AES-GCM payload decryption.
|
||||||
func (s *Signer) WithGCM(key, additionalData []byte) *Signer {
|
func (s *Signer) WithEncryption(key, additionalData []byte) *Signer {
|
||||||
encrypt, _, err := jwt.GCM(key, additionalData)
|
encrypt, _, err := jwt.GCM(key, additionalData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // important error before serve, stop everything.
|
panic(err) // important error before serve, stop everything.
|
||||||
|
@ -35,10 +35,14 @@ func (s *Signer) WithGCM(key, additionalData []byte) *Signer {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sign generates a new token based on the given "claims" which is valid up to "s.MaxAge".
|
||||||
func (s *Signer) Sign(claims interface{}, opts ...SignOption) ([]byte, error) {
|
func (s *Signer) Sign(claims interface{}, opts ...SignOption) ([]byte, error) {
|
||||||
return SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, append([]SignOption{MaxAge(s.MaxAge)}, opts...)...)
|
return SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, append([]SignOption{MaxAge(s.MaxAge)}, opts...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTokenPair accepts the access and refresh claims plus the life time duration for the refresh token
|
||||||
|
// and generates a new token pair which can be sent to the client.
|
||||||
|
// The same token pair can be json-decoded.
|
||||||
func (s *Signer) NewTokenPair(accessClaims interface{}, refreshClaims interface{}, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) {
|
func (s *Signer) NewTokenPair(accessClaims interface{}, refreshClaims interface{}, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) {
|
||||||
if refreshMaxAge <= s.MaxAge {
|
if refreshMaxAge <= s.MaxAge {
|
||||||
return TokenPair{}, fmt.Errorf("refresh max age should be bigger than access token's one[%d - %d]", refreshMaxAge, s.MaxAge)
|
return TokenPair{}, fmt.Errorf("refresh max age should be bigger than access token's one[%d - %d]", refreshMaxAge, s.MaxAge)
|
||||||
|
|
|
@ -49,6 +49,8 @@ type Verifier struct {
|
||||||
Validators []TokenValidator
|
Validators []TokenValidator
|
||||||
|
|
||||||
ErrorHandler func(ctx *context.Context, err error)
|
ErrorHandler func(ctx *context.Context, err error)
|
||||||
|
// DisableContextUser disables the registration of the claims as context User.
|
||||||
|
DisableContextUser bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVerifier accepts the algorithm for the token's signature among with its (private) key
|
// NewVerifier accepts the algorithm for the token's signature among with its (private) key
|
||||||
|
@ -67,8 +69,8 @@ func NewVerifier(signatureAlg Alg, signatureKey interface{}, validators ...Token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithGCM enables AES-GCM payload encryption.
|
// WithDecryption enables AES-GCM payload encryption.
|
||||||
func (v *Verifier) WithGCM(key, additionalData []byte) *Verifier {
|
func (v *Verifier) WithDecryption(key, additionalData []byte) *Verifier {
|
||||||
_, decrypt, err := jwt.GCM(key, additionalData)
|
_, decrypt, err := jwt.GCM(key, additionalData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // important error before serve, stop everything.
|
panic(err) // important error before serve, stop everything.
|
||||||
|
@ -176,8 +178,8 @@ func (v *Verifier) Verify(claimsType func() interface{}, validators ...TokenVali
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u, ok := dest.(context.User); ok {
|
if !v.DisableContextUser {
|
||||||
ctx.SetUser(u)
|
ctx.SetUser(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Values().Set(claimsContextKey, dest)
|
ctx.Values().Set(claimsContextKey, dest)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user