mirror of https://github.com/velour/catbase.git
admin: apppass uses httpin
This commit is contained in:
parent
a6d224c87b
commit
b73e64ad72
|
@ -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>
|
||||
|
|
|
@ -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='password'],[name='secret']\" 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 {
|
||||
|
|
|
@ -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,107 +63,117 @@ func (p *PassEntry) Compare(pass string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
type AppPassCheckReq struct {
|
||||
Secret string `json:"secret"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (p *AdminPlugin) handleAppPassCheck(w http.ResponseWriter, r *http.Request) {
|
||||
req := 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) {
|
||||
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()
|
||||
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")
|
||||
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))
|
||||
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) == "" {
|
||||
c := 10
|
||||
b := make([]byte, c)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
return
|
||||
}
|
||||
req.PassEntry.Pass = fmt.Sprintf("%x", md5.Sum(b))
|
||||
}
|
||||
q := `insert into apppass (secret, encoded_pass, cost) values (?, ?, ?)`
|
||||
req.PassEntry.EncodePass()
|
||||
|
||||
check := bcrypt.CompareHashAndPassword(req.PassEntry.encodedPass, []byte(req.PassEntry.Pass))
|
||||
|
||||
log.Debug().
|
||||
Str("secret", req.PassEntry.Secret).
|
||||
Str("encoded", string(req.PassEntry.encodedPass)).
|
||||
Str("password", string(req.PassEntry.Pass)).
|
||||
Interface("check", check).
|
||||
Msg("debug pass creation")
|
||||
|
||||
res, err := p.db.Exec(q, req.PassEntry.Secret, req.PassEntry.encodedPass, req.PassEntry.Cost)
|
||||
if string(input.PassEntry.Pass) == "" {
|
||||
c := 10
|
||||
b := make([]byte, c)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
writeErr(r.Context(), w, err)
|
||||
fmt.Println("error:", err)
|
||||
return
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
req.PassEntry.ID = id
|
||||
p.showPassword(req.PassEntry).Render(r.Context(), w)
|
||||
input.PassEntry.Pass = fmt.Sprintf("%x", md5.Sum(b))
|
||||
}
|
||||
q := `insert into apppass (secret, encoded_pass, cost) values (?, ?, ?)`
|
||||
input.PassEntry.EncodePass()
|
||||
|
||||
check := bcrypt.CompareHashAndPassword(input.PassEntry.encodedPass, []byte(input.PassEntry.Pass))
|
||||
|
||||
log.Debug().
|
||||
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, input.PassEntry.Secret, input.PassEntry.encodedPass, input.PassEntry.Cost)
|
||||
if err != nil {
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
case http.MethodDelete:
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
input.PassEntry.ID = id
|
||||
p.showPassword(input.PassEntry).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
if req.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)
|
||||
if err != nil {
|
||||
writeErr(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
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, 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
|
||||
|
|
Loading…
Reference in New Issue