|
|
@@ -6,56 +6,77 @@ import (
|
|
|
"encoding/base64"
|
|
|
"encoding/hex"
|
|
|
"encoding/json"
|
|
|
- "goseg/config"
|
|
|
"fmt"
|
|
|
- "time"
|
|
|
+ "goseg/config"
|
|
|
+ "goseg/structs"
|
|
|
+ "net"
|
|
|
+ "net/http"
|
|
|
+ "strings"
|
|
|
"sync"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/gorilla/websocket"
|
|
|
"golang.org/x/crypto/nacl/secretbox"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
- AuthenticatedClients = struct{
|
|
|
- Conns map[*websocket.Conn]bool
|
|
|
+ // maps a websocket conn to a tokenid
|
|
|
+ // tokenid's can be referenced from the global conf
|
|
|
+ AuthenticatedClients = struct {
|
|
|
+ Conns map[*websocket.Conn]string
|
|
|
+ sync.Mutex
|
|
|
+ }{
|
|
|
+ Conns: make(map[*websocket.Conn]string),
|
|
|
+ }
|
|
|
+ UnauthClients = struct {
|
|
|
+ Conns map[*websocket.Conn]string
|
|
|
sync.Mutex
|
|
|
}{
|
|
|
- Conns: make(map[*websocket.Conn]bool),
|
|
|
+ Conns: make(map[*websocket.Conn]string),
|
|
|
}
|
|
|
)
|
|
|
|
|
|
-// CheckToken checks the validity of the token.
|
|
|
-func CheckToken(token string, conn *websocket.Conn, setup bool) (bool, string, error) {
|
|
|
- if token == "" {
|
|
|
- authStatus := false
|
|
|
- if setup {
|
|
|
- authStatus = true
|
|
|
- }
|
|
|
- newToken, err := CreateToken(conn, setup)
|
|
|
- if err != nil {
|
|
|
- return false, "", err
|
|
|
- }
|
|
|
- return authStatus, newToken["token"], nil
|
|
|
- }
|
|
|
+// check if websocket session is in the auth map
|
|
|
+func WsIsAuthenticated(conn *websocket.Conn) bool {
|
|
|
+ AuthenticatedClients.Lock()
|
|
|
+ _, exists := AuthenticatedClients.Conns[conn]
|
|
|
+ AuthenticatedClients.Unlock()
|
|
|
+ return exists
|
|
|
+}
|
|
|
|
|
|
- // Token verification logic here...
|
|
|
- // ...
|
|
|
+// // CheckToken checks the validity of the token.
|
|
|
+// func CheckToken(token string, conn *websocket.Conn, setup bool) (bool, string, error) {
|
|
|
+// if token == "" {
|
|
|
+// authStatus := false
|
|
|
+// if setup {
|
|
|
+// authStatus = true
|
|
|
+// }
|
|
|
+// newToken, err := CreateToken(conn, setup)
|
|
|
+// if err != nil {
|
|
|
+// return false, "", err
|
|
|
+// }
|
|
|
+// return authStatus, newToken["token"], nil
|
|
|
+// }
|
|
|
|
|
|
- return false, "", nil
|
|
|
-}
|
|
|
+// return false, "", nil
|
|
|
+// }
|
|
|
|
|
|
// CreateToken creates a new session token.
|
|
|
-func CreateToken(conn *websocket.Conn, setup bool) (map[string]string, error) {
|
|
|
- // placeholder logic for IP and UserAgent
|
|
|
- ip := "localhost"
|
|
|
- userAgent := "lick"
|
|
|
- // if more properties are needed from websocket, you can extract them here
|
|
|
+func CreateToken(conn *websocket.Conn, r *http.Request, setup bool) (map[string]string, error) {
|
|
|
+ // extract conn info
|
|
|
+ var ip string
|
|
|
+ if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
|
|
|
+ ip = strings.Split(forwarded, ",")[0]
|
|
|
+ } else {
|
|
|
+ ip, _, _ = net.SplitHostPort(r.RemoteAddr)
|
|
|
+ }
|
|
|
+ userAgent := r.Header.Get("User-Agent")
|
|
|
conf := config.Conf()
|
|
|
- key := conf.KeyFile
|
|
|
+ now := time.Now().Format("2006-01-02_15:04:05")
|
|
|
// generate random strings for id, secret, and padding
|
|
|
- id := NewSecretString(32)
|
|
|
- secret := NewSecretString(128)
|
|
|
- padding := NewSecretString(32)
|
|
|
+ id := config.RandString(32)
|
|
|
+ secret := config.RandString(128)
|
|
|
+ padding := config.RandString(32)
|
|
|
contents := map[string]string{
|
|
|
"id": id,
|
|
|
"ip": ip,
|
|
|
@@ -63,42 +84,56 @@ func CreateToken(conn *websocket.Conn, setup bool) (map[string]string, error) {
|
|
|
"secret": secret,
|
|
|
"padding": padding,
|
|
|
"authorized": fmt.Sprintf("%v", setup),
|
|
|
- "created": time.Now().Format("2006-01-02_15:04:05"),
|
|
|
+ "created": now,
|
|
|
}
|
|
|
// encrypt the contents
|
|
|
+ key := conf.KeyFile
|
|
|
encryptedText, err := KeyfileEncrypt(contents, key)
|
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("failed to encrypt token: %v", err)
|
|
|
}
|
|
|
- // and hash
|
|
|
hashed := sha256.Sum256([]byte(encryptedText))
|
|
|
+ hash := hex.EncodeToString(hashed[:])
|
|
|
// Update sessions in the system's configuration
|
|
|
- now := time.Now().Format("2006-01-02_15:04:05")
|
|
|
- if setup {
|
|
|
- updates := map[string]interface{}{
|
|
|
+ AddSession(id, hash, now, setup)
|
|
|
+ return map[string]string{
|
|
|
+ "id": id,
|
|
|
+ "token": encryptedText,
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+// take session details and add to SysConfig
|
|
|
+func AddSession(tokenID string, hash string, created string, authorized bool) error {
|
|
|
+ session := structs.SessionInfo{
|
|
|
+ Hash: hash,
|
|
|
+ Created: created,
|
|
|
+ }
|
|
|
+ if authorized {
|
|
|
+ update := map[string]interface{}{
|
|
|
"Sessions": map[string]interface{}{
|
|
|
"Authorized": map[string]string{
|
|
|
- "Hash": hex.EncodeToString(hashed[:]),
|
|
|
- "Created": now,
|
|
|
+ "Hash": session.Hash,
|
|
|
+ "Created": session.Created,
|
|
|
},
|
|
|
},
|
|
|
}
|
|
|
- config.UpdateConf(updates)
|
|
|
+ if err := config.UpdateConf(update); err != nil {
|
|
|
+ return fmt.Errorf("Error adding session: %v", err)
|
|
|
+ }
|
|
|
} else {
|
|
|
- updates := map[string]interface{}{
|
|
|
+ update := map[string]interface{}{
|
|
|
"Sessions": map[string]interface{}{
|
|
|
"Unauthorized": map[string]string{
|
|
|
- "Hash": hex.EncodeToString(hashed[:]),
|
|
|
- "Created": now,
|
|
|
+ "Hash": session.Hash,
|
|
|
+ "Created": session.Created,
|
|
|
},
|
|
|
},
|
|
|
}
|
|
|
- config.UpdateConf(updates)
|
|
|
+ if err := config.UpdateConf(update); err != nil {
|
|
|
+ return fmt.Errorf("Error adding session: %v", err)
|
|
|
+ }
|
|
|
}
|
|
|
- return map[string]string{
|
|
|
- "id": id,
|
|
|
- "token": encryptedText,
|
|
|
- }, nil
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
// encrypt the contents using stored keyfile val
|
|
|
@@ -127,46 +162,38 @@ func KeyfileEncrypt(contents map[string]string, key string) (string, error) {
|
|
|
// decrypt routine
|
|
|
func KeyfileDecrypt(encryptedText string, key string) (map[string]string, error) {
|
|
|
// get bytes
|
|
|
- keyBytes := []byte(key)
|
|
|
- var keyArray [32]byte
|
|
|
- copy(keyArray[:], keyBytes)
|
|
|
- encryptedBytes, err := base64.URLEncoding.DecodeString(encryptedText)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
+ keyBytes := []byte(key)
|
|
|
+ var keyArray [32]byte
|
|
|
+ copy(keyArray[:], keyBytes)
|
|
|
+ encryptedBytes, err := base64.URLEncoding.DecodeString(encryptedText)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
// get nonce
|
|
|
- var nonce [24]byte
|
|
|
- copy(nonce[:], encryptedBytes[:24])
|
|
|
+ var nonce [24]byte
|
|
|
+ copy(nonce[:], encryptedBytes[:24])
|
|
|
// attempt decrypt
|
|
|
- decrypted, ok := secretbox.Open(nil, encryptedBytes[24:], &nonce, &keyArray)
|
|
|
- if !ok {
|
|
|
- return nil, fmt.Errorf("Decryption failed")
|
|
|
- }
|
|
|
- var contents map[string]string
|
|
|
- if err := json.Unmarshal(decrypted, &contents); err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- return contents, nil
|
|
|
-}
|
|
|
-
|
|
|
-// NewSecretString generates a random secret string of the given length.
|
|
|
-func NewSecretString(length int) string {
|
|
|
- randBytes := make([]byte, length)
|
|
|
- _, err := rand.Read(randBytes)
|
|
|
- if err != nil {
|
|
|
- return ""
|
|
|
+ decrypted, ok := secretbox.Open(nil, encryptedBytes[24:], &nonce, &keyArray)
|
|
|
+ if !ok {
|
|
|
+ return nil, fmt.Errorf("Decryption failed")
|
|
|
+ }
|
|
|
+ var contents map[string]string
|
|
|
+ if err := json.Unmarshal(decrypted, &contents); err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- return base64.URLEncoding.EncodeToString(randBytes)
|
|
|
+ return contents, nil
|
|
|
}
|
|
|
|
|
|
+// salted sha256
|
|
|
func Hasher(password string) string {
|
|
|
- conf := config.Conf()
|
|
|
- salt := conf.Salt
|
|
|
- toHash := password + salt
|
|
|
- res := sha256.Sum256([]byte(toHash))
|
|
|
- return hex.EncodeToString(res[:])
|
|
|
+ conf := config.Conf()
|
|
|
+ salt := conf.Salt
|
|
|
+ toHash := salt + password
|
|
|
+ res := sha256.Sum256([]byte(toHash))
|
|
|
+ return hex.EncodeToString(res[:])
|
|
|
}
|
|
|
|
|
|
+// check if pw matches sysconfig
|
|
|
func AuthenticateLogin(password string) bool {
|
|
|
conf := config.Conf()
|
|
|
if Hasher(password) == conf.PwHash {
|
|
|
@@ -175,10 +202,3 @@ func AuthenticateLogin(password string) bool {
|
|
|
return false
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-func WsIsAuthenticated(conn *websocket.Conn) bool {
|
|
|
- AuthenticatedClients.Lock()
|
|
|
- _, exists := AuthenticatedClients.Conns[conn]
|
|
|
- AuthenticatedClients.Unlock()
|
|
|
- return exists
|
|
|
-}
|