You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
4.9 KiB
Go
197 lines
4.9 KiB
Go
package login
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt"
|
|
"github.com/google/uuid"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type Login struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type User struct {
|
|
Username string `json:"username"`
|
|
Session string `json:"session"`
|
|
HashedPassword []byte `json:"hashedPassword"`
|
|
}
|
|
|
|
var JwtSecret = os.Getenv("secret")
|
|
var Address = "127.0.0.1:8001"
|
|
var DbAddress = "http://127.0.0.1:8002"
|
|
|
|
func decodeLogin(w *http.ResponseWriter, r **http.Request) (login Login, ok bool) {
|
|
(*w).Header().Set("Access-Control-Allow-Origin", "*")
|
|
if (*r).Method == "OPTIONS" {
|
|
return
|
|
}
|
|
if (*r).Method != "POST" {
|
|
(*w).WriteHeader(http.StatusMethodNotAllowed)
|
|
return Login{}, false
|
|
}
|
|
|
|
if err := json.NewDecoder((*r).Body).Decode(&login); err != nil {
|
|
fmt.Println("Error decoding login", err.Error())
|
|
(*w).WriteHeader(http.StatusBadRequest)
|
|
(*w).Write([]byte(`{"error":"malformed login"}`))
|
|
return Login{}, false
|
|
}
|
|
|
|
return login, true
|
|
}
|
|
|
|
func validateRegistration(login Login) (message string, ok bool) {
|
|
const (
|
|
numLowerBound = 48
|
|
numUpperBound = 57
|
|
lcAlphaLowerBound = 97
|
|
lcAlphaUpperBound = 122
|
|
ucAlphaLowerBound = 65
|
|
ucAlphaUpperBound = 90
|
|
)
|
|
|
|
if len(login.Password) < 8 {
|
|
message = `{"error":"password too short"}`
|
|
return
|
|
}
|
|
|
|
var lcAlphaCount, ucAlphaCount, numCount, specialCount int
|
|
for _, char := range login.Password {
|
|
intVal := int(char)
|
|
if numLowerBound <= intVal && intVal <= numUpperBound {
|
|
numCount++
|
|
} else if lcAlphaLowerBound <= intVal && intVal <= lcAlphaUpperBound {
|
|
lcAlphaCount++
|
|
} else if ucAlphaLowerBound <= intVal && intVal <= ucAlphaUpperBound {
|
|
ucAlphaCount++
|
|
} else {
|
|
specialCount++
|
|
}
|
|
}
|
|
|
|
if lcAlphaCount == 0 || ucAlphaCount == 0 || numCount == 0 || specialCount == 0 {
|
|
message = `{"error":"password failed criteria"}`
|
|
return
|
|
}
|
|
return "", true
|
|
}
|
|
|
|
func hashPassword(password string) (hash []byte, err error) {
|
|
hash, err = bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "Error hashing password", err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
func checkBody(res **http.Response) (mess []byte, ok bool) {
|
|
body, err := ioutil.ReadAll((*res).Body)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "error reading response body:", err)
|
|
return nil, false
|
|
}
|
|
if (*res).StatusCode != http.StatusOK {
|
|
fmt.Println(string(body))
|
|
return body, false
|
|
}
|
|
return body, true
|
|
}
|
|
|
|
func encodeJson(data interface{}) ([]byte, bool) {
|
|
if jsonData, err := json.Marshal(data); err != nil {
|
|
fmt.Fprintln(os.Stderr, "Could not marshal json data", data)
|
|
return nil, false
|
|
} else {
|
|
return jsonData, true
|
|
}
|
|
}
|
|
|
|
func createJWT(username string) (string, error) {
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
"exp": time.Now().Add(5 * time.Minute).Unix(),
|
|
"username": username,
|
|
})
|
|
|
|
tokenString, err := token.SignedString([]byte(JwtSecret))
|
|
return tokenString, err
|
|
}
|
|
|
|
func newSession(username string) (jwt, sessionId string, err error) {
|
|
jwt, err = createJWT(username)
|
|
return jwt, uuid.NewString(), err
|
|
}
|
|
|
|
func storeSessionToken(username, token string) (message string, ok bool) {
|
|
user := User{username, token, nil}
|
|
jsonData, ok := encodeJson(user)
|
|
if !ok {
|
|
return "error encoding json", false
|
|
}
|
|
res, err := http.Post(DbAddress+"/session", "application/json", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "could not reach db server:", err)
|
|
return "could not reach db server", false
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
return "could not decode response body", false
|
|
}
|
|
|
|
if err != nil || res.StatusCode != http.StatusOK {
|
|
return string(body), false
|
|
}
|
|
|
|
return "", true
|
|
}
|
|
|
|
func retrieveSessionToken(username string) (token, message string, ok bool) {
|
|
res, err := http.Get(DbAddress + "/session?username=" + username)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "could not reach db server:", err)
|
|
return "", "could not reach db server", false
|
|
}
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
|
|
if err != nil || res.StatusCode != http.StatusOK {
|
|
return "", string(body), false
|
|
}
|
|
|
|
var user User
|
|
if err := json.Unmarshal(body, &user); err != nil {
|
|
return "", err.Error(), false
|
|
}
|
|
|
|
return user.Session, "", true
|
|
}
|
|
|
|
func getUserBySession(session string) (user User, ok bool) {
|
|
res, err := http.Get(DbAddress + "/user?session=" + session)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "error reaching db server", err.Error())
|
|
return User{}, false
|
|
}
|
|
if res.StatusCode != http.StatusOK {
|
|
return User{}, false
|
|
}
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "could not read response body:", err)
|
|
return User{}, false
|
|
}
|
|
if err := json.Unmarshal(body, &user); err != nil {
|
|
fmt.Fprintln(os.Stderr, "unmarhsal user error", err.Error())
|
|
return user, false
|
|
}
|
|
return user, true
|
|
}
|