prepare user system

This commit is contained in:
SowinskiBraeden committed 2023-09-28 21:42:58 -07:00
1 parent ecf4fabd86
commit 5bc4adceea
13 files changed
+470 -18

No files matched your search

+44 -2
View File
@@ -1,14 +1,20 @@
package handlers package handlers
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log"
"net/http" "net/http"
"time"
"github.com/SowinskiBraeden/DeviceBookingAPI/api" "github.com/SowinskiBraeden/DeviceBookingAPI/api"
"github.com/SowinskiBraeden/DeviceBookingAPI/models" "github.com/SowinskiBraeden/DeviceBookingAPI/models"
"github.com/SowinskiBraeden/DeviceBookingAPI/util"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"go.mongodb.org/mongo-driver/bson"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/SowinskiBraeden/DeviceBookingAPI/config" "github.com/SowinskiBraeden/DeviceBookingAPI/config"
@@ -27,9 +33,14 @@ type App struct {
// New creates a new mux router and all the routes // New creates a new mux router and all the routes
func (a *App) New() *mux.Router { func (a *App) New() *mux.Router {
// Detect if system is new and needs default admin
a.newSystem()
r := mux.NewRouter() r := mux.NewRouter()
cow := Cow{DB: databases.NewCowDatabase(a.dbHelper)} cow := Cow{DB: databases.NewCowDatabase(a.dbHelper)}
device := Device{DB: databases.NewDeviceDatabase(a.dbHelper)} device := Device{DB: databases.NewDeviceDatabase(a.dbHelper)}
// user := User{DB: databases.NewUserDatabase(a.dbHelper)}
// healthcheck // healthcheck
r.HandleFunc("/health", healthCheckHandler) r.HandleFunc("/health", healthCheckHandler)
@@ -37,7 +48,7 @@ func (a *App) New() *mux.Router {
apiCreate := r.PathPrefix("/api/v1").Subrouter() apiCreate := r.PathPrefix("/api/v1").Subrouter()
// Data handlers, create, delete, update etc. // Data handlers, create, delete, update etc.
apiCreate.Handle("/cow/{cow_id}", api.Middleware(http.HandlerFunc(cow.CowByIDHandler))).Methods("GET") // By Object ID not Cow Name apiCreate.Handle("/cow/{cow_id}", api.Middleware(http.HandlerFunc(cow.CowByObjectIDHandler))).Methods("GET") // By Object ID not Cow Name
apiCreate.Handle("/cows", api.Middleware(http.HandlerFunc(cow.CowHandler))).Methods("GET") // Returns all cows apiCreate.Handle("/cows", api.Middleware(http.HandlerFunc(cow.CowHandler))).Methods("GET") // Returns all cows
apiCreate.Handle("/cows", api.Middleware(http.HandlerFunc(cow.CowHandlerQuery))).Methods("POST") // Returns list of cows based of name query apiCreate.Handle("/cows", api.Middleware(http.HandlerFunc(cow.CowHandlerQuery))).Methods("POST") // Returns list of cows based of name query
apiCreate.Handle("/cows/new", api.Middleware(http.HandlerFunc(cow.NewCowHandler))).Methods("POST") // Create new cow apiCreate.Handle("/cows/new", api.Middleware(http.HandlerFunc(cow.NewCowHandler))).Methods("POST") // Create new cow
@@ -46,7 +57,7 @@ func (a *App) New() *mux.Router {
apiCreate.Handle("/cows/get_devices/{cow_id}", api.Middleware(http.HandlerFunc(device.GetChildDevices))).Methods("POST") // Returns a list of devices from a given Cow obj apiCreate.Handle("/cows/get_devices/{cow_id}", api.Middleware(http.HandlerFunc(device.GetChildDevices))).Methods("POST") // Returns a list of devices from a given Cow obj
apiCreate.Handle("cows/bookings/{cow_id}", api.Middleware(http.HandlerFunc(cow.GetBookingsHandler))).Methods("GET") // Returns all bookings for a given cow apiCreate.Handle("cows/bookings/{cow_id}", api.Middleware(http.HandlerFunc(cow.GetBookingsHandler))).Methods("GET") // Returns all bookings for a given cow
apiCreate.Handle("/device/{device_id}", api.Middleware(http.HandlerFunc(device.DeviceByIDHandler))).Methods("GET") // By Object ID not Device Name apiCreate.Handle("/device/{device_id}", api.Middleware(http.HandlerFunc(device.DeviceByObjectIDHandler))).Methods("GET") // By Object ID not Device Name
apiCreate.Handle("/devices", api.Middleware(http.HandlerFunc(device.DeviceHandler))).Methods("GET") // Returns all devices apiCreate.Handle("/devices", api.Middleware(http.HandlerFunc(device.DeviceHandler))).Methods("GET") // Returns all devices
apiCreate.Handle("/devices", api.Middleware(http.HandlerFunc(device.DeviceHandlerQuery))).Methods("POST") // Returns list of devices based of name query apiCreate.Handle("/devices", api.Middleware(http.HandlerFunc(device.DeviceHandlerQuery))).Methods("POST") // Returns list of devices based of name query
apiCreate.Handle("/devices/new", api.Middleware(http.HandlerFunc(device.NewDeviceHandler))).Methods("POST") // create new device apiCreate.Handle("/devices/new", api.Middleware(http.HandlerFunc(device.NewDeviceHandler))).Methods("POST") // create new device
@@ -93,3 +104,34 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
}) })
_, _ = io.WriteString(w, string(b)) _, _ = io.WriteString(w, string(b))
} }
func (a *App) newSystem() {
var DB databases.UserDatabase = databases.NewUserDatabase(a.dbHelper)
dbResp, err := DB.Find(context.TODO(), bson.M{})
if err != nil {
zap.S().With(err).Error("Unable to detect new system: failed to get users")
}
if len(dbResp) == 0 {
fmt.Println("Admin account setup...")
for {
defaultAdmin := util.CreateDefaultAdmin(DB)
if util.Confirm("Are the above credentials correct?") {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, err := DB.InsertOne(ctx, defaultAdmin)
if err != nil {
log.Printf("Failed to create an admin\n")
break
}
log.Printf("Successfully created default admin")
log.Printf("Your default admin ID is %s", defaultAdmin.Details.UID)
break
}
}
}
}
+1 -1
View File
@@ -77,7 +77,7 @@ func (c Cow) CowHandlerQuery(w http.ResponseWriter, r *http.Request) {
} }
// CowByIDHandler returns a cow by ID // CowByIDHandler returns a cow by ID
func (c Cow) CowByIDHandler(w http.ResponseWriter, r *http.Request) { func (c Cow) CowByObjectIDHandler(w http.ResponseWriter, r *http.Request) {
cowID := mux.Vars(r)["cow_id"] cowID := mux.Vars(r)["cow_id"]
cID, err := primitive.ObjectIDFromHex(cowID) cID, err := primitive.ObjectIDFromHex(cowID)
+4 -4
View File
@@ -79,12 +79,12 @@ func (d Device) DeviceHandlerQuery(w http.ResponseWriter, r *http.Request) {
} }
// DeviceByIDHandler returns a cow by ID // DeviceByIDHandler returns a cow by ID
func (d Device) DeviceByIDHandler(w http.ResponseWriter, r *http.Request) { func (d Device) DeviceByObjectIDHandler(w http.ResponseWriter, r *http.Request) {
deviceID := mux.Vars(r)["cow_id"] deviceID := mux.Vars(r)["device_id"]
dbResp, err := d.DB.FindOne(context.Background(), bson.M{"_id": deviceID}) dbResp, err := d.DB.FindOne(context.Background(), bson.M{"_id": deviceID})
if err != nil { if err != nil {
config.ErrorStatus("failed to get device by ID", http.StatusNotFound, w, err) config.ErrorStatus("failed to get device by ObjectID", http.StatusNotFound, w, err)
return return
} }
@@ -100,7 +100,7 @@ func (d Device) DeviceByIDHandler(w http.ResponseWriter, r *http.Request) {
// NewDeviceHandler inserts a new cow into the collection and returns a result and error // NewDeviceHandler inserts a new cow into the collection and returns a result and error
func (d Device) NewDeviceHandler(w http.ResponseWriter, r *http.Request) { func (d Device) NewDeviceHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var deviceDetails models.DeviceDetails // Json data will represent the cow details model var deviceDetails models.DeviceDetails // Json data will represent the device details model
defer cancel() defer cancel()
// validate the request body // validate the request body
+84
View File
@@ -0,0 +1,84 @@
package handlers
import (
"context"
"encoding/json"
"net/http"
"time"
"github.com/gorilla/mux"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"github.com/SowinskiBraeden/DeviceBookingAPI/config"
"github.com/SowinskiBraeden/DeviceBookingAPI/databases"
"github.com/SowinskiBraeden/DeviceBookingAPI/models"
)
type User struct {
DB databases.UserDatabase
}
// CowByIDHandler returns a cow by ID
func (u User) UserByObjectIDHandler(w http.ResponseWriter, r *http.Request) {
userID := mux.Vars(r)["user_object_id"]
cID, err := primitive.ObjectIDFromHex(userID)
if err != nil {
config.ErrorStatus("failed to get objectID from Hex", http.StatusBadRequest, w, err)
return
}
dbResp, err := u.DB.FindOne(context.Background(), bson.M{"_id": cID})
if err != nil {
config.ErrorStatus("failed to get cow by ID", http.StatusNotFound, w, err)
return
}
b, err := json.Marshal(models.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"result": dbResp}})
if err != nil {
config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err)
return
}
w.WriteHeader(http.StatusOK)
w.Write(b)
}
// TODO: Handle User.Details.UserType properly i.e only SuperUser
// NewUserHandler inserts a new cow into the collection and returns a result and error
func (u User) NewUserHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var userDetails models.UserDetails // Json data will represent the user details model
defer cancel()
// validate the request body
if err := json.NewDecoder(r.Body).Decode(&userDetails); err != nil {
config.ErrorStatus("failed to unpack request body", http.StatusInternalServerError, w, err)
return
}
// use the validator library to validate required fields
if validationErr := validate.Struct(&userDetails); validationErr != nil {
config.ErrorStatus("invalid request body", http.StatusBadRequest, w, validationErr)
return
}
newUser := models.User{
ID: primitive.NewObjectID().Hex(),
Details: userDetails,
}
result, err := u.DB.InsertOne(ctx, newUser)
if err != nil {
config.ErrorStatus("failed to insert user", http.StatusBadRequest, w, err)
return
}
b, err := json.Marshal(models.UserResponse{Status: http.StatusCreated, Message: "success", Data: map[string]interface{}{"result": result}})
if err != nil {
config.ErrorStatus("failed to marshal response", http.StatusInternalServerError, w, err)
return
}
w.WriteHeader(http.StatusCreated)
w.Write(b)
}
+5 -5
View File
@@ -8,7 +8,7 @@ import (
"github.com/SowinskiBraeden/DeviceBookingAPI/models" "github.com/SowinskiBraeden/DeviceBookingAPI/models"
) )
const cowName = "cows" const cowDBO = "cows"
// CowDatabase contains the methods to use with the cow database // CowDatabase contains the methods to use with the cow database
type CowDatabase interface { type CowDatabase interface {
@@ -31,7 +31,7 @@ func NewCowDatabase(db DatabaseHelper) CowDatabase {
func (c *cowDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Cow, error) { func (c *cowDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Cow, error) {
cow := &models.Cow{} cow := &models.Cow{}
err := c.db.Collection(cowName).FindOne(ctx, filter).Decode(&cow) err := c.db.Collection(cowDBO).FindOne(ctx, filter).Decode(&cow)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -40,7 +40,7 @@ func (c *cowDatabase) FindOne(ctx context.Context, filter interface{}) (*models.
func (c *cowDatabase) Find(ctx context.Context, filter interface{}) ([]models.Cow, error) { func (c *cowDatabase) Find(ctx context.Context, filter interface{}) ([]models.Cow, error) {
var cows []models.Cow var cows []models.Cow
err := c.db.Collection(cowName).Find(ctx, filter).Decode(&cows) err := c.db.Collection(cowDBO).Find(ctx, filter).Decode(&cows)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -49,7 +49,7 @@ func (c *cowDatabase) Find(ctx context.Context, filter interface{}) ([]models.Co
// Returns the result (document id) and error // Returns the result (document id) and error
func (c *cowDatabase) InsertOne(ctx context.Context, document interface{}) (*mongoInsertOneResult, error) { func (c *cowDatabase) InsertOne(ctx context.Context, document interface{}) (*mongoInsertOneResult, error) {
result, err := c.db.Collection(cowName).InsertOne(ctx, document) result, err := c.db.Collection(cowDBO).InsertOne(ctx, document)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -57,7 +57,7 @@ func (c *cowDatabase) InsertOne(ctx context.Context, document interface{}) (*mon
} }
func (c *cowDatabase) UpdateOne(ctx context.Context, filter, update interface{}) (*mongoUpdateResult, error) { func (c *cowDatabase) UpdateOne(ctx context.Context, filter, update interface{}) (*mongoUpdateResult, error) {
result, err := c.db.Collection(cowName).UpdateOne(ctx, filter, update) result, err := c.db.Collection(cowDBO).UpdateOne(ctx, filter, update)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+5 -5
View File
@@ -8,7 +8,7 @@ import (
"github.com/SowinskiBraeden/DeviceBookingAPI/models" "github.com/SowinskiBraeden/DeviceBookingAPI/models"
) )
const deviceName = "devices" const deviceDBO = "devices"
// DeviceDatabase contains the methods to use with the cow database // DeviceDatabase contains the methods to use with the cow database
type DeviceDatabase interface { type DeviceDatabase interface {
@@ -31,7 +31,7 @@ func NewDeviceDatabase(db DatabaseHelper) DeviceDatabase {
func (d *deviceDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Device, error) { func (d *deviceDatabase) FindOne(ctx context.Context, filter interface{}) (*models.Device, error) {
device := &models.Device{} device := &models.Device{}
err := d.db.Collection(deviceName).FindOne(ctx, filter).Decode(&device) err := d.db.Collection(deviceDBO).FindOne(ctx, filter).Decode(&device)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -40,7 +40,7 @@ func (d *deviceDatabase) FindOne(ctx context.Context, filter interface{}) (*mode
func (d *deviceDatabase) Find(ctx context.Context, filter interface{}) ([]models.Device, error) { func (d *deviceDatabase) Find(ctx context.Context, filter interface{}) ([]models.Device, error) {
var devices []models.Device var devices []models.Device
err := d.db.Collection(deviceName).FindOne(ctx, filter).Decode(&devices) err := d.db.Collection(deviceDBO).FindOne(ctx, filter).Decode(&devices)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -48,7 +48,7 @@ func (d *deviceDatabase) Find(ctx context.Context, filter interface{}) ([]models
} }
func (d *deviceDatabase) InsertOne(ctx context.Context, document interface{}) (*mongoInsertOneResult, error) { func (d *deviceDatabase) InsertOne(ctx context.Context, document interface{}) (*mongoInsertOneResult, error) {
result, err := d.db.Collection(deviceName).InsertOne(ctx, document) result, err := d.db.Collection(deviceDBO).InsertOne(ctx, document)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -56,7 +56,7 @@ func (d *deviceDatabase) InsertOne(ctx context.Context, document interface{}) (*
} }
func (d *deviceDatabase) UpdateOne(ctx context.Context, filter, update interface{}) (*mongoUpdateResult, error) { func (d *deviceDatabase) UpdateOne(ctx context.Context, filter, update interface{}) (*mongoUpdateResult, error) {
result, err := d.db.Collection(deviceName).UpdateOne(ctx, filter, update) result, err := d.db.Collection(deviceDBO).UpdateOne(ctx, filter, update)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+65
View File
@@ -0,0 +1,65 @@
package databases
// go generate: mockery --name CowDatabase
import (
"context"
"github.com/SowinskiBraeden/DeviceBookingAPI/models"
)
const userDBO = "users"
// UserDatabase contains the methods to use with the cow database
type UserDatabase interface {
FindOne(ctx context.Context, filter interface{}) (*models.User, error)
Find(ctx context.Context, filter interface{}) ([]models.User, error)
InsertOne(ctx context.Context, document interface{}) (*mongoInsertOneResult, error)
UpdateOne(ctx context.Context, filter, document interface{}) (*mongoUpdateResult, error)
}
type userDatabase struct {
db DatabaseHelper
}
// NewCowDatabase initialized a new instance of a cow database with the provided db conntection
func NewUserDatabase(db DatabaseHelper) UserDatabase {
return &userDatabase{
db: db,
}
}
func (u *userDatabase) FindOne(ctx context.Context, filter interface{}) (*models.User, error) {
user := &models.User{}
err := u.db.Collection(userDBO).FindOne(ctx, filter).Decode(&user)
if err != nil {
return nil, err
}
return user, nil
}
func (u *userDatabase) Find(ctx context.Context, filter interface{}) ([]models.User, error) {
var users []models.User
err := u.db.Collection(userDBO).Find(ctx, filter).Decode(&users)
if err != nil {
return nil, err
}
return users, nil
}
// Returns the result (document id) and error
func (u *userDatabase) InsertOne(ctx context.Context, document interface{}) (*mongoInsertOneResult, error) {
result, err := u.db.Collection(userDBO).InsertOne(ctx, document)
if err != nil {
return nil, err
}
return &result, nil
}
func (u *userDatabase) UpdateOne(ctx context.Context, filter, update interface{}) (*mongoUpdateResult, error) {
result, err := u.db.Collection(userDBO).UpdateOne(ctx, filter, update)
if err != nil {
return nil, err
}
return &result, nil
}
+3 -1
View File
@@ -5,10 +5,12 @@ go 1.19
require ( require (
github.com/go-playground/validator/v10 v10.11.1 github.com/go-playground/validator/v10 v10.11.1
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef
github.com/joho/godotenv v1.4.0 github.com/joho/godotenv v1.4.0
github.com/thanhpk/randstr v1.0.4 github.com/thanhpk/randstr v1.0.4
go.mongodb.org/mongo-driver v1.10.2 go.mongodb.org/mongo-driver v1.10.2
go.uber.org/zap v1.23.0 go.uber.org/zap v1.23.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
) )
require ( require (
@@ -25,8 +27,8 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
) )
+3
View File
@@ -17,6 +17,8 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
@@ -78,6 +80,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+9
View File
@@ -0,0 +1,9 @@
package models
import "go.mongodb.org/mongo-driver/bson/primitive"
type Business struct {
ID primitive.ObjectID `bson:"_id"`
Admins []string `json:"admins"` // array of admin user ID's
Users []string `json:"users"` // array of user ID's
}
+123
View File
@@ -0,0 +1,123 @@
package models
import (
"math/rand"
"strings"
"time"
"unicode"
"golang.org/x/crypto/bcrypt"
)
const (
lowerCharSet = "abcdedfghijklmnopqrst"
upperCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
specialCharSet = "!@#$%&*?,.`~"
numberSet = "0123456789"
allCharSet = lowerCharSet + upperCharSet + specialCharSet + numberSet
)
// User types
const (
// SuperUsers can:
// - Create/Delete/Modify businesses
// - Create/Delete/Modify Admins & Users
// - Assign Admins & Users to businesses
TypeSuperUser int = 1
// Admins can:
// - Manage/Modify assigned business
// - Use reservation system for their business
// - Manage/Modify Cows & Devices for their business
// - Create Users & assign to their business
// - Promote Users to Admins in their business
TypeAdmin = 2
// Users can:
// - Use reservation system for their business
TypeUser = 3
)
type User struct {
ID string `bson:"_id"`
Details UserDetails `json:"details"`
}
type UserDetails struct {
FirstName string `json:"firstname" validate:"required"`
LastName string `json:"lastname" validate:"required"`
Email string `json:"email" validate:"required"`
Password string `json:"-" validate:"min=10,max=32"`
TempPassword bool `json:"temppassword"`
UID string `json:"uid"`
Business string `json:"business"`
UserType int `json:"usertype"`
Created_at time.Time `json:"created_at"`
Updated_at time.Time `json:"updated_at"`
}
func (s *User) HashPassword(password string) string {
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(hash)
}
func (a *User) ComparePasswords(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(a.Details.Password), []byte(password))
return err == nil
}
func (a *User) CheckPasswordStrength(password string) bool {
var hasUpper bool = false
for _, r := range password {
if unicode.IsUpper(r) && unicode.IsLetter(r) {
hasUpper = true
}
}
var hasLower bool = false
for _, r := range password {
if !unicode.IsLower(r) && unicode.IsLetter(r) {
hasLower = true
}
}
if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper && len(password) >= 8 {
return true
} else {
return false
}
}
func (a *User) GeneratePassword(passwordLength, minSpecialChar, minNum, minUpperCase int) string {
var password strings.Builder
//Set special character
for i := 0; i < minSpecialChar; i++ {
random := rand.Intn(len(specialCharSet))
password.WriteString(string(specialCharSet[random]))
}
//Set numeric
for i := 0; i < minNum; i++ {
random := rand.Intn(len(numberSet))
password.WriteString(string(numberSet[random]))
}
//Set uppercase
for i := 0; i < minUpperCase; i++ {
random := rand.Intn(len(upperCharSet))
password.WriteString(string(upperCharSet[random]))
}
remainingLength := passwordLength - minSpecialChar - minNum - minUpperCase
for i := 0; i < remainingLength; i++ {
random := rand.Intn(len(allCharSet))
password.WriteString(string(allCharSet[random]))
}
inRune := []rune(password.String())
rand.Shuffle(len(inRune), func(i, j int) {
inRune[i], inRune[j] = inRune[j], inRune[i]
})
return string(inRune)
}
+41
View File
@@ -0,0 +1,41 @@
package util
import (
"context"
"crypto/rand"
"io"
"github.com/SowinskiBraeden/DeviceBookingAPI/databases"
"go.uber.org/zap"
"go.mongodb.org/mongo-driver/bson"
)
var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}
func ValidateID(id string, DB databases.UserDatabase) bool { // true: valid id, false: id already in use
dbResp, err := DB.Find(context.TODO(), bson.M{})
if err != nil {
zap.S().With(err).Error("failed to get users")
return false
}
// If len == 0 then ID is not in use
if len(dbResp) == 0 {
return true
}
return false
}
func GenerateID(length int) string {
b := make([]byte, length)
n, err := io.ReadAtLeast(rand.Reader, b, length)
if n != length {
panic(err)
}
for i := 0; i < len(b); i++ {
b[i] = table[int(b[i])%len(table)]
}
return string(b)
}
+83
View File
@@ -0,0 +1,83 @@
package util
import (
"bufio"
"fmt"
"log"
"net/mail"
"os"
"strings"
"time"
"github.com/howeyc/gopass"
"go.mongodb.org/mongo-driver/bson/primitive"
"github.com/SowinskiBraeden/DeviceBookingAPI/databases"
"github.com/SowinskiBraeden/DeviceBookingAPI/models"
)
func ValidMailAddress(address string) (string, bool) {
addr, err := mail.ParseAddress(address)
if err != nil {
return "", false
}
return addr.Address, true
}
func Confirm(s string) bool {
r := bufio.NewReader(os.Stdin)
fmt.Printf("%s [y/n]: ", s)
res, err := r.ReadString('\n')
if err != nil {
log.Fatal(err)
}
return strings.ToLower(strings.TrimSpace(res))[0] == 'y'
}
func CreateDefaultAdmin(DB databases.UserDatabase) models.User {
fmt.Println()
reader := bufio.NewReader(os.Stdin)
fmt.Print("First Name: ")
firstname, _ := reader.ReadString('\n')
fmt.Print("Last Name: ")
lastname, _ := reader.ReadString('\n')
fmt.Print("Email: ")
email, _ := reader.ReadString('\n')
fmt.Print("Password: ")
password, _ := gopass.GetPasswd()
// Clear values of new lines and enter characters
firstname = strings.ReplaceAll(firstname, "\n", "")
lastname = strings.ReplaceAll(lastname, "\n", "")
email = strings.ReplaceAll(email, "\n", "")
firstname = strings.ReplaceAll(firstname, "\r", "")
lastname = strings.ReplaceAll(lastname, "\r", "")
email = strings.ReplaceAll(email, "\r", "")
var admin models.User
admin.Details.FirstName = firstname
admin.Details.LastName = lastname
admin.Details.Email = email
pass := strings.TrimSuffix(string(password), "\n")
admin.Details.Password = admin.HashPassword(pass)
admin.Details.TempPassword = false
var aid string
for {
aid = GenerateID(6)
if ValidateID(aid, DB) {
break
}
}
admin.Details.UID = aid
admin.Details.UserType = models.TypeSuperUser
admin.Details.Created_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
admin.Details.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
admin.ID = primitive.NewObjectID().Hex()
return admin
}