mirror of
https://github.com/cadmium-im/zirconium-go.git
synced 2024-11-23 10:52:24 +00:00
Implement user authorization (login)
This commit is contained in:
parent
123610d33b
commit
0f6d2ff3d9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/zr
|
/zr
|
||||||
|
/.config.toml
|
@ -16,8 +16,10 @@ func main() {
|
|||||||
var cfg core.Config
|
var cfg core.Config
|
||||||
var configPath string
|
var configPath string
|
||||||
var generateConfig bool
|
var generateConfig bool
|
||||||
|
|
||||||
|
defer logger.Init("auth-server", true, false, ioutil.Discard).Close() // TODO Make ability to use file for log output
|
||||||
flag.StringVar(&configPath, "config", "", "Path to config")
|
flag.StringVar(&configPath, "config", "", "Path to config")
|
||||||
flag.BoolVar(&generateConfig, "gen_config", false, "Generate the config")
|
flag.BoolVar(&generateConfig, "gen-config", false, "Generate the config")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if generateConfig == true {
|
if generateConfig == true {
|
||||||
sampleConfig := &core.Config{}
|
sampleConfig := &core.Config{}
|
||||||
|
@ -10,13 +10,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AppContext struct {
|
type AppContext struct {
|
||||||
router *Router
|
router *Router
|
||||||
authManager *AuthManager
|
authManager *AuthManager
|
||||||
connectionHandler *ConnectionHandler
|
sessionManager *SessionManager
|
||||||
websocketServer *WebsocketServer
|
websocketServer *WebsocketServer
|
||||||
cfg *Config
|
cfg *Config
|
||||||
database *mongo.Database
|
database *mongo.Database
|
||||||
userManager *UserManager
|
userManager *UserManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAppContext(cfg *Config) *AppContext {
|
func NewAppContext(cfg *Config) *AppContext {
|
||||||
@ -32,19 +32,22 @@ func NewAppContext(cfg *Config) *AppContext {
|
|||||||
}
|
}
|
||||||
appContext.router = router
|
appContext.router = router
|
||||||
|
|
||||||
authManager, err := NewAuthManager()
|
um, err := NewUserManager(appContext.database)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("Unable to initialize user manager: %s", err.Error())
|
||||||
|
}
|
||||||
|
appContext.userManager = um
|
||||||
|
|
||||||
|
authManager, err := NewAuthManager(um, cfg.ServerID, router)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("Unable to initialize authentication manager: %s", err.Error())
|
logger.Fatalf("Unable to initialize authentication manager: %s", err.Error())
|
||||||
}
|
}
|
||||||
appContext.authManager = authManager
|
appContext.authManager = authManager
|
||||||
|
|
||||||
um := NewUserManager(appContext.database)
|
sessionManager := NewSessionManager(router)
|
||||||
appContext.userManager = um
|
appContext.sessionManager = sessionManager
|
||||||
|
|
||||||
connHandler := NewConnectionHandler(router)
|
wss := NewWebsocketServer(cfg, sessionManager)
|
||||||
appContext.connectionHandler = connHandler
|
|
||||||
|
|
||||||
wss := NewWebsocketServer(cfg, connHandler)
|
|
||||||
appContext.websocketServer = wss
|
appContext.websocketServer = wss
|
||||||
|
|
||||||
return appContext
|
return appContext
|
||||||
@ -72,6 +75,5 @@ func (ac *AppContext) connectToDatabase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppContext) Run() error {
|
func (ac *AppContext) Run() error {
|
||||||
// TODO
|
return ac.websocketServer.Run()
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
81
core/auth_handler.go
Normal file
81
core/auth_handler.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/models"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/models/auth"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/utils"
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/google/logger"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthHandler struct {
|
||||||
|
serverID string
|
||||||
|
authManager *AuthManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthHandler(am *AuthManager, serverID string) *AuthHandler {
|
||||||
|
return &AuthHandler{
|
||||||
|
authManager: am,
|
||||||
|
serverID: serverID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *AuthHandler) HandleMessage(s *Session, message models.BaseMessage) {
|
||||||
|
var authRequest auth.AuthRequest
|
||||||
|
err := mapstructure.Decode(message.Payload, &authRequest)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch authRequest.Type {
|
||||||
|
case "urn:cadmium:auth:simple":
|
||||||
|
{
|
||||||
|
token, claims, err := ah.authManager.HandleSimpleAuth(authRequest.Fields["username"].(string), authRequest.Fields["password"].(string))
|
||||||
|
if err != nil {
|
||||||
|
if mongo.ErrNoDocuments == err {
|
||||||
|
msg := utils.PrepareErrorMessage(message, "auth-failed", "invalid username", ah.serverID)
|
||||||
|
_ = s.Send(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Errorf(err.Error())
|
||||||
|
msg := utils.PrepareMessageInternalServerError(message, err, ah.serverID)
|
||||||
|
_ = s.Send(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ar := auth.AuthResponse{
|
||||||
|
Token: token,
|
||||||
|
DeviceID: claims.DeviceID,
|
||||||
|
}
|
||||||
|
payload := structs.Map(ar)
|
||||||
|
msg := models.NewBaseMessage(message.ID, message.MessageType, ah.serverID, nil, true, payload)
|
||||||
|
_ = s.Send(msg)
|
||||||
|
s.Claims = claims
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "urn:cadmium:auth:token":
|
||||||
|
{
|
||||||
|
claims, err := ah.authManager.ValidateToken(authRequest.Fields["token"].(string))
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf(err.Error())
|
||||||
|
msg := utils.PrepareMessageInternalServerError(message, err, ah.serverID)
|
||||||
|
_ = s.Send(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Claims = claims
|
||||||
|
msg := models.NewBaseMessage(message.ID, message.MessageType, ah.serverID, nil, true, nil)
|
||||||
|
_ = s.Send(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *AuthHandler) IsAuthorizationRequired() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *AuthHandler) HandlingType() string {
|
||||||
|
return "urn:cadmium:auth"
|
||||||
|
}
|
@ -3,6 +3,8 @@ package core
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/alexedwards/argon2id"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/utils"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cadmium-im/zirconium-go/core/models"
|
"github.com/cadmium-im/zirconium-go/core/models"
|
||||||
@ -16,7 +18,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AuthManager struct {
|
type AuthManager struct {
|
||||||
signingKey string // For now it is random bytes string represented in Base64
|
serverID string
|
||||||
|
userManager *UserManager
|
||||||
|
signingKey string // For now it is random bytes string represented in Base64
|
||||||
}
|
}
|
||||||
|
|
||||||
type JWTCustomClaims struct {
|
type JWTCustomClaims struct {
|
||||||
@ -25,17 +29,22 @@ type JWTCustomClaims struct {
|
|||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthManager() (*AuthManager, error) {
|
func NewAuthManager(um *UserManager, serverID string, r *Router) (*AuthManager, error) {
|
||||||
am := &AuthManager{}
|
am := &AuthManager{
|
||||||
bytes, err := GenRandomBytes(SigningKeyBytesAmount)
|
userManager: um,
|
||||||
|
serverID: serverID,
|
||||||
|
}
|
||||||
|
bytes, err := utils.GenRandomBytes(SigningKeyBytesAmount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
am.signingKey = base64.RawStdEncoding.EncodeToString(bytes)
|
am.signingKey = base64.RawStdEncoding.EncodeToString(bytes)
|
||||||
|
ah := NewAuthHandler(am, serverID)
|
||||||
|
r.RegisterC2SHandler(ah)
|
||||||
return am, nil
|
return am, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *AuthManager) CreateNewToken(entityID *models.EntityID, deviceID string, tokenExpireTimeDuration time.Duration) (string, error) {
|
func (am *AuthManager) CreateNewToken(entityID *models.EntityID, deviceID string, tokenExpireTimeDuration time.Duration) (*JWTCustomClaims, string, error) {
|
||||||
timeNow := time.Now()
|
timeNow := time.Now()
|
||||||
expiringTime := timeNow.Add(tokenExpireTimeDuration)
|
expiringTime := timeNow.Add(tokenExpireTimeDuration)
|
||||||
|
|
||||||
@ -59,9 +68,9 @@ func (am *AuthManager) CreateNewToken(entityID *models.EntityID, deviceID string
|
|||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
tokenString, err := token.SignedString(am.signingKey)
|
tokenString, err := token.SignedString(am.signingKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
return tokenString, nil
|
return &claims, tokenString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *AuthManager) ValidateToken(tokenString string) (*JWTCustomClaims, error) {
|
func (am *AuthManager) ValidateToken(tokenString string) (*JWTCustomClaims, error) {
|
||||||
@ -84,3 +93,28 @@ func (am *AuthManager) ValidateToken(tokenString string) (*JWTCustomClaims, erro
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *AuthManager) HandleSimpleAuth(username string, pass string) (string, *JWTCustomClaims, error) {
|
||||||
|
user, err := am.userManager.GetByUsername(username)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
match, err := argon2id.ComparePasswordAndHash(pass, user.PasswordHash)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
return "", nil, fmt.Errorf("incorrect password")
|
||||||
|
}
|
||||||
|
|
||||||
|
eid, err := models.NewEntityID("@", user.UUID, am.serverID)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
claims, token, err := am.CreateNewToken(eid, "ABCDEF", TokenExpireTimeDuration)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, claims, nil
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ type BaseMessage struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
MessageType string `json:"type"`
|
MessageType string `json:"type"`
|
||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
To []string `json:"to"`
|
To []string `json:"to,omitempty"`
|
||||||
Ok bool `json:"ok"`
|
Ok bool `json:"ok"`
|
||||||
Payload map[string]interface{} `json:"payload"`
|
Payload map[string]interface{} `json:"payload"`
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type ProtocolError struct {
|
type ProtocolError struct {
|
||||||
ErrCode string `json:"code"`
|
ErrCode string `structs:"code"`
|
||||||
ErrText string `json:"text"`
|
ErrText string `structs:"text"`
|
||||||
ErrPayload map[string]interface{} `json:"payload"`
|
ErrPayload map[string]interface{} `structs:"payload,omitempty"`
|
||||||
}
|
}
|
||||||
|
10
core/models/user.go
Normal file
10
core/models/user.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID primitive.ObjectID `json:"-" bson:"_id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
PasswordHash string `json:"-" bson:"passwordHash"`
|
||||||
|
}
|
@ -2,13 +2,14 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cadmium-im/zirconium-go/core/models"
|
"github.com/cadmium-im/zirconium-go/core/models"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/utils"
|
||||||
|
"github.com/fatih/structs"
|
||||||
"github.com/google/logger"
|
"github.com/google/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
appContext *AppContext
|
appContext *AppContext
|
||||||
handlers map[string][]C2SMessageHandler
|
handlers map[string][]C2SMessageHandler
|
||||||
connections []*Session
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type C2SMessageHandler interface {
|
type C2SMessageHandler interface {
|
||||||
@ -30,10 +31,10 @@ func (r *Router) RouteMessage(origin *Session, message models.BaseMessage) {
|
|||||||
if handlers != nil {
|
if handlers != nil {
|
||||||
for _, v := range handlers {
|
for _, v := range handlers {
|
||||||
if v.IsAuthorizationRequired() {
|
if v.IsAuthorizationRequired() {
|
||||||
if len(origin.entityID) == 0 {
|
if origin.Claims == nil {
|
||||||
logger.Warningf("Connection %s isn't authorized", origin.connID)
|
logger.Warningf("Connection %s isn't authorized", origin.connID)
|
||||||
|
|
||||||
msg := PrepareMessageUnauthorized(message, r.appContext.cfg.ServerDomains[0]) // fixme: domain
|
msg := utils.PrepareMessageUnauthorized(message, r.appContext.cfg.ServerDomains[0]) // fixme: domain
|
||||||
_ = origin.Send(msg)
|
_ = origin.Send(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +46,7 @@ func (r *Router) RouteMessage(origin *Session, message models.BaseMessage) {
|
|||||||
ErrText: "Server doesn't implement message type " + message.MessageType,
|
ErrText: "Server doesn't implement message type " + message.MessageType,
|
||||||
ErrPayload: make(map[string]interface{}),
|
ErrPayload: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
errMsg := models.NewBaseMessage(message.ID, message.MessageType, r.appContext.cfg.ServerID, []string{message.From}, false, StructToMap(protocolError))
|
errMsg := models.NewBaseMessage(message.ID, message.MessageType, r.appContext.cfg.ServerID, []string{message.From}, false, structs.Map(protocolError))
|
||||||
logger.Infof("Drop message with type %s because server hasn't proper handlers", message.MessageType)
|
logger.Infof("Drop message with type %s because server hasn't proper handlers", message.MessageType)
|
||||||
_ = origin.Send(errMsg)
|
_ = origin.Send(errMsg)
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
wsConn *websocket.Conn
|
wsConn *websocket.Conn
|
||||||
connID string
|
connID string
|
||||||
entityID []*models.EntityID
|
Claims *JWTCustomClaims
|
||||||
deviceID *string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Send(message models.BaseMessage) error {
|
func (s *Session) Send(message models.BaseMessage) error {
|
||||||
|
@ -7,24 +7,23 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConnectionHandler struct {
|
type SessionManager struct {
|
||||||
router *Router
|
router *Router
|
||||||
connections map[string]*Session
|
connections map[string]*Session
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnectionHandler(r *Router) *ConnectionHandler {
|
func NewSessionManager(r *Router) *SessionManager {
|
||||||
return &ConnectionHandler{
|
return &SessionManager{
|
||||||
router: r,
|
router: r,
|
||||||
connections: make(map[string]*Session),
|
connections: make(map[string]*Session),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *ConnectionHandler) HandleNewConnection(wsocket *websocket.Conn) {
|
func (ch *SessionManager) HandleNewConnection(wsocket *websocket.Conn) {
|
||||||
randomUUID, _ := uuid.NewRandom()
|
randomUUID := uuid.New().String()
|
||||||
uuidStr := randomUUID.String()
|
|
||||||
o := &Session{
|
o := &Session{
|
||||||
wsConn: wsocket,
|
wsConn: wsocket,
|
||||||
connID: uuidStr,
|
connID: randomUUID,
|
||||||
}
|
}
|
||||||
ch.connections[o.connID] = o
|
ch.connections[o.connID] = o
|
||||||
go func() {
|
go func() {
|
@ -1,6 +1,15 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import "go.mongodb.org/mongo-driver/mongo"
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/models"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/utils"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UsersCollectionName = "users"
|
UsersCollectionName = "users"
|
||||||
@ -10,12 +19,52 @@ type UserManager struct {
|
|||||||
usersCol *mongo.Collection
|
usersCol *mongo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserManager(db *mongo.Database) *UserManager {
|
func NewUserManager(db *mongo.Database) (*UserManager, error) {
|
||||||
col := db.Collection(UsersCollectionName)
|
col := db.Collection(UsersCollectionName)
|
||||||
|
|
||||||
um := &UserManager{
|
um := &UserManager{
|
||||||
usersCol: col,
|
usersCol: col,
|
||||||
}
|
}
|
||||||
|
|
||||||
return um
|
err := um.initMongo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return um, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *UserManager) initMongo() error {
|
||||||
|
usernameIndex := mongo.IndexModel{
|
||||||
|
Keys: bson.M{
|
||||||
|
"username": 1,
|
||||||
|
}, Options: options.Index().SetUnique(true),
|
||||||
|
}
|
||||||
|
|
||||||
|
exists, err := utils.IsCollectionExists(context.Background(), um.usersCol.Database(), um.usersCol.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
_, err := um.usersCol.Indexes().DropAll(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = um.usersCol.Indexes().CreateMany(context.Background(), []mongo.IndexModel{usernameIndex})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *UserManager) SaveUser(user *models.User) error {
|
||||||
|
user.ID = primitive.NewObjectID()
|
||||||
|
user.UUID = uuid.New().String()
|
||||||
|
_, err := um.usersCol.InsertOne(context.Background(), user)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *UserManager) GetByUsername(username string) (*models.User, error) {
|
||||||
|
var user models.User
|
||||||
|
err := um.usersCol.FindOne(context.Background(), bson.D{{"username", username}}).Decode(&user)
|
||||||
|
return &user, err
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/cadmium-im/zirconium-go/core/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GenRandomBytes(size int) (blk []byte, err error) {
|
|
||||||
blk = make([]byte, size)
|
|
||||||
_, err = rand.Read(blk)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func StructToMap(item interface{}) map[string]interface{} {
|
|
||||||
res := map[string]interface{}{}
|
|
||||||
if item == nil {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
v := reflect.TypeOf(item)
|
|
||||||
reflectValue := reflect.ValueOf(item)
|
|
||||||
reflectValue = reflect.Indirect(reflectValue)
|
|
||||||
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
for i := 0; i < v.NumField(); i++ {
|
|
||||||
tag := v.Field(i).Tag.Get("json")
|
|
||||||
field := reflectValue.Field(i).Interface()
|
|
||||||
if tag != "" && tag != "-" {
|
|
||||||
if v.Field(i).Type.Kind() == reflect.Struct {
|
|
||||||
res[tag] = StructToMap(field)
|
|
||||||
} else {
|
|
||||||
res[tag] = field
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func PrepareMessageUnauthorized(msg models.BaseMessage, serverDomain string) models.BaseMessage {
|
|
||||||
protocolError := models.ProtocolError{
|
|
||||||
ErrCode: "unauthorized",
|
|
||||||
ErrText: "Unauthorized access",
|
|
||||||
ErrPayload: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
errMsg := models.NewBaseMessage(msg.ID, msg.MessageType, serverDomain, []string{msg.From}, false, StructToMap(protocolError))
|
|
||||||
return errMsg
|
|
||||||
}
|
|
91
core/utils/utils.go
Normal file
91
core/utils/utils.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"github.com/alexedwards/argon2id"
|
||||||
|
"github.com/cadmium-im/zirconium-go/core/models"
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrPasswordIsEmpty = fmt.Errorf("the password is empty")
|
||||||
|
ErrPasswordIsTooShort = fmt.Errorf("the password is too short")
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenRandomBytes(size int) (blk []byte, err error) {
|
||||||
|
blk = make([]byte, size)
|
||||||
|
_, err = rand.Read(blk)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrepareMessageUnauthorized(msg models.BaseMessage, serverDomain string) models.BaseMessage {
|
||||||
|
protocolError := models.ProtocolError{
|
||||||
|
ErrCode: "unauthorized",
|
||||||
|
ErrText: "Unauthorized access",
|
||||||
|
ErrPayload: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
errMsg := models.NewBaseMessage(msg.ID, msg.MessageType, serverDomain, []string{msg.From}, false, structs.Map(protocolError))
|
||||||
|
return errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrepareMessageInternalServerError(msg models.BaseMessage, err error, serverID string) models.BaseMessage {
|
||||||
|
protocolError := models.ProtocolError{
|
||||||
|
ErrCode: "internal-server-error",
|
||||||
|
ErrText: err.Error(),
|
||||||
|
ErrPayload: nil,
|
||||||
|
}
|
||||||
|
var to []string
|
||||||
|
if msg.From != "" {
|
||||||
|
to = append(to, msg.From)
|
||||||
|
}
|
||||||
|
errMsg := models.NewBaseMessage(msg.ID, msg.MessageType, serverID, to, false, structs.Map(protocolError))
|
||||||
|
return errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrepareErrorMessage(msg models.BaseMessage, errorType string, errorText string, serverID string) models.BaseMessage {
|
||||||
|
protocolError := models.ProtocolError{
|
||||||
|
ErrCode: errorType,
|
||||||
|
ErrText: errorText,
|
||||||
|
ErrPayload: nil,
|
||||||
|
}
|
||||||
|
var to []string
|
||||||
|
if msg.From != "" {
|
||||||
|
to = append(to, msg.From)
|
||||||
|
}
|
||||||
|
errMsg := models.NewBaseMessage(msg.ID, msg.MessageType, serverID, to, false, structs.Map(protocolError))
|
||||||
|
return errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashPassword(password string) (string, error) {
|
||||||
|
return argon2id.CreateHash(password, argon2id.DefaultParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidatePassword(password string) error {
|
||||||
|
if password == "" {
|
||||||
|
return ErrPasswordIsEmpty
|
||||||
|
}
|
||||||
|
if len(password) < 4 {
|
||||||
|
return ErrPasswordIsTooShort
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCollectionExists(ctx context.Context, db *mongo.Database, collectionName string) (bool, error) {
|
||||||
|
isExists := false
|
||||||
|
names, err := db.ListCollectionNames(ctx, bson.D{{"name", collectionName}})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
if name == collectionName {
|
||||||
|
isExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isExists, nil
|
||||||
|
}
|
@ -15,15 +15,15 @@ var wsUpgrader = websocket.Upgrader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebsocketServer struct {
|
type WebsocketServer struct {
|
||||||
r *mux.Router
|
r *mux.Router
|
||||||
connHandler *ConnectionHandler
|
sessionManager *SessionManager
|
||||||
cfg *Config
|
cfg *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebsocketServer(cfg *Config, connHandler *ConnectionHandler) *WebsocketServer {
|
func NewWebsocketServer(cfg *Config, sessionManager *SessionManager) *WebsocketServer {
|
||||||
wss := &WebsocketServer{}
|
wss := &WebsocketServer{}
|
||||||
|
|
||||||
wss.connHandler = connHandler
|
wss.sessionManager = sessionManager
|
||||||
wss.cfg = cfg
|
wss.cfg = cfg
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
wss.r = r
|
wss.r = r
|
||||||
@ -36,7 +36,7 @@ func NewWebsocketServer(cfg *Config, connHandler *ConnectionHandler) *WebsocketS
|
|||||||
logger.Errorf(err.Error())
|
logger.Errorf(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wss.connHandler.HandleNewConnection(ws)
|
wss.sessionManager.HandleNewConnection(ws)
|
||||||
})
|
})
|
||||||
|
|
||||||
return wss
|
return wss
|
||||||
|
4
go.mod
4
go.mod
@ -3,12 +3,14 @@ module github.com/cadmium-im/zirconium-go
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/alexedwards/argon2id v0.0.0-20210326052512-e2135f7c9c77 // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
github.com/google/logger v1.0.1
|
github.com/google/logger v1.0.1
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
github.com/gorilla/websocket v1.4.1
|
github.com/gorilla/websocket v1.4.1
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.7.0
|
github.com/pelletier/go-toml v1.7.0
|
||||||
go.mongodb.org/mongo-driver v1.5.1
|
go.mongodb.org/mongo-driver v1.5.1
|
||||||
)
|
)
|
||||||
|
11
go.sum
11
go.sum
@ -1,5 +1,7 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/alexedwards/argon2id v0.0.0-20210326052512-e2135f7c9c77 h1:X6U+/fhTYeDYS3sN4xHcoORJhhar+zSgrNeraapuRK4=
|
||||||
|
github.com/alexedwards/argon2id v0.0.0-20210326052512-e2135f7c9c77/go.mod h1:Kmn5t2Rb93Q4NTprN4+CCgARGvigKMJyxP0WckpTUp0=
|
||||||
github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk=
|
github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk=
|
||||||
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -7,6 +9,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
@ -65,6 +69,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
|
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
|
||||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
@ -105,6 +111,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||||
@ -122,6 +130,9 @@ golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s=
|
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s=
|
||||||
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
Loading…
Reference in New Issue
Block a user