Overview
The users package defines the User model and provides database operations for user management. Users are stored in the users collection in MongoDB.
User Struct
The User struct is defined in users/model.go:12-22:
type User struct {
ID string `json:"id" bson:"_id" query:"id" form:"id" param:"id"`
Email string `json:"email" bson:"email" query:"email" form:"email" param:"email"`
Phone string `json:"phone" bson:"phone" query:"phone" form:"phone" param:"phone"`
Password [] byte `json:"password" bson:"password" query:"password" form:"password" param:"password"`
Role string `json:"role" bson:"role" query:"role" form:"role" param:"role"`
Status string `json:"status" bson:"status" query:"status" form:"status" param:"status"`
AccountType string `json:"account_type" bson:"account_type" query:"account_type" form:"account_type" param:"account_type"`
CreatedAt time . Time `json:"created_at" bson:"created_at" query:"created_at" form:"created_at" param:"created_at"`
UpdatedAt time . Time `json:"updated_at" bson:"updated_at" query:"updated_at" form:"updated_at" param:"updated_at"`
}
Field Definitions
Unique user identifier. Generated using UUID v4 during registration. Maps to MongoDB’s _id field.
User’s email address. Used for authentication and communication.
User’s phone number. Optional field for additional contact information.
Hashed password stored as byte array. Never store plain text passwords. Uses bcrypt with cost factor 14.
User’s role in the system (e.g., "admin", "user", "moderator"). Optional field for authorization.
Account status. Valid values:
"pending" - New users awaiting activation
"active" - Active users who can log in
"suspended" - Temporarily disabled accounts
"deleted" - Soft-deleted accounts
Type of user account (e.g., "free", "premium", "enterprise"). Optional field for subscription management.
Timestamp when the user account was created. Set automatically during registration.
Timestamp of the last account update. Should be updated on profile modifications.
Each field includes multiple struct tags for different frameworks:
JSON Serialization
MongoDB
Echo Framework
`json:"field_name"`
// Controls JSON encoding/decoding
// Used in API responses
The comprehensive struct tags enable automatic binding from HTTP requests to the User struct using Echo’s binding methods.
CRUD Operations
Create User
The CreateNewUser method inserts a new user into the database (users/model.go:24-30):
func ( u * User ) CreateNewUser () error {
_ , err := configs . StoreRequestInDb ( * u , "users" )
if err != nil {
return err
}
return nil
}
Usage:
user := users . User {
ID : uuid . New (). String (),
Email : "[email protected] " ,
Password : hashedPassword ,
Status : "pending" ,
CreatedAt : time . Now (),
}
err := user . CreateNewUser ()
if err != nil {
// Handle error
}
This is a method on the User struct, so it’s called on an instance: user.CreateNewUser()
Get User by ID
Retrieve a user by their unique identifier (users/model.go:32-36):
func GetUserById ( id string ) User {
var user User
_ = configs . MI . DB . Collection ( "users" ). FindOne ( context . TODO (), bson . M { "_id" : id }). Decode ( & user )
return user
}
The user’s unique identifier (UUID string)
Returns: User struct (returns empty User if not found)
Usage:
user := users . GetUserById ( "550e8400-e29b-41d4-a716-446655440000" )
if user . ID == "" {
// User not found
}
This function ignores errors and returns an empty User struct if the user is not found. Always check if user.ID is empty to verify the user exists.
Get User by Phone
Find a user by phone number (users/model.go:38-45):
func GetUserByPhone ( phone string ) ( User , error ) {
var user User
err := configs . MI . DB . Collection ( "users" ). FindOne ( context . TODO (), bson . M { "phone" : phone }). Decode ( & user )
if err != nil {
return user , err
}
return user , nil
}
The phone number to search for
Returns: User struct and error (error is non-nil if user not found)
Usage:
user , err := users . GetUserByPhone ( "+250788123456" )
if err != nil {
// User not found or database error
}
Unlike GetUserById, this function properly returns errors, making it more reliable for error handling.
Get User by Email
Retrieve a user by email address (users/model.go:47-54):
func GetUserByEmail ( email string ) ( User , error ) {
var user User
err := configs . MI . DB . Collection ( "users" ). FindOne ( context . TODO (), bson . M { "email" : email }). Decode ( & user )
if err != nil {
return user , err
}
return user , nil
}
The email address to search for
Returns: User struct and error
Usage:
user , err := users . GetUserByEmail ( "[email protected] " )
if err != nil {
if err == mongo . ErrNoDocuments {
// User doesn't exist
} else {
// Database error
}
}
This function is used by the authentication system to validate login credentials.
User Registration Flow
The complete user registration process in auth/controller.go:30-48:
func createNewUser ( email string , password [] byte ) ( users . User , error ) {
uid := uuid . New (). String ()
hashedPassword , err := bcrypt . GenerateFromPassword ([] byte ( password ), 14 )
if err != nil {
return users . User {}, err
}
user := users . User {
Email : email ,
Status : "pending" ,
Password : hashedPassword ,
ID : uid ,
CreatedAt : time . Now (),
}
err = user . CreateNewUser ()
if err != nil {
return users . User {}, err
}
return user , nil
}
Registration Steps:
Generate unique UUID for user ID
Hash password with bcrypt (cost 14)
Create User struct with "pending" status
Set creation timestamp
Insert into database
Return created user
New users are created with status: "pending" and cannot log in until their status is changed to "active".
User Login Validation
The login validation process in auth/controller.go:13-27:
func processUserLogin ( email , password string ) ( string , bool , error ) {
user , err := users . GetUserByEmail ( email )
if err != nil {
return "" , false , err
}
if user . Status != "active" {
return "" , false , fmt . Errorf ( "user is not active" )
}
errr := bcrypt . CompareHashAndPassword ( user . Password , [] byte ( password ))
if errr != nil {
return "" , false , fmt . Errorf ( "the password provided is not valid" )
}
return user . ID , true , nil
}
Validation Steps:
Fetch user by email
Check if status is "active"
Compare password hash with bcrypt
Return user ID on success
Status Management
Status Values
Default status for new users. Cannot log in.
Verified and active users. Can log in and use the system.
Temporarily disabled accounts. Cannot log in.
Soft-deleted accounts. Cannot log in.
Updating User Status
To activate a pending user:
updateData := bson . M {
"status" : "active" ,
"updated_at" : time . Now (),
}
_ , err := configs . UpdateRequestInDb ( userID , updateData , "users" )
if err != nil {
// Handle error
}
Consider implementing email verification before changing status from "pending" to "active".
Password Management
Hashing
Passwords are hashed using bcrypt with cost factor 14:
hashedPassword , err := bcrypt . GenerateFromPassword ([] byte ( password ), 14 )
Cost Factor 14:
Provides strong security against brute force attacks
~180ms hashing time on modern hardware
2^14 (16,384) hashing iterations
Verification
Password verification during login:
err := bcrypt . CompareHashAndPassword ( user . Password , [] byte ( password ))
if err != nil {
// Invalid password
}
Never log or expose password hashes. They are sensitive security credentials.
Missing Functionality
The following common user operations are not yet implemented:
Update User
// Not implemented - suggested implementation
func ( u * User ) UpdateUser () error {
u . UpdatedAt = time . Now ()
_ , err := configs . UpdateRequestInDb ( u . ID , * u , "users" )
return err
}
Delete User
// Not implemented - suggested soft delete
func ( u * User ) DeleteUser () error {
updateData := bson . M {
"status" : "deleted" ,
"updated_at" : time . Now (),
}
_ , err := configs . UpdateRequestInDb ( u . ID , updateData , "users" )
return err
}
List Users
// Not implemented - suggested implementation
func GetAllUsers ( filter bson . M ) ([] User , error ) {
cursor , err := configs . MI . DB . Collection ( "users" ). Find ( context . TODO (), filter )
if err != nil {
return nil , err
}
defer cursor . Close ( context . TODO ())
var users [] User
if err = cursor . All ( context . TODO (), & users ); err != nil {
return nil , err
}
return users , nil
}
Best Practices
Data Validation
No input validation is currently implemented. Consider adding:
// Email validation
func ( u * User ) ValidateEmail () error {
emailRegex := regexp . MustCompile ( `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` )
if ! emailRegex . MatchString ( u . Email ) {
return fmt . Errorf ( "invalid email format" )
}
return nil
}
// Password strength validation
func ValidatePassword ( password string ) error {
if len ( password ) < 8 {
return fmt . Errorf ( "password must be at least 8 characters" )
}
// Add more validation rules
return nil
}
Security Considerations
Unique Email Index - Add a unique index on email field to prevent duplicates:
indexModel := mongo . IndexModel {
Keys : bson . D {{ Key : "email" , Value : 1 }},
Options : options . Index (). SetUnique ( true ),
}
Password in JSON - Consider excluding password from JSON responses:
Password [] byte `json:"-" bson:"password"`
Sensitive Data Logging - Never log full user objects that contain passwords.
Usage Examples
Complete Registration Flow
import (
" backend/users "
" github.com/google/uuid "
" golang.org/x/crypto/bcrypt "
" time "
)
// Hash password
hashedPassword , err := bcrypt . GenerateFromPassword ([] byte ( "userPassword123" ), 14 )
if err != nil {
return err
}
// Create user
user := users . User {
ID : uuid . New (). String (),
Email : "[email protected] " ,
Phone : "+250788123456" ,
Password : hashedPassword ,
Status : "pending" ,
Role : "user" ,
CreatedAt : time . Now (),
}
// Save to database
err = user . CreateNewUser ()
if err != nil {
return err
}
Check User Existence
user , err := users . GetUserByEmail ( "[email protected] " )
if err != nil {
if err == mongo . ErrNoDocuments {
// User doesn't exist - safe to register
} else {
// Database error
}
} else {
// User already exists
}
Next Steps
Authentication See how the User model integrates with JWT authentication
Database Learn about MongoDB operations and configuration