♻️ Refactor login sub-package
This commit is contained in:
parent
8a41bcfe09
commit
280f059bd4
|
@ -4,46 +4,58 @@ import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrUserNotDefined describes error of an unassigned structure of user
|
http "github.com/valyala/fasthttp"
|
||||||
var ErrUserNotDefined = errors.New("user is not defined")
|
)
|
||||||
|
|
||||||
// CheckAuthorization verify the authentication and the integrity of the data
|
// CheckAuthorization verify the authentication and the integrity of the data
|
||||||
// received by comparing the received hash parameter with the hexadecimal
|
// received by comparing the received hash parameter with the hexadecimal
|
||||||
// representation of the HMAC-SHA-256 signature of the data-check-string with the
|
// representation of the HMAC-SHA-256 signature of the data-check-string with the
|
||||||
// SHA256 hash of the bot's token used as a secret key.
|
// SHA256 hash of the bot's token used as a secret key.
|
||||||
func (a *App) CheckAuthorization(user *User) (ok bool, err error) {
|
func CheckAuthorization(data interface{}, secretKey string) (bool, error) {
|
||||||
if user == nil {
|
args := http.AcquireArgs()
|
||||||
err = ErrUserNotDefined
|
defer http.ReleaseArgs(args)
|
||||||
return
|
|
||||||
|
switch d := data.(type) {
|
||||||
|
case *User:
|
||||||
|
return d.CheckAuthorization(secretKey)
|
||||||
|
case *http.Args:
|
||||||
|
d.CopyTo(args)
|
||||||
|
http.ReleaseArgs(d)
|
||||||
|
case []byte:
|
||||||
|
args.ParseBytes(d)
|
||||||
|
case string:
|
||||||
|
args.Parse(d)
|
||||||
|
default:
|
||||||
|
return false, ErrUnsupportedType
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dataCheck := make(url.Values)
|
hash := args.Peek(KeyHash)
|
||||||
dataCheck.Add(KeyAuthDate, string(user.AuthDate))
|
args.Del(KeyHash)
|
||||||
dataCheck.Add(KeyFirstName, user.FirstName)
|
|
||||||
dataCheck.Add(KeyID, strconv.Itoa(user.ID))
|
|
||||||
|
|
||||||
// Add optional values if exist
|
return check(args.QueryString(), []byte(secretKey), hash)
|
||||||
if user.LastName != "" {
|
}
|
||||||
dataCheck.Add(KeyLastName, user.LastName)
|
|
||||||
}
|
// CheckAuthorization verify the authentication and the integrity of the data
|
||||||
if user.PhotoURL != "" {
|
// received by comparing the received hash parameter with the hexadecimal
|
||||||
dataCheck.Add(KeyPhotoURL, user.PhotoURL)
|
// representation of the HMAC-SHA-256 signature of the data-check-string with the
|
||||||
}
|
// SHA256 hash of the bot's token used as a secret key.
|
||||||
if user.Username != "" {
|
func (u *User) CheckAuthorization(secretKey string) (ok bool, err error) {
|
||||||
dataCheck.Add(KeyUsername, user.Username)
|
args := u.toArgs()
|
||||||
}
|
defer http.ReleaseArgs(args)
|
||||||
|
hash := args.Peek(KeyHash)
|
||||||
secretKey := sha256.Sum256([]byte(a.SecretKey))
|
args.Del(KeyHash)
|
||||||
hash := hmac.New(sha256.New, secretKey[0:])
|
|
||||||
if _, err = hash.Write([]byte(dataCheck.Encode())); err != nil {
|
return check(args.QueryString(), []byte(secretKey), hash)
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
func check(data, secretKey, hash []byte) (bool, error) {
|
||||||
ok = hex.EncodeToString(hash.Sum(nil)) == user.Hash
|
sk := sha256.Sum256(secretKey)
|
||||||
return
|
h := hmac.New(sha256.New, sk[0:])
|
||||||
|
if _, err := h.Write(data); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(h.Sum(nil)) == string(hash), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package login
|
|
||||||
|
|
||||||
// Key... represents available and supported data keys
|
|
||||||
const (
|
|
||||||
KeyAuthDate = "auth_date"
|
|
||||||
KeyFirstName = "first_name"
|
|
||||||
KeyHash = "hash"
|
|
||||||
KeyID = "id"
|
|
||||||
KeyLastName = "last_name"
|
|
||||||
KeyPhotoURL = "photo_url"
|
|
||||||
KeyUsername = "username"
|
|
||||||
)
|
|
|
@ -1,9 +0,0 @@
|
||||||
package login
|
|
||||||
|
|
||||||
// App represents a widget which get and validate users authorizations.
|
|
||||||
type App struct{ SecretKey string }
|
|
||||||
|
|
||||||
// New create new app widget for validate authorizations with bot token as secret key.
|
|
||||||
func New(accessToken string) *App {
|
|
||||||
return &App{SecretKey: accessToken}
|
|
||||||
}
|
|
|
@ -1,42 +1,31 @@
|
||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import http "github.com/valyala/fasthttp"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// User contains data about authenticated user.
|
|
||||||
type User struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
AuthDate int64 `json:"auth_date"`
|
|
||||||
FirstName string `json:"first_name"`
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
LastName string `json:"last_name,omitempty"`
|
|
||||||
PhotoURL string `json:"photo_url,omitempty"`
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseUser create User structure from input url.Values.
|
// ParseUser create User structure from input url.Values.
|
||||||
func ParseUser(src url.Values) (u *User, err error) {
|
func ParseUser(data interface{}) (*User, error) {
|
||||||
u = new(User)
|
args := http.AcquireArgs()
|
||||||
|
defer http.ReleaseArgs(args)
|
||||||
|
|
||||||
var ad int
|
switch d := data.(type) {
|
||||||
ad, err = strconv.Atoi(src.Get(KeyAuthDate))
|
case *http.Args:
|
||||||
if err != nil {
|
d.CopyTo(args)
|
||||||
return
|
http.ReleaseArgs(d)
|
||||||
|
case []byte:
|
||||||
|
args.ParseBytes(d)
|
||||||
|
case string:
|
||||||
|
args.Parse(d)
|
||||||
|
default:
|
||||||
|
return nil, ErrUnsupportedType
|
||||||
}
|
}
|
||||||
|
|
||||||
u.ID, err = strconv.Atoi(src.Get(KeyID))
|
return &User{
|
||||||
if err != nil {
|
ID: args.GetUintOrZero(KeyID),
|
||||||
return
|
AuthDate: int64(args.GetUintOrZero(KeyAuthDate)),
|
||||||
}
|
FirstName: string(args.Peek(KeyFirstName)),
|
||||||
|
Hash: string(args.Peek(KeyHash)),
|
||||||
u.AuthDate = int64(ad)
|
LastName: string(args.Peek(KeyLastName)),
|
||||||
u.FirstName = src.Get(KeyFirstName)
|
PhotoURL: string(args.Peek(KeyPhotoURL)),
|
||||||
u.Hash = src.Get(KeyHash)
|
Username: string(args.Peek(KeyUsername)),
|
||||||
u.LastName = src.Get(KeyLastName)
|
}, nil
|
||||||
u.PhotoURL = src.Get(KeyPhotoURL)
|
|
||||||
u.Username = src.Get(KeyUsername)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package login
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// User contains data about authenticated user.
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
AuthDate int64 `json:"auth_date"`
|
||||||
|
FirstName string `json:"first_name"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
LastName string `json:"last_name,omitempty"`
|
||||||
|
PhotoURL string `json:"photo_url,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key represents available and supported query arguments keys.
|
||||||
|
const (
|
||||||
|
KeyAuthDate = "auth_date"
|
||||||
|
KeyFirstName = "first_name"
|
||||||
|
KeyHash = "hash"
|
||||||
|
KeyID = "id"
|
||||||
|
KeyLastName = "last_name"
|
||||||
|
KeyPhotoURL = "photo_url"
|
||||||
|
KeyUsername = "username"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrUserNotDefined describes error of an unassigned structure of user.
|
||||||
|
ErrUserNotDefined = errors.New("user is not defined")
|
||||||
|
|
||||||
|
// ErrEmptyToken describes error of an empty access token of the bot.
|
||||||
|
ErrEmptyToken = errors.New("empty bot access token")
|
||||||
|
|
||||||
|
// ErrUnsupportedType describes error of unsupported input data type for
|
||||||
|
// CheckAuthorization method.
|
||||||
|
ErrUnsupportedType = errors.New("unsupported data type")
|
||||||
|
)
|
|
@ -1,6 +1,10 @@
|
||||||
package login
|
package login
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
http "github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
// FullName return user first name only or full name if last name is present.
|
// FullName return user first name only or full name if last name is present.
|
||||||
func (user *User) FullName() string {
|
func (user *User) FullName() string {
|
||||||
|
@ -34,3 +38,25 @@ func (u *User) HasLastName() bool {
|
||||||
func (u *User) HasUsername() bool {
|
func (u *User) HasUsername() bool {
|
||||||
return u != nil && u.Username != ""
|
return u != nil && u.Username != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) toArgs() *http.Args {
|
||||||
|
args := http.AcquireArgs()
|
||||||
|
defer http.ReleaseArgs(args)
|
||||||
|
args.SetUint(KeyAuthDate, int(u.AuthDate))
|
||||||
|
args.Set(KeyFirstName, u.FirstName)
|
||||||
|
args.SetUint(KeyID, u.ID)
|
||||||
|
args.Set(KeyHash, u.Hash)
|
||||||
|
|
||||||
|
// Add optional values if exist
|
||||||
|
if u.LastName != "" {
|
||||||
|
args.Set(KeyLastName, u.LastName)
|
||||||
|
}
|
||||||
|
if u.PhotoURL != "" {
|
||||||
|
args.Set(KeyPhotoURL, u.PhotoURL)
|
||||||
|
}
|
||||||
|
if u.Username != "" {
|
||||||
|
args.Set(KeyUsername, u.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
Loading…
Reference in New Issue