158 lines
4.6 KiB
Go
158 lines
4.6 KiB
Go
package v1
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"fmt"
|
|
|
|
"reichard.io/antholume/database"
|
|
argon2id "github.com/alexedwards/argon2id"
|
|
)
|
|
|
|
// GET /settings
|
|
func (s *Server) GetSettings(ctx context.Context, request GetSettingsRequestObject) (GetSettingsResponseObject, error) {
|
|
auth, ok := s.getSessionFromContext(ctx)
|
|
if !ok {
|
|
return GetSettings401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
|
}
|
|
|
|
user, err := s.db.Queries.GetUser(ctx, auth.UserName)
|
|
if err != nil {
|
|
return GetSettings500JSONResponse{Code: 500, Message: err.Error()}, nil
|
|
}
|
|
|
|
devices, err := s.db.Queries.GetDevices(ctx, auth.UserName)
|
|
if err != nil {
|
|
return GetSettings500JSONResponse{Code: 500, Message: err.Error()}, nil
|
|
}
|
|
|
|
apiDevices := make([]Device, len(devices))
|
|
for i, device := range devices {
|
|
apiDevices[i] = Device{
|
|
Id: &device.ID,
|
|
DeviceName: &device.DeviceName,
|
|
CreatedAt: parseTimePtr(device.CreatedAt),
|
|
LastSynced: parseTimePtr(device.LastSynced),
|
|
}
|
|
}
|
|
|
|
response := SettingsResponse{
|
|
User: UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
|
Timezone: user.Timezone,
|
|
Devices: &apiDevices,
|
|
}
|
|
return GetSettings200JSONResponse(response), nil
|
|
}
|
|
|
|
// authorizeCredentials verifies if credentials are valid
|
|
func (s *Server) authorizeCredentials(ctx context.Context, username string, password string) bool {
|
|
user, err := s.db.Queries.GetUser(ctx, username)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Try argon2 hash comparison
|
|
if match, err := argon2id.ComparePasswordAndHash(password, *user.Pass); err == nil && match {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// PUT /settings
|
|
func (s *Server) UpdateSettings(ctx context.Context, request UpdateSettingsRequestObject) (UpdateSettingsResponseObject, error) {
|
|
auth, ok := s.getSessionFromContext(ctx)
|
|
if !ok {
|
|
return UpdateSettings401JSONResponse{Code: 401, Message: "Unauthorized"}, nil
|
|
}
|
|
|
|
if request.Body == nil {
|
|
return UpdateSettings400JSONResponse{Code: 400, Message: "Request body is required"}, nil
|
|
}
|
|
|
|
user, err := s.db.Queries.GetUser(ctx, auth.UserName)
|
|
if err != nil {
|
|
return UpdateSettings500JSONResponse{Code: 500, Message: err.Error()}, nil
|
|
}
|
|
|
|
updateParams := database.UpdateUserParams{
|
|
UserID: auth.UserName,
|
|
Admin: auth.IsAdmin,
|
|
}
|
|
|
|
// Update password if provided
|
|
if request.Body.NewPassword != nil {
|
|
if request.Body.Password == nil {
|
|
return UpdateSettings400JSONResponse{Code: 400, Message: "Current password is required to set new password"}, nil
|
|
}
|
|
|
|
// Verify current password - first try bcrypt (new format), then argon2, then MD5 (legacy format)
|
|
currentPasswordMatched := false
|
|
|
|
// Try argon2 (current format)
|
|
if !currentPasswordMatched {
|
|
currentPassword := fmt.Sprintf("%x", md5.Sum([]byte(*request.Body.Password)))
|
|
if match, err := argon2id.ComparePasswordAndHash(currentPassword, *user.Pass); err == nil && match {
|
|
currentPasswordMatched = true
|
|
}
|
|
}
|
|
|
|
if !currentPasswordMatched {
|
|
return UpdateSettings400JSONResponse{Code: 400, Message: "Invalid current password"}, nil
|
|
}
|
|
|
|
// Hash new password with argon2
|
|
newPassword := fmt.Sprintf("%x", md5.Sum([]byte(*request.Body.NewPassword)))
|
|
hashedPassword, err := argon2id.CreateHash(newPassword, argon2id.DefaultParams)
|
|
if err != nil {
|
|
return UpdateSettings500JSONResponse{Code: 500, Message: "Failed to hash password"}, nil
|
|
}
|
|
updateParams.Password = &hashedPassword
|
|
}
|
|
|
|
// Update timezone if provided
|
|
if request.Body.Timezone != nil {
|
|
updateParams.Timezone = request.Body.Timezone
|
|
}
|
|
|
|
// If nothing to update, return error
|
|
if request.Body.NewPassword == nil && request.Body.Timezone == nil {
|
|
return UpdateSettings400JSONResponse{Code: 400, Message: "At least one field must be provided"}, nil
|
|
}
|
|
|
|
// Update user
|
|
_, err = s.db.Queries.UpdateUser(ctx, updateParams)
|
|
if err != nil {
|
|
return UpdateSettings500JSONResponse{Code: 500, Message: err.Error()}, nil
|
|
}
|
|
|
|
// Get updated settings to return
|
|
user, err = s.db.Queries.GetUser(ctx, auth.UserName)
|
|
if err != nil {
|
|
return UpdateSettings500JSONResponse{Code: 500, Message: err.Error()}, nil
|
|
}
|
|
|
|
devices, err := s.db.Queries.GetDevices(ctx, auth.UserName)
|
|
if err != nil {
|
|
return UpdateSettings500JSONResponse{Code: 500, Message: err.Error()}, nil
|
|
}
|
|
|
|
apiDevices := make([]Device, len(devices))
|
|
for i, device := range devices {
|
|
apiDevices[i] = Device{
|
|
Id: &device.ID,
|
|
DeviceName: &device.DeviceName,
|
|
CreatedAt: parseTimePtr(device.CreatedAt),
|
|
LastSynced: parseTimePtr(device.LastSynced),
|
|
}
|
|
}
|
|
|
|
response := SettingsResponse{
|
|
User: UserData{Username: auth.UserName, IsAdmin: auth.IsAdmin},
|
|
Timezone: user.Timezone,
|
|
Devices: &apiDevices,
|
|
}
|
|
return UpdateSettings200JSONResponse(response), nil
|
|
}
|
|
|