Skip to content

Commit

Permalink
implement user controller methods for admin panel api
Browse files Browse the repository at this point in the history
  • Loading branch information
erudenko committed Jul 4, 2023
1 parent c398469 commit e94cb5a
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 49 deletions.
35 changes: 15 additions & 20 deletions model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ type User struct {
PhoneNumber string `json:"phone_number,omitempty"`

// authentication data for user
PasswordHash string `json:"password_hash,omitempty"` // TODO: do we need salt and pepper here as well?
PasswordResetRequired bool `json:"password_reset_required,omitempty"`
PasswordChangeForced bool `json:"password_change_forced,omitempty"`
PasswordHash string `json:"password_hash,omitempty"` // TODO: do we need salt and pepper here as well?
PasswordResetRequired bool `json:"password_reset_required,omitempty"`
PasswordChangeForced bool `json:"password_change_forced,omitempty"`
LastPasswordResetAt time.Time `json:"last_password_reset_at,omitempty"`

Tags []string `json:"tags,omitempty"`

Expand All @@ -38,10 +39,9 @@ type User struct {
Address map[string]UserAddress `json:"address,omitempty"` // addresses for home, work, etc

// login stats
LastLoginIP string `json:"last_login_ip,omitempty"`
LastLoginAt time.Time `json:"last_login_at,omitempty"`
LastPasswordResetAt time.Time `json:"last_password_reset_at,omitempty"`
LoginsCount int `json:"logins_count,omitempty"`
LastLoginIP string `json:"last_login_ip,omitempty"`
LastLoginAt time.Time `json:"last_login_at,omitempty"`
LoginsCount int `json:"logins_count,omitempty"`

// verification data
PhoneVerificationDetails struct {
Expand All @@ -55,13 +55,8 @@ type User struct {
} `json:"email_verification_details,omitempty"`

// blocked user
Blocked bool `json:"blocked,omitempty"`
BlockedDetails struct {
Reason string `json:"reason,omitempty"`
BlockedAt time.Time `json:"blocked_at,omitempty"`
BlockedByName string `json:"blocked_by_name,omitempty"`
BlockedById string `json:"blocked_by_id,omitempty"`
} `json:"blocked_details,omitempty"`
Blocked bool `json:"blocked,omitempty"`
BlockedDetails *UserBlockedDetails `json:"blocked_details,omitempty"`

// mapping to external systems
ExternalID string `json:"external_id,omitempty"` // external system user ID
Expand Down Expand Up @@ -92,12 +87,12 @@ type UserData struct {
DebugOTPCode string `json:"debug_otp,omitempty"`
}

type UserField string

const (
UserFieldAll UserField = "all"
UserFieldPassword UserField = "password"
)
type UserBlockedDetails struct {
Reason string `json:"reason,omitempty"`
BlockedAt time.Time `json:"blocked_at,omitempty"`
BlockedByName string `json:"blocked_by_name,omitempty"`
BlockedById string `json:"blocked_by_id,omitempty"`
}

type UserDataField string

Expand Down
78 changes: 78 additions & 0 deletions model/user_fieldset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package model

type UserFieldset string

const (
UserFieldsetBasic UserFieldset = "basic"
UserFieldsetAll UserFieldset = "all"
UserFieldsetBlockStatus UserFieldset = "block_status"
UserFieldsetPassword UserFieldset = "password"
// TODO: Add fieldset cases for other cases.
)

// TODO: Add more fieldset for a map.
var UserFieldsetMap = map[UserFieldset][]string{
UserFieldsetBasic: {
"ID",
"Name",
"Username",
"Email",
"GivenName",
"FamilyName",
"MiddleName",
"Nickname",
"PreferredUsername",
"PhoneNumber",
},
UserFieldsetPassword: {
"ID",
"PasswordHash",
"PasswordResetRequired",
"PasswordChangeForced",
"LastPasswordResetAt",
},
}

func (f UserFieldset) Fields() []string {
return UserFieldsetMap[f]
}

var ImmutableFields = []string{"ID", "CreatedAt"}

// the list for update for specific fieldset
// usually it includes UpdateAt and excludes ID from the list
func (f UserFieldset) UpdateFields() []string {
r := UserFieldsetMap[f]
r = subtract(r, ImmutableFields)
r = append(r, "UpdatedAt")
return r
}

// subtract sl2 from sl1
func subtract[T comparable](sl1, sl2 []T) []T {
var diff []T

// Loop two times, first to find slice1 strings not in slice2,
// second loop to find slice2 strings not in slice1
for i := 0; i < 2; i++ {
for _, s1 := range sl1 {
found := false
for _, s2 := range sl2 {
if s1 == s2 {
found = true
break
}
}
// String not found. We add it to return slice
if !found {
diff = append(diff, s1)
}
}
// Swap the slices, only if it was the first loop
if i == 0 {
sl1, sl2 = sl2, sl1
}
}

return diff
}
3 changes: 2 additions & 1 deletion model/user_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ type UserStorage interface {
type UserMutableStorage interface {
// User mutation
AddUser(ctx context.Context, user User) (User, error)
UpdateUser(ctx context.Context, user User, fields ...UserField) (User, error)
UpdateUser(ctx context.Context, user User, fields ...string) (User, error)
UpdateUserData(ctx context.Context, userID string, data UserData, fields ...UserDataField) (UserData, error)
DeleteUser(ctx context.Context, userID string) error
}

// UserAuthStorage is a storage which keep all auth information for user.
Expand Down
54 changes: 48 additions & 6 deletions storage/user_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,17 @@ func NewUserStorageController(u model.UserStorage, s model.SecurityServerSetting

// UserByID returns User with ID with basic fields
func (c *UserStorageController) UserByID(ctx context.Context, userID string) (model.User, error) {
return c.UserByIDWithFields(ctx, userID, UserFieldsetBasic)
return c.UserByIDWithFields(ctx, userID, model.UserFieldsetBasic)
}

// UserByIDWithFields returns user with specific fieldset
func (c *UserStorageController) UserByIDWithFields(ctx context.Context, userID string, fields UserFieldset) (model.User, error) {
func (c *UserStorageController) UserByIDWithFields(ctx context.Context, userID string, fields model.UserFieldset) (model.User, error) {
user, err := c.u.UserByID(ctx, userID)
if err != nil {
return model.User{}, err
}
// strip user fields
result := model.CopyFields(user, UserFieldsetMap[fields])
result := model.CopyFields(user, fields.Fields())
return result, nil
}

Expand All @@ -63,7 +63,7 @@ func (c *UserStorageController) GetUsers(ctx context.Context, filter string, ski

// strip user fields
for i, user := range users {
users[i] = model.CopyFields(user, UserFieldsetMap[UserFieldsetBasic])
users[i] = model.CopyFields(user, model.UserFieldsetBasic.Fields())
}
return users, total, nil
}
Expand Down Expand Up @@ -135,7 +135,7 @@ func (c *UserStorageController) UpdateUserPassword(ctx context.Context, userID,
UpdatedAt: time.Now(),
LastPasswordResetAt: time.Now(),
}
_, err = c.ums.UpdateUser(ctx, user, model.UserFieldPassword)
_, err = c.ums.UpdateUser(ctx, user, model.UserFieldsetPassword.UpdateFields()...)
if err != nil {
return err
}
Expand All @@ -146,10 +146,52 @@ func (c *UserStorageController) UpdateUserPassword(ctx context.Context, userID,
}

func (c *UserStorageController) ChangeBlockStatus(ctx context.Context, userID, reason, whoName, whoID string, blocked bool) error {
user := model.User{
ID: userID,
UpdatedAt: time.Now(),
Blocked: blocked,
}
if blocked {
user.BlockedDetails = &model.UserBlockedDetails{
Reason: reason,
BlockedByName: whoName,
BlockedById: whoID,
BlockedAt: time.Now(),
}
}

// TODO: Call pre-block callback

_, err := c.ums.UpdateUser(ctx, user, model.UserFieldsetBlockStatus.Fields()...)
if err != nil {
return err
}

// TODO: Call post-block callback

return nil
}

func (c *UserStorageController) UpdateUser(ctx context.Context, u model.User, fields []string) (model.User, error) {
func (c *UserStorageController) UpdateUser(ctx context.Context, user model.User, fields []string) (model.User, error) {
// TODO: Call pre-update callback

u, err := c.ums.UpdateUser(ctx, user, fields...)
if err != nil {
return model.User{}, err
}

// TODO: Call post-update callback
return u, nil
}

func (c *UserStorageController) DeleteUser(ctx context.Context, userID string) error {
// TODO: Call pre-update callback

err := c.ums.DeleteUser(ctx, userID)
if err != nil {
return err
}

// TODO: Call post-update callback
return nil
}
22 changes: 0 additions & 22 deletions storage/user_fieldset.go

This file was deleted.

0 comments on commit e94cb5a

Please sign in to comment.