feat(admin): handle user demotion & promotion
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Evan Reichard 2024-05-25 21:12:07 -04:00
parent 7c6acad689
commit 546600db93
3 changed files with 75 additions and 30 deletions

View File

@ -71,7 +71,7 @@ const (
type requestAdminUpdateUser struct { type requestAdminUpdateUser struct {
User string `form:"user"` User string `form:"user"`
Password *string `form:"password"` Password *string `form:"password"`
isAdmin *bool `form:"is_admin"` IsAdmin *string `form:"is_admin"`
Operation operationType `form:"operation"` Operation operationType `form:"operation"`
} }
@ -674,14 +674,14 @@ func (api *API) restoreData(zipReader *zip.Reader) error {
destPath := filepath.Join(api.cfg.DataPath, file.Name) destPath := filepath.Join(api.cfg.DataPath, file.Name)
destFile, err := os.Create(destPath) destFile, err := os.Create(destPath)
if err != nil { if err != nil {
fmt.Println("Error creating destination file:", err) log.Errorf("error creating destination file: %v", err)
return err return err
} }
defer destFile.Close() defer destFile.Close()
// Copy the contents from the zip file to the destination file. // Copy the contents from the zip file to the destination file.
if _, err := io.Copy(destFile, rc); err != nil { if _, err := io.Copy(destFile, rc); err != nil {
fmt.Println("Error copying file contents:", err) log.Errorf("Error copying file contents: %v", err)
return err return err
} }
} }
@ -796,8 +796,8 @@ func (api *API) createUser(createRequest requestAdminUpdateUser) error {
} }
// Handle Admin (Explicit or False) // Handle Admin (Explicit or False)
if createRequest.isAdmin != nil { if createRequest.IsAdmin != nil {
createParams.Admin = *createRequest.isAdmin createParams.Admin = *createRequest.IsAdmin == "true"
} else { } else {
createParams.Admin = false createParams.Admin = false
} }
@ -835,7 +835,7 @@ func (api *API) updateUser(updateRequest requestAdminUpdateUser) error {
if updateRequest.User == "" { if updateRequest.User == "" {
return fmt.Errorf("username can't be empty") return fmt.Errorf("username can't be empty")
} }
if updateRequest.Password == nil && updateRequest.isAdmin == nil { if updateRequest.Password == nil && updateRequest.IsAdmin == nil {
return fmt.Errorf("nothing to update") return fmt.Errorf("nothing to update")
} }
@ -845,8 +845,8 @@ func (api *API) updateUser(updateRequest requestAdminUpdateUser) error {
} }
// Handle Admin (Update or Existing) // Handle Admin (Update or Existing)
if updateRequest.isAdmin != nil { if updateRequest.IsAdmin != nil {
updateParams.Admin = *updateRequest.isAdmin updateParams.Admin = *updateRequest.IsAdmin == "true"
} else { } else {
user, err := api.db.Queries.GetUser(api.db.Ctx, updateRequest.User) user, err := api.db.Queries.GetUser(api.db.Ctx, updateRequest.User)
if err != nil { if err != nil {
@ -855,8 +855,12 @@ func (api *API) updateUser(updateRequest requestAdminUpdateUser) error {
updateParams.Admin = user.Admin updateParams.Admin = user.Admin
} }
// TODO: // Check Admins
// - Validate Not Last Admin if isLast, err := api.isLastAdmin(updateRequest.User); err != nil {
return err
} else if isLast {
return fmt.Errorf("unable to demote %s - last admin", updateRequest.User)
}
// Handle Password // Handle Password
if updateRequest.Password != nil { if updateRequest.Password != nil {
@ -891,7 +895,31 @@ func (api *API) updateUser(updateRequest requestAdminUpdateUser) error {
} }
func (api *API) deleteUser(updateRequest requestAdminUpdateUser) error { func (api *API) deleteUser(updateRequest requestAdminUpdateUser) error {
// TODO: // Check Admins
// - Validate Not Last Admin if isLast, err := api.isLastAdmin(updateRequest.User); err != nil {
return err
} else if isLast {
return fmt.Errorf("unable to demote %s - last admin", updateRequest.User)
}
// TODO - Implementation
return errors.New("unimplemented") return errors.New("unimplemented")
} }
func (api *API) isLastAdmin(userID string) (bool, error) {
allUsers, err := api.db.Queries.GetUsers(api.db.Ctx)
if err != nil {
return false, errors.Wrap(err, fmt.Sprintf("GetUsers DB Error: %v", err))
}
hasAdmin := false
for _, user := range allUsers {
if user.Admin && user.ID != userID {
hasAdmin = true
break
}
}
return !hasAdmin, nil
}

File diff suppressed because one or more lines are too long

View File

@ -68,11 +68,8 @@
<form method="POST" <form method="POST"
action="./users" action="./users"
class="flex flex gap-2 text-black dark:text-white text-sm"> class="flex flex gap-2 text-black dark:text-white text-sm">
<input type="text" <input type="hidden" id="operation" name="operation" value="UPDATE" />
id="operation" <input type="hidden" id="user" name="user" value="{{ $user.ID }}" />
name="operation"
value="UPDATE"
class="hidden" />
<input type="password" <input type="password"
id="password" id="password"
name="password" name="password"
@ -83,17 +80,37 @@
</form> </form>
</div> </div>
</td> </td>
<td class="p-3 border-b border-gray-200 text-center min-w-40"> <td class="flex gap-2 justify-center p-3 border-b border-gray-200 text-center min-w-40">
<span class="px-2 py-1 rounded-md text-white dark:text-black {{ if $user.Admin }}bg-gray-800 dark:bg-gray-100{{ else }}bg-gray-400 dark:bg-gray-600 cursor-pointer{{ end }}">admin</span> <!-- Set Admin & User Styles -->
<span class="px-2 py-1 rounded-md text-white dark:text-black {{ if $user.Admin }}bg-gray-400 dark:bg-gray-600 cursor-pointer{{ else }}bg-gray-800 dark:bg-gray-100{{ end }}">user</span> {{ $adminStyle := "bg-gray-400 dark:bg-gray-600 cursor-pointer" }}
</td> {{ $userStyle := "bg-gray-400 dark:bg-gray-600 cursor-pointer" }}
<td class="p-3 border-b border-gray-200"> {{ if $user.Admin }}{{ $adminStyle = "bg-gray-800 dark:bg-gray-100 cursor-default" }}{{ end }}
<p>{{ $user.CreatedAt }}</p> {{ if not $user.Admin }}{{ $userStyle = "bg-gray-800 dark:bg-gray-100 cursor-default" }}{{ end }}
</td> <form method="POST"
</tr> action="./users"
{{ end }} class="flex flex gap-2 text-black dark:text-white text-sm">
</tbody> <input type="hidden" id="operation" name="operation" value="UPDATE" />
</table> <input type="hidden" id="user" name="user" value="{{ $user.ID }}" />
</div> <input type="hidden" id="is_admin" name="is_admin" value="true" />
<button {{ if $user.Admin }}type="button"{{ else }}type="submit"{{ end }} class="px-2 py-1 rounded-md text-white dark:text-black {{ $adminStyle }}">admin
</button>
</form>
<form method="POST"
action="./users"
class="flex flex gap-2 text-black dark:text-white text-sm">
<input type="hidden" id="operation" name="operation" value="UPDATE" />
<input type="hidden" id="user" name="user" value="{{ $user.ID }}" />
<input type="hidden" id="is_admin" name="is_admin" value="false" />
<button {{ if $user.Admin }}type="submit"{{ else }}type="button"{{ end }} class="px-2 py-1 rounded-md text-white dark:text-black {{ $userStyle }}">user
</form>
</td>
<td class="p-3 border-b border-gray-200">
<p>{{ $user.CreatedAt }}</p>
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div> </div>
{{ end }} {{ end }}