diff --git a/admin/accounthttp.go b/admin/accounthttp.go index b5deca2..9402410 100644 --- a/admin/accounthttp.go +++ b/admin/accounthttp.go @@ -30,15 +30,30 @@ func accountIndexHandler(app *model.AppState) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := r.Context().Value("session").(*model.Session) - totps, err := controller.GetTOTPsForAccount(app.DB, session.Account.ID) + dbTOTPs, err := controller.GetTOTPsForAccount(app.DB, session.Account.ID) if err != nil { fmt.Printf("WARN: Failed to fetch TOTPs: %v\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } - type accountResponse struct { - Session *model.Session - TOTPs []model.TOTP + type ( + TOTP struct { + model.TOTP + CreatedAtString string + } + + accountResponse struct { + Session *model.Session + TOTPs []TOTP + } + ) + + totps := []TOTP{} + for _, totp := range dbTOTPs { + totps = append(totps, TOTP{ + TOTP: totp, + CreatedAtString: totp.CreatedAt.Format("02 Jan 2006, 15:04:05"), + }) } sessionMessage := session.Message diff --git a/admin/http.go b/admin/http.go index 7dd5207..4d32aa9 100644 --- a/admin/http.go +++ b/admin/http.go @@ -284,26 +284,65 @@ func loginHandler(app *model.AppState) http.Handler { return } - totpMethod, err := controller.CheckTOTPForAccount(app.DB, account.ID, credentials.TOTP) - if err != nil { - fmt.Fprintf(os.Stderr, "WARN: Failed to fetch TOTPs: %v\n", err) - controller.SetSessionError(app.DB, session, "Something went wrong. Please try again.") - render() - return + var totpMethod *model.TOTP + if len(credentials.TOTP) == 0 { + // check if user has TOTP + totps, err := controller.GetTOTPsForAccount(app.DB, account.ID) + if err != nil { + fmt.Fprintf(os.Stderr, "WARN: Failed to fetch TOTPs: %v\n", err) + controller.SetSessionError(app.DB, session, "Something went wrong. Please try again.") + render() + return + } + + if len(totps) > 0 { + type loginTOTPData struct { + Session *model.Session + Username string + Password string + } + err = pages["login-totp"].Execute(w, loginTOTPData{ + Session: session, + Username: credentials.Username, + Password: credentials.Password, + }) + if err != nil { + fmt.Fprintf(os.Stderr, "WARN: Failed to render login TOTP page: %v\n", err) + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + } + } else { + totpMethod, err = controller.CheckTOTPForAccount(app.DB, account.ID, credentials.TOTP) + if err != nil { + fmt.Fprintf(os.Stderr, "WARN: Failed to fetch TOTPs: %v\n", err) + controller.SetSessionError(app.DB, session, "Something went wrong. Please try again.") + render() + return + } + if totpMethod == nil { + controller.SetSessionError(app.DB, session, "Invalid TOTP.") + render() + return + } } - if totpMethod == nil { - controller.SetSessionError(app.DB, session, "Invalid TOTP.") - render() - return + + if totpMethod != nil { + fmt.Printf( + "[%s] INFO: Account \"%s\" logged in with method \"%s\"\n", + time.Now().Format(time.UnixDate), + account.Username, + totpMethod.Name, + ) + } else { + fmt.Printf( + "[%s] INFO: Account \"%s\" logged in\n", + time.Now().Format(time.UnixDate), + account.Username, + ) } // TODO: log login activity to user - fmt.Printf( - "[%s] INFO: Account \"%s\" logged in with method \"%s\"\n", - time.Now().Format(time.UnixDate), - account.Username, - totpMethod.Name, - ) // login success! controller.SetSessionAccount(app.DB, session, account) diff --git a/admin/static/admin.css b/admin/static/admin.css index cbb827e..a6e0bc2 100644 --- a/admin/static/admin.css +++ b/admin/static/admin.css @@ -85,6 +85,15 @@ a img.icon { height: .8em; } +code { + background: #303030; + color: #f0f0f0; + padding: .23em .3em; + border-radius: 4px; +} + + + .card { margin-bottom: 1em; } @@ -93,13 +102,6 @@ a img.icon { margin: 0 0 .5em 0; } -/* -.card h3, -.card p { - margin: 0; -} -*/ - .card-title { margin-bottom: 1em; display: flex; diff --git a/admin/templates.go b/admin/templates.go index 3bae106..d9a74ca 100644 --- a/admin/templates.go +++ b/admin/templates.go @@ -18,6 +18,11 @@ var pages = map[string]*template.Template{ filepath.Join("views", "prideflag.html"), filepath.Join("admin", "views", "login.html"), )), + "login-totp": template.Must(template.ParseFiles( + filepath.Join("admin", "views", "layout.html"), + filepath.Join("views", "prideflag.html"), + filepath.Join("admin", "views", "login-totp.html"), + )), "register": template.Must(template.ParseFiles( filepath.Join("admin", "views", "layout.html"), filepath.Join("views", "prideflag.html"), diff --git a/admin/views/edit-account.html b/admin/views/edit-account.html index b1d083a..6c17088 100644 --- a/admin/views/edit-account.html +++ b/admin/views/edit-account.html @@ -40,11 +40,11 @@ {{range .TOTPs}}
-

{{.Name}}

-

Added: {{.CreatedAt}}

+

{{.TOTP.Name}}

+

Added: {{.CreatedAtString}}

- Delete + Delete
{{end}} diff --git a/admin/views/login-totp.html b/admin/views/login-totp.html new file mode 100644 index 0000000..d959e3c --- /dev/null +++ b/admin/views/login-totp.html @@ -0,0 +1,42 @@ +{{define "head"}} +Login - ari melody 💫 + + + +{{end}} + +{{define "content"}} +
+
+

Two-Factor Authentication

+ +
+ + + + +
+ + +
+
+{{end}} diff --git a/admin/views/login.html b/admin/views/login.html index b77af83..fbb7294 100644 --- a/admin/views/login.html +++ b/admin/views/login.html @@ -3,14 +3,6 @@ {{end}} @@ -42,15 +34,14 @@ input { {{end}}
+

Log In

+
- + - - -
diff --git a/admin/views/register.html b/admin/views/register.html index 94170c9..37f0947 100644 --- a/admin/views/register.html +++ b/admin/views/register.html @@ -27,7 +27,7 @@ form button { } input { - width: 100%; + width: calc(100% - 1rem - 2px); } {{end}} @@ -39,9 +39,11 @@ input { {{end}} +

Create Account

+
- + diff --git a/admin/views/totp-confirm.html b/admin/views/totp-confirm.html index af6b6e1..ac39e52 100644 --- a/admin/views/totp-confirm.html +++ b/admin/views/totp-confirm.html @@ -26,7 +26,7 @@ code {

- + diff --git a/admin/views/totp-setup.html b/admin/views/totp-setup.html index 62b9daf..e74c970 100644 --- a/admin/views/totp-setup.html +++ b/admin/views/totp-setup.html @@ -12,7 +12,7 @@
- +
diff --git a/controller/totp.go b/controller/totp.go index 02f1c4b..bc71747 100644 --- a/controller/totp.go +++ b/controller/totp.go @@ -92,7 +92,6 @@ func GetTOTPsForAccount(db *sqlx.DB, accountID string) ([]model.TOTP, error) { func CheckTOTPForAccount(db *sqlx.DB, accountID string, totp string) (*model.TOTP, error) { totps, err := GetTOTPsForAccount(db, accountID) if err != nil { - // user has no TOTP methods return nil, err } for _, method := range totps {