Gitfed
bastien-mrq/gitfed / cmd / gitfed-web / handlers_admin_users.go
package main

import (
	"bytes"
	"html/template"
	"net/http"

	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/i18n"
	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/store"
)

var adminUsersTpl = newTpl("admin-users", `
{{.Flash}}
<p><a href="/admin">&larr; {{t .Lang "nav.admin"}}</a></p>
<h1>{{t .Lang "admin.users_title"}}</h1>
<table>
<tr><th>{{t .Lang "admin.users_col_username"}}</th><th>{{t .Lang "admin.users_col_keys"}}</th><th>{{t .Lang "nav.admin"}}</th><th></th></tr>
{{range .Users}}
<tr>
  <td>{{.Username}}</td>
  <td>{{len .PubKeys}}</td>
  <td>
    <form class="inline" method="post" action="/admin/users/set-admin">
      <input type="hidden" name="username" value="{{.Username}}">
      <input type="hidden" name="is_admin" value="{{if .IsAdmin}}0{{else}}1{{end}}">
      <button type="submit">{{if .IsAdmin}}{{t $.Lang "admin.revoke_admin"}}{{else}}{{t $.Lang "admin.make_admin"}}{{end}}</button>
    </form>
  </td>
  <td>
    <form class="inline" method="post" action="/admin/users/delete" data-confirm="{{t $.Lang "admin.confirm_delete_user"}} {{.Username}}?">
      <input type="hidden" name="username" value="{{.Username}}">
      <button class="danger" type="submit">{{t $.Lang "settings.remove"}}</button>
    </form>
  </td>
</tr>
{{else}}
<tr><td colspan="4" class="muted">{{t .Lang "admin.users_empty"}}</td></tr>
{{end}}
</table>

<form class="card" method="post" action="/admin/users">
  <strong>{{t .Lang "admin.add_user"}}</strong>
  <label>{{t .Lang "admin.users_col_username"}}</label>
  <input name="username" required placeholder="alice">
  <label>{{t .Lang "settings.pubkey_label"}}</label>
  <input name="pubkey" required placeholder="ssh-ed25519 AAAA... comment">
  <label>{{t .Lang "admin.initial_password"}}</label>
  <input name="password" type="password" required minlength="8">
  <label><input type="checkbox" name="is_admin" value="1" style="width:auto; display:inline-block;"> {{t .Lang "admin.grant_admin"}}</label>
  <button type="submit">{{t .Lang "dashboard.create"}}</button>
</form>
`)

func (s *server) handleAdminUsersList(w http.ResponseWriter, r *http.Request) {
	lang := s.lang(r)
	users, err := s.ops.ListUsers()
	if err != nil {
		s.serverError(w, r, err)
		return
	}
	var buf bytes.Buffer
	_ = adminUsersTpl.Execute(&buf, struct {
		Users []store.User
		Lang  string
		Flash template.HTML
	}{users, string(lang), flash(r)})
	s.render(w, r, i18n.T(lang, "admin.users_title"), "admin", template.HTML(buf.String()))
}

func (s *server) handleAdminUsersCreate(w http.ResponseWriter, r *http.Request) {
	lang := s.lang(r)
	username := r.FormValue("username")
	pubkey := r.FormValue("pubkey")
	password := r.FormValue("password")

	if err := s.ops.CreateUser(username, pubkey); err != nil {
		redirectWithMsg(w, r, "/admin/users", err.Error(), true)
		return
	}
	if err := s.ops.SetPassword(username, password); err != nil {
		redirectWithMsg(w, r, "/admin/users", err.Error(), true)
		return
	}
	if r.FormValue("is_admin") == "1" {
		if err := s.ops.SetUserAdmin(username, true); err != nil {
			redirectWithMsg(w, r, "/admin/users", err.Error(), true)
			return
		}
	}
	redirectWithMsg(w, r, "/admin/users", i18n.T(lang, "admin.msg_user_created", username), false)
}

func (s *server) handleAdminUsersDelete(w http.ResponseWriter, r *http.Request) {
	lang := s.lang(r)
	username := r.FormValue("username")
	if err := s.ops.DeleteUser(username); err != nil {
		redirectWithMsg(w, r, "/admin/users", err.Error(), true)
		return
	}
	redirectWithMsg(w, r, "/admin/users", i18n.T(lang, "admin.msg_user_deleted", username), false)
}

func (s *server) handleAdminUsersSetAdmin(w http.ResponseWriter, r *http.Request) {
	lang := s.lang(r)
	username := r.FormValue("username")
	isAdmin := r.FormValue("is_admin") == "1"
	if err := s.ops.SetUserAdmin(username, isAdmin); err != nil {
		redirectWithMsg(w, r, "/admin/users", err.Error(), true)
		return
	}
	redirectWithMsg(w, r, "/admin/users", i18n.T(lang, "admin.msg_user_updated", username), false)
}