admin: apppass uses httpin

This commit is contained in:
Chris Sexton 2024-03-13 12:15:38 -04:00
parent a6d224c87b
commit b73e64ad72
3 changed files with 117 additions and 102 deletions

View File

@ -24,17 +24,17 @@ templ (a *AdminPlugin) page() {
<button hx-put="/apppass/api" hx-target="#data" class="submit success button">New</button>
</div>
</div>
</form>
<div class="grid-x">
<div class="cell" id="data"></div>
</div>
</form>
</div>
}
templ (a *AdminPlugin) showPassword(entry PassEntry) {
<div><span>ID</span><span>{ fmt.Sprintf("%d", entry.ID) }</span></div>
<div><span>Password</span><span>{ entry.Secret }:{ entry.Pass }</span></div>
<div><span style="margin-right: 2em">ID</span><span>{ fmt.Sprintf(" %d", entry.ID) }</span></div>
<div><span style="margin-right: 2em">Password</span><span> { entry.Secret }:{ entry.Pass }</span></div>
}
templ (a *AdminPlugin) entries(items []PassEntry) {
@ -51,7 +51,6 @@ templ (a *AdminPlugin) entries(items []PassEntry) {
hx-delete="/apppass/api"
hx-confirm={ fmt.Sprintf("Are you sure you want to delete %d?", entry.ID) }
hx-target="#data"
hx-include="this,[name='password'],[name='secret']"
name="id" value={ fmt.Sprintf("%d", entry.ID) }>X</button>
{ fmt.Sprintf("%d", entry.ID) }
</li>

View File

@ -25,7 +25,7 @@ func (a *AdminPlugin) page() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid-container\"><form><div class=\"grid-x grid-margin-x align-bottom\"><h2>App Pass</h2></div><div class=\"grid-x grid-margin-x align-bottom\"><div class=\"cell auto\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"cell auto\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"cell auto\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"button\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"submit success button\">New</button></div></div></form><div class=\"grid-x\"><div class=\"cell\" id=\"data\"></div></div></div>")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid-container\"><form><div class=\"grid-x grid-margin-x align-bottom\"><h2>App Pass</h2></div><div class=\"grid-x grid-margin-x align-bottom\"><div class=\"cell auto\"><label for=\"password\">Password: <input type=\"text\" name=\"password\"></label></div><div class=\"cell auto\"><label for=\"secret\">Secret: <input type=\"text\" name=\"secret\"></label></div><div class=\"cell auto\"><button hx-post=\"/apppass/api\" hx-target=\"#data\" class=\"button\">List</button> <button hx-put=\"/apppass/api\" hx-target=\"#data\" class=\"submit success button\">New</button></div></div><div class=\"grid-x\"><div class=\"cell\" id=\"data\"></div></div></form></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -49,27 +49,27 @@ func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><span>ID</span><span>")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><span style=\"margin-right: 2em\">ID</span><span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID))
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(" %d", entry.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 35, Col: 59}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 35, Col: 86}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div><div><span>Password</span><span>")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div><div><span style=\"margin-right: 2em\">Password</span><span>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Secret)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 36, Col: 50}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 36, Col: 77}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@ -82,7 +82,7 @@ func (a *AdminPlugin) showPassword(entry PassEntry) templ.Component {
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Pass)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 36, Col: 65}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 36, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@ -135,7 +135,7 @@ func (a *AdminPlugin) entries(items []PassEntry) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" hx-include=\"this,[name=&#39;password&#39;],[name=&#39;secret&#39;]\" name=\"id\" value=\"")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"#data\" name=\"id\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -150,7 +150,7 @@ func (a *AdminPlugin) entries(items []PassEntry) templ.Component {
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", entry.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 55, Col: 57}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 54, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
@ -192,7 +192,7 @@ func renderError(err error) templ.Component {
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(err.Error())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 63, Col: 22}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 62, Col: 22}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
@ -234,7 +234,7 @@ func vars(items []configEntry) templ.Component {
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.Key)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 79, Col: 38}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 78, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
@ -247,7 +247,7 @@ func vars(items []configEntry) templ.Component {
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 79, Col: 61}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `plugins/admin/admin.templ`, Line: 78, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {

View File

@ -4,13 +4,12 @@ import (
"context"
"crypto/md5"
"crypto/rand"
"encoding/json"
"fmt"
"github.com/ggicci/httpin"
"github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log"
"golang.org/x/crypto/bcrypt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
@ -21,8 +20,15 @@ func (p *AdminPlugin) registerWeb() {
r.HandleFunc("/", p.handleVars)
p.bot.GetWeb().RegisterWebName(r, "/vars", "Variables")
r = chi.NewRouter()
r.HandleFunc("/verify", p.handleAppPassCheck)
r.HandleFunc("/api", p.handleAppPassAPI)
r.With(httpin.NewInput(AppPassCheckReq{})).
HandleFunc("/verify", p.handleAppPassCheck)
//r.HandleFunc("/api", p.handleAppPassAPI)
r.With(httpin.NewInput(AppPassAPIReq{})).
Put("/api", p.handleAppPassAPIPut)
r.With(httpin.NewInput(AppPassAPIReq{})).
Delete("/api", p.handleAppPassAPIDelete)
r.With(httpin.NewInput(AppPassAPIReq{})).
Post("/api", p.handleAppPassAPIPost)
r.HandleFunc("/", p.handleAppPass)
p.bot.GetWeb().RegisterWebName(r, "/apppass", "App Pass")
}
@ -32,8 +38,8 @@ func (p *AdminPlugin) handleAppPass(w http.ResponseWriter, r *http.Request) {
}
type PassEntry struct {
ID int64 `json:"id"`
Secret string `json:"secret"`
ID int64 `json:"id" in:"form=id;query=password"`
Secret string `json:"secret" in:"form=secret;query=password"`
// Should be null unless inserting a new entry
Pass string `json:"pass"`
@ -57,57 +63,55 @@ func (p *PassEntry) Compare(pass string) bool {
return true
}
func (p *AdminPlugin) handleAppPassCheck(w http.ResponseWriter, r *http.Request) {
req := struct {
type AppPassCheckReq struct {
Secret string `json:"secret"`
Password string `json:"password"`
}{}
body, _ := ioutil.ReadAll(r.Body)
_ = json.Unmarshal(body, &req)
if p.bot.CheckPassword(req.Secret, req.Password) {
}
func (p *AdminPlugin) handleAppPassCheck(w http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*AppPassCheckReq)
if p.bot.CheckPassword(input.Secret, input.Password) {
w.WriteHeader(204)
} else {
w.WriteHeader(403)
}
}
func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
type AppPassAPIReq struct {
Password string `in:"form=password;query=password"`
PassEntry PassEntry
}
func (p *AdminPlugin) checkAPIInput(w http.ResponseWriter, r *http.Request) *AppPassAPIReq {
input := r.Context().Value(httpin.Input).(*AppPassAPIReq)
if input.Password == "" && input.PassEntry.Secret == "" {
b, _ := io.ReadAll(r.Body)
query, _ := url.ParseQuery(string(b))
secret := r.FormValue("secret")
password := r.FormValue("password")
id, _ := strconv.ParseInt(r.FormValue("id"), 10, 64)
if !r.Form.Has("secret") && query.Has("secret") {
secret = query.Get("secret")
input.Password = query.Get("password")
input.PassEntry.ID, _ = strconv.ParseInt(query.Get("id"), 10, 64)
input.PassEntry.Secret = query.Get("secret")
}
if !r.Form.Has("password") && query.Has("password") {
password = query.Get("password")
}
if !r.Form.Has("id") && query.Has("id") {
id, _ = strconv.ParseInt(query.Get("id"), 10, 64)
}
req := struct {
Password string `json:"password"`
PassEntry PassEntry `json:"passEntry"`
}{
password,
PassEntry{
ID: id,
Secret: secret,
},
}
if req.PassEntry.Secret == "" {
log.Printf("checkAPIInput: %#v", input)
if input.PassEntry.Secret == "" {
writeErr(r.Context(), w, fmt.Errorf("missing secret"))
return
return nil
}
if req.Password == "" || !p.bot.CheckPassword(req.PassEntry.Secret, req.Password) {
if input.Password == "" || !p.bot.CheckPassword(input.PassEntry.Secret, input.Password) {
writeErr(r.Context(), w, fmt.Errorf("missing or incorrect password"))
return nil
}
return input
}
func (p *AdminPlugin) handleAppPassAPIPut(w http.ResponseWriter, r *http.Request) {
input := p.checkAPIInput(w, r)
if input == nil {
return
}
switch r.Method {
case http.MethodPut:
if string(req.PassEntry.Pass) == "" {
if string(input.PassEntry.Pass) == "" {
c := 10
b := make([]byte, c)
_, err := rand.Read(b)
@ -115,21 +119,21 @@ func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
fmt.Println("error:", err)
return
}
req.PassEntry.Pass = fmt.Sprintf("%x", md5.Sum(b))
input.PassEntry.Pass = fmt.Sprintf("%x", md5.Sum(b))
}
q := `insert into apppass (secret, encoded_pass, cost) values (?, ?, ?)`
req.PassEntry.EncodePass()
input.PassEntry.EncodePass()
check := bcrypt.CompareHashAndPassword(req.PassEntry.encodedPass, []byte(req.PassEntry.Pass))
check := bcrypt.CompareHashAndPassword(input.PassEntry.encodedPass, []byte(input.PassEntry.Pass))
log.Debug().
Str("secret", req.PassEntry.Secret).
Str("encoded", string(req.PassEntry.encodedPass)).
Str("password", string(req.PassEntry.Pass)).
Str("secret", input.PassEntry.Secret).
Str("encoded", string(input.PassEntry.encodedPass)).
Str("password", string(input.PassEntry.Pass)).
Interface("check", check).
Msg("debug pass creation")
res, err := p.db.Exec(q, req.PassEntry.Secret, req.PassEntry.encodedPass, req.PassEntry.Cost)
res, err := p.db.Exec(q, input.PassEntry.Secret, input.PassEntry.encodedPass, input.PassEntry.Cost)
if err != nil {
writeErr(r.Context(), w, err)
return
@ -139,25 +143,37 @@ func (p *AdminPlugin) handleAppPassAPI(w http.ResponseWriter, r *http.Request) {
writeErr(r.Context(), w, err)
return
}
req.PassEntry.ID = id
p.showPassword(req.PassEntry).Render(r.Context(), w)
return
case http.MethodDelete:
input.PassEntry.ID = id
p.showPassword(input.PassEntry).Render(r.Context(), w)
}
if req.PassEntry.ID <= 0 {
func (p *AdminPlugin) handleAppPassAPIDelete(w http.ResponseWriter, r *http.Request) {
input := p.checkAPIInput(w, r)
if input == nil {
return
}
if input.PassEntry.ID <= 0 {
writeErr(r.Context(), w, fmt.Errorf("missing ID"))
return
}
q := `delete from apppass where id = ?`
_, err := p.db.Exec(q, req.PassEntry.ID)
_, err := p.db.Exec(q, input.PassEntry.ID)
if err != nil {
writeErr(r.Context(), w, err)
return
}
p.handleAppPassAPIPost(w, r)
}
func (p *AdminPlugin) handleAppPassAPIPost(w http.ResponseWriter, r *http.Request) {
input := p.checkAPIInput(w, r)
if input == nil {
return
}
q := `select id,secret from apppass where secret = ?`
passEntries := []PassEntry{}
err := p.db.Select(&passEntries, q, req.PassEntry.Secret)
err := p.db.Select(&passEntries, q, input.PassEntry.Secret)
if err != nil {
writeErr(r.Context(), w, err)
return