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

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
}