1
0
Fork 0

🏗️ Login package refactor

This commit is contained in:
Maxim Lebedev 2018-04-12 18:13:56 +05:00
parent 90a7dec4be
commit 29c6ef9fd6
No known key found for this signature in database
GPG Key ID: F8978F46FF0FFA4F
5 changed files with 104 additions and 49 deletions

View File

@ -4,34 +4,40 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"errors"
"net/url"
"strconv"
)
var ErrUserNotDefined = errors.New("user is not defined")
// CheckAuthorization verify the authentication and the integrity of the data
// received by comparing the received hash parameter with the hexadecimal
// 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.
func (user *User) CheckAuthorization(botToken string) (bool, error) {
dataCheckString := fmt.Sprint(
"auth_date=", user.AuthDate.Unix(),
"\n", "first_name=", user.FirstName,
// Eliminate 'hash' to avoid recursion and incorrect data validation.
"\n", "id=", user.ID,
)
func (app *App) CheckAuthorization(user *User) (bool, error) {
if user == nil {
return false, ErrUserNotDefined
}
dataCheck := make(url.Values)
dataCheck.Add(KeyAuthDate, string(user.AuthDate))
dataCheck.Add(KeyFirstName, user.FirstName)
dataCheck.Add(KeyID, strconv.Itoa(user.ID))
// Add optional values if exist
if user.LastName != "" {
dataCheckString += fmt.Sprint("\n", "last_name=", user.LastName)
dataCheck.Add(KeyLastName, user.LastName)
}
if user.PhotoURL != "" {
dataCheckString += fmt.Sprint("\n", "photo_url=", user.PhotoURL)
dataCheck.Add(KeyPhotoURL, user.PhotoURL)
}
if user.Username != "" {
dataCheckString += fmt.Sprint("\n", "username=", user.Username)
dataCheck.Add(KeyUsername, user.Username)
}
secretKey := sha256.Sum256([]byte(botToken))
secretKey := sha256.Sum256([]byte(app.SecretKey))
hash := hmac.New(sha256.New, secretKey[0:])
_, err := hash.Write([]byte(dataCheckString))
_, err := hash.Write([]byte(dataCheck.Encode()))
return hex.EncodeToString(hash.Sum(nil)) == user.Hash, err
}

11
login/constants.go Normal file
View File

@ -0,0 +1,11 @@
package login
const (
KeyAuthDate = "auth_date"
KeyFirstName = "first_name"
KeyHash = "hash"
KeyID = "id"
KeyLastName = "last_name"
KeyPhotoURL = "photo_url"
KeyUsername = "username"
)

View File

@ -1,41 +1,11 @@
package login
import (
"net/url"
"strconv"
"time"
)
// User contains data about authenticated user.
type User struct {
AuthDate time.Time `json:"auth_date"`
FirstName string `json:"first_name"`
Hash string `json:"hash"`
ID int `json:"id"`
LastName string `json:"last_name,omitempty"`
PhotoURL string `json:"photo_url,omitempty"`
Username string `json:"username,omitempty"`
// App represents a widget which get and validate users authorizations.
type App struct {
SecretKey string
}
// New create User structure from input url.Values.
func New(src url.Values) (*User, error) {
authDate, err := strconv.Atoi(src.Get("auth_date"))
if err != nil {
return nil, err
}
id, err := strconv.Atoi(src.Get("id"))
if err != nil {
return nil, err
}
return &User{
AuthDate: time.Unix(int64(authDate), 0),
FirstName: src.Get("first_name"),
Hash: src.Get("hash"),
ID: id,
LastName: src.Get("last_name"),
PhotoURL: src.Get("photo_url"),
Username: src.Get("username"),
}, nil
// New create new app widget for validate authorizations with bot token as secret key.
func New(botToken string) *App {
return &App{SecretKey: botToken}
}

40
login/parse_user.go Normal file
View File

@ -0,0 +1,40 @@
package login
import (
"net/url"
"strconv"
)
// User contains data about authenticated user.
type User struct {
AuthDate int64 `json:"auth_date"`
FirstName string `json:"first_name"`
Hash string `json:"hash"`
ID int `json:"id"`
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.
func ParseUser(src url.Values) (*User, error) {
authDate, err := strconv.Atoi(src.Get(KeyAuthDate))
if err != nil {
return nil, err
}
id, err := strconv.Atoi(src.Get(KeyID))
if err != nil {
return nil, err
}
return &User{
AuthDate: int64(authDate),
FirstName: src.Get(KeyFirstName),
Hash: src.Get(KeyHash),
ID: id,
LastName: src.Get(KeyLastName),
PhotoURL: src.Get(KeyPhotoURL),
Username: src.Get(KeyUsername),
}, nil
}

28
login/utils.go Normal file
View File

@ -0,0 +1,28 @@
package login
import (
"fmt"
"time"
)
// FullName return user first name only or full name if last name is present.
func (user *User) FullName() string {
if user == nil {
return ""
}
if user.LastName != "" {
return fmt.Sprintln(user.FirstName, user.LastName)
}
return user.FirstName
}
// AuthTime convert AuthDate field into time.Time.
func (user *User) AuthTime() time.Time {
if user == nil {
return time.Time{}
}
return time.Unix(user.AuthDate, 0)
}