diff --git a/auth/auth.qtpl b/auth/auth.qtpl deleted file mode 100644 index 1a27ce7..0000000 --- a/auth/auth.qtpl +++ /dev/null @@ -1,143 +0,0 @@ -{% import "net/http" %} -{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %} -{% import "github.com/bouncepaw/mycorrhiza/l18n" %} - -{% func Register(rq *http.Request) %} -{% code - lc := l18n.FromRequest(rq) -%} -
-
- {% if cfg.AllowRegistration %} - - {%= telegramWidget(lc) %} - {% elseif cfg.UseAuth %} -

{%s lc.Get("auth.noregister") %}

-

← {%s lc.Get("auth.go_back") %}

- {% else %} -

{%s lc.Get("auth.noauth") %}

-

← {%s lc.Get("auth.go_back") %}

- {% endif %} -
-
-{% endfunc %} - -{% func Login(lc *l18n.Localizer) %} -
-
- {% if cfg.UseAuth %} - - {%= telegramWidget(lc) %} - {% else %} -

{%s lc.Get("auth.noauth") %}

-

← {%s lc.Get("auth.go_home") %}

- {% endif %} -
-
-{% endfunc %} - -Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't. -{% func telegramWidget(lc *l18n.Localizer) %} -{% if cfg.TelegramEnabled %} -

{%s lc.Get("auth.telegram_tip") %}

- -{% endif %} -{% endfunc %} - -{% func LoginError(err string, lc *l18n.Localizer) %} -
-
- {% switch err %} - {% case "unknown username" %} -

{%s lc.Get("auth.error_username") %}

- {% case "wrong password" %} -

{%s lc.Get("auth.error_password") %}

- {% default %} -

{%s err %}

- {% endswitch %} -

← {%s lc.Get("auth.try_again") %}

-
-
-{% endfunc %} - -{% func Logout(can bool, lc *l18n.Localizer) %} -
-
- {% if can %} -

{%s lc.Get("auth.logout_header") %}

-
- - {%s lc.Get("auth.go_home") %} -
- {% else %} -

{%s lc.Get("auth.logout_anon") %}

-

{%s lc.Get("auth.login_title") %}

-

← {%s lc.Get("auth.go_home") %}

- {% endif %} -
-
-{% endfunc %} - -{% func Lock(lc *l18n.Localizer) %} - - - - - - 🔒 {%s lc.Get("auth.lock_title") %} - - - - -
-
-

🔒

-

{%s lc.Get("auth.lock_title") %}

- - {%= telegramWidget(lc) %} -
-
- - -{% endfunc %} - diff --git a/auth/auth.qtpl.go b/auth/auth.qtpl.go deleted file mode 100644 index 79d7218..0000000 --- a/auth/auth.qtpl.go +++ /dev/null @@ -1,597 +0,0 @@ -// Code generated by qtc from "auth.qtpl". DO NOT EDIT. -// See https://github.com/valyala/quicktemplate for details. - -//line auth/auth.qtpl:1 -package auth - -//line auth/auth.qtpl:1 -import "net/http" - -//line auth/auth.qtpl:2 -import "github.com/bouncepaw/mycorrhiza/internal/cfg" - -//line auth/auth.qtpl:3 -import "github.com/bouncepaw/mycorrhiza/l18n" - -//line auth/auth.qtpl:5 -import ( - qtio422016 "io" - - qt422016 "github.com/valyala/quicktemplate" -) - -//line auth/auth.qtpl:5 -var ( - _ = qtio422016.Copy - _ = qt422016.AcquireByteBuffer -) - -//line auth/auth.qtpl:5 -func StreamRegister(qw422016 *qt422016.Writer, rq *http.Request) { -//line auth/auth.qtpl:5 - qw422016.N().S(` -`) -//line auth/auth.qtpl:7 - lc := l18n.FromRequest(rq) - -//line auth/auth.qtpl:8 - qw422016.N().S(` -
-
- `) -//line auth/auth.qtpl:11 - if cfg.AllowRegistration { -//line auth/auth.qtpl:11 - qw422016.N().S(` - - `) -//line auth/auth.qtpl:29 - streamtelegramWidget(qw422016, lc) -//line auth/auth.qtpl:29 - qw422016.N().S(` - `) -//line auth/auth.qtpl:30 - } else if cfg.UseAuth { -//line auth/auth.qtpl:30 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:31 - qw422016.E().S(lc.Get("auth.noregister")) -//line auth/auth.qtpl:31 - qw422016.N().S(`

-

← `) -//line auth/auth.qtpl:32 - qw422016.E().S(lc.Get("auth.go_back")) -//line auth/auth.qtpl:32 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:33 - } else { -//line auth/auth.qtpl:33 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:34 - qw422016.E().S(lc.Get("auth.noauth")) -//line auth/auth.qtpl:34 - qw422016.N().S(`

-

← `) -//line auth/auth.qtpl:35 - qw422016.E().S(lc.Get("auth.go_back")) -//line auth/auth.qtpl:35 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:36 - } -//line auth/auth.qtpl:36 - qw422016.N().S(` -
-
-`) -//line auth/auth.qtpl:39 -} - -//line auth/auth.qtpl:39 -func WriteRegister(qq422016 qtio422016.Writer, rq *http.Request) { -//line auth/auth.qtpl:39 - qw422016 := qt422016.AcquireWriter(qq422016) -//line auth/auth.qtpl:39 - StreamRegister(qw422016, rq) -//line auth/auth.qtpl:39 - qt422016.ReleaseWriter(qw422016) -//line auth/auth.qtpl:39 -} - -//line auth/auth.qtpl:39 -func Register(rq *http.Request) string { -//line auth/auth.qtpl:39 - qb422016 := qt422016.AcquireByteBuffer() -//line auth/auth.qtpl:39 - WriteRegister(qb422016, rq) -//line auth/auth.qtpl:39 - qs422016 := string(qb422016.B) -//line auth/auth.qtpl:39 - qt422016.ReleaseByteBuffer(qb422016) -//line auth/auth.qtpl:39 - return qs422016 -//line auth/auth.qtpl:39 -} - -//line auth/auth.qtpl:41 -func StreamLogin(qw422016 *qt422016.Writer, lc *l18n.Localizer) { -//line auth/auth.qtpl:41 - qw422016.N().S(` -
-
- `) -//line auth/auth.qtpl:44 - if cfg.UseAuth { -//line auth/auth.qtpl:44 - qw422016.N().S(` - - `) -//line auth/auth.qtpl:60 - streamtelegramWidget(qw422016, lc) -//line auth/auth.qtpl:60 - qw422016.N().S(` - `) -//line auth/auth.qtpl:61 - } else { -//line auth/auth.qtpl:61 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:62 - qw422016.E().S(lc.Get("auth.noauth")) -//line auth/auth.qtpl:62 - qw422016.N().S(`

-

← `) -//line auth/auth.qtpl:63 - qw422016.E().S(lc.Get("auth.go_home")) -//line auth/auth.qtpl:63 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:64 - } -//line auth/auth.qtpl:64 - qw422016.N().S(` -
-
-`) -//line auth/auth.qtpl:67 -} - -//line auth/auth.qtpl:67 -func WriteLogin(qq422016 qtio422016.Writer, lc *l18n.Localizer) { -//line auth/auth.qtpl:67 - qw422016 := qt422016.AcquireWriter(qq422016) -//line auth/auth.qtpl:67 - StreamLogin(qw422016, lc) -//line auth/auth.qtpl:67 - qt422016.ReleaseWriter(qw422016) -//line auth/auth.qtpl:67 -} - -//line auth/auth.qtpl:67 -func Login(lc *l18n.Localizer) string { -//line auth/auth.qtpl:67 - qb422016 := qt422016.AcquireByteBuffer() -//line auth/auth.qtpl:67 - WriteLogin(qb422016, lc) -//line auth/auth.qtpl:67 - qs422016 := string(qb422016.B) -//line auth/auth.qtpl:67 - qt422016.ReleaseByteBuffer(qb422016) -//line auth/auth.qtpl:67 - return qs422016 -//line auth/auth.qtpl:67 -} - -// Telegram auth widget was requested by Yogurt. As you can see, we don't offer user administrators control over it. Of course we don't. - -//line auth/auth.qtpl:70 -func streamtelegramWidget(qw422016 *qt422016.Writer, lc *l18n.Localizer) { -//line auth/auth.qtpl:70 - qw422016.N().S(` -`) -//line auth/auth.qtpl:71 - if cfg.TelegramEnabled { -//line auth/auth.qtpl:71 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:72 - qw422016.E().S(lc.Get("auth.telegram_tip")) -//line auth/auth.qtpl:72 - qw422016.N().S(`

- -`) -//line auth/auth.qtpl:74 - } -//line auth/auth.qtpl:74 - qw422016.N().S(` -`) -//line auth/auth.qtpl:75 -} - -//line auth/auth.qtpl:75 -func writetelegramWidget(qq422016 qtio422016.Writer, lc *l18n.Localizer) { -//line auth/auth.qtpl:75 - qw422016 := qt422016.AcquireWriter(qq422016) -//line auth/auth.qtpl:75 - streamtelegramWidget(qw422016, lc) -//line auth/auth.qtpl:75 - qt422016.ReleaseWriter(qw422016) -//line auth/auth.qtpl:75 -} - -//line auth/auth.qtpl:75 -func telegramWidget(lc *l18n.Localizer) string { -//line auth/auth.qtpl:75 - qb422016 := qt422016.AcquireByteBuffer() -//line auth/auth.qtpl:75 - writetelegramWidget(qb422016, lc) -//line auth/auth.qtpl:75 - qs422016 := string(qb422016.B) -//line auth/auth.qtpl:75 - qt422016.ReleaseByteBuffer(qb422016) -//line auth/auth.qtpl:75 - return qs422016 -//line auth/auth.qtpl:75 -} - -//line auth/auth.qtpl:77 -func StreamLoginError(qw422016 *qt422016.Writer, err string, lc *l18n.Localizer) { -//line auth/auth.qtpl:77 - qw422016.N().S(` -
-
- `) -//line auth/auth.qtpl:80 - switch err { -//line auth/auth.qtpl:81 - case "unknown username": -//line auth/auth.qtpl:81 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:82 - qw422016.E().S(lc.Get("auth.error_username")) -//line auth/auth.qtpl:82 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:83 - case "wrong password": -//line auth/auth.qtpl:83 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:84 - qw422016.E().S(lc.Get("auth.error_password")) -//line auth/auth.qtpl:84 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:85 - default: -//line auth/auth.qtpl:85 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:86 - qw422016.E().S(err) -//line auth/auth.qtpl:86 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:87 - } -//line auth/auth.qtpl:87 - qw422016.N().S(` -

← `) -//line auth/auth.qtpl:88 - qw422016.E().S(lc.Get("auth.try_again")) -//line auth/auth.qtpl:88 - qw422016.N().S(`

-
-
-`) -//line auth/auth.qtpl:91 -} - -//line auth/auth.qtpl:91 -func WriteLoginError(qq422016 qtio422016.Writer, err string, lc *l18n.Localizer) { -//line auth/auth.qtpl:91 - qw422016 := qt422016.AcquireWriter(qq422016) -//line auth/auth.qtpl:91 - StreamLoginError(qw422016, err, lc) -//line auth/auth.qtpl:91 - qt422016.ReleaseWriter(qw422016) -//line auth/auth.qtpl:91 -} - -//line auth/auth.qtpl:91 -func LoginError(err string, lc *l18n.Localizer) string { -//line auth/auth.qtpl:91 - qb422016 := qt422016.AcquireByteBuffer() -//line auth/auth.qtpl:91 - WriteLoginError(qb422016, err, lc) -//line auth/auth.qtpl:91 - qs422016 := string(qb422016.B) -//line auth/auth.qtpl:91 - qt422016.ReleaseByteBuffer(qb422016) -//line auth/auth.qtpl:91 - return qs422016 -//line auth/auth.qtpl:91 -} - -//line auth/auth.qtpl:93 -func StreamLogout(qw422016 *qt422016.Writer, can bool, lc *l18n.Localizer) { -//line auth/auth.qtpl:93 - qw422016.N().S(` -
-
- `) -//line auth/auth.qtpl:96 - if can { -//line auth/auth.qtpl:96 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:97 - qw422016.E().S(lc.Get("auth.logout_header")) -//line auth/auth.qtpl:97 - qw422016.N().S(`

-
- - `) -//line auth/auth.qtpl:100 - qw422016.E().S(lc.Get("auth.go_home")) -//line auth/auth.qtpl:100 - qw422016.N().S(` -
- `) -//line auth/auth.qtpl:102 - } else { -//line auth/auth.qtpl:102 - qw422016.N().S(` -

`) -//line auth/auth.qtpl:103 - qw422016.E().S(lc.Get("auth.logout_anon")) -//line auth/auth.qtpl:103 - qw422016.N().S(`

-

`) -//line auth/auth.qtpl:104 - qw422016.E().S(lc.Get("auth.login_title")) -//line auth/auth.qtpl:104 - qw422016.N().S(`

-

← `) -//line auth/auth.qtpl:105 - qw422016.E().S(lc.Get("auth.go_home")) -//line auth/auth.qtpl:105 - qw422016.N().S(`

- `) -//line auth/auth.qtpl:106 - } -//line auth/auth.qtpl:106 - qw422016.N().S(` -
-
-`) -//line auth/auth.qtpl:109 -} - -//line auth/auth.qtpl:109 -func WriteLogout(qq422016 qtio422016.Writer, can bool, lc *l18n.Localizer) { -//line auth/auth.qtpl:109 - qw422016 := qt422016.AcquireWriter(qq422016) -//line auth/auth.qtpl:109 - StreamLogout(qw422016, can, lc) -//line auth/auth.qtpl:109 - qt422016.ReleaseWriter(qw422016) -//line auth/auth.qtpl:109 -} - -//line auth/auth.qtpl:109 -func Logout(can bool, lc *l18n.Localizer) string { -//line auth/auth.qtpl:109 - qb422016 := qt422016.AcquireByteBuffer() -//line auth/auth.qtpl:109 - WriteLogout(qb422016, can, lc) -//line auth/auth.qtpl:109 - qs422016 := string(qb422016.B) -//line auth/auth.qtpl:109 - qt422016.ReleaseByteBuffer(qb422016) -//line auth/auth.qtpl:109 - return qs422016 -//line auth/auth.qtpl:109 -} - -//line auth/auth.qtpl:111 -func StreamLock(qw422016 *qt422016.Writer, lc *l18n.Localizer) { -//line auth/auth.qtpl:111 - qw422016.N().S(` - - - - - - 🔒 `) -//line auth/auth.qtpl:117 - qw422016.E().S(lc.Get("auth.lock_title")) -//line auth/auth.qtpl:117 - qw422016.N().S(` - - - - -
-
-

🔒

-

`) -//line auth/auth.qtpl:125 - qw422016.E().S(lc.Get("auth.lock_title")) -//line auth/auth.qtpl:125 - qw422016.N().S(`

- - `) -//line auth/auth.qtpl:137 - streamtelegramWidget(qw422016, lc) -//line auth/auth.qtpl:137 - qw422016.N().S(` -
-
- - -`) -//line auth/auth.qtpl:142 -} - -//line auth/auth.qtpl:142 -func WriteLock(qq422016 qtio422016.Writer, lc *l18n.Localizer) { -//line auth/auth.qtpl:142 - qw422016 := qt422016.AcquireWriter(qq422016) -//line auth/auth.qtpl:142 - StreamLock(qw422016, lc) -//line auth/auth.qtpl:142 - qt422016.ReleaseWriter(qw422016) -//line auth/auth.qtpl:142 -} - -//line auth/auth.qtpl:142 -func Lock(lc *l18n.Localizer) string { -//line auth/auth.qtpl:142 - qb422016 := qt422016.AcquireByteBuffer() -//line auth/auth.qtpl:142 - WriteLock(qb422016, lc) -//line auth/auth.qtpl:142 - qs422016 := string(qb422016.B) -//line auth/auth.qtpl:142 - qt422016.ReleaseByteBuffer(qb422016) -//line auth/auth.qtpl:142 - return qs422016 -//line auth/auth.qtpl:142 -} diff --git a/internal/user/net.go b/internal/user/net.go index 03a8b48..00f60c2 100644 --- a/internal/user/net.go +++ b/internal/user/net.go @@ -79,6 +79,11 @@ func Register(username, password, group, source string, force bool) error { return SaveUserDatabase() } +var ( + ErrUnknownUsername = errors.New("unknown username") + ErrWrongPassword = errors.New("wrong password") +) + // LoginDataHTTP logs such user in and returns string representation of an error if there is any. // // The HTTP parameters are used for setting header status (bad request, if it is bad) and saving a cookie. @@ -87,12 +92,12 @@ func LoginDataHTTP(w http.ResponseWriter, username, password string) error { if !HasUsername(username) { w.WriteHeader(http.StatusBadRequest) log.Println("Unknown username", username, "was entered") - return errors.New("unknown username") + return ErrUnknownUsername } if !CredentialsOK(username, password) { w.WriteHeader(http.StatusBadRequest) log.Println("A wrong password was entered for username", username) - return errors.New("wrong password") + return ErrWrongPassword } token, err := AddSession(username) if err != nil { diff --git a/l18n/en/auth.json b/l18n/en/auth.json index 307420a..3ead3dd 100644 --- a/l18n/en/auth.json +++ b/l18n/en/auth.json @@ -3,33 +3,29 @@ "password": "Password", "register_title": "Register", - "register_header": "Register on {{.name}}", + "register_header": "", "register_button": "Register", - - "login_title": "Login", - "login_header": "Log in to {{.name}}", - "login_button": "Log in", - "logout_title": "Logout?", + "logout_title": "", "logout_header": "Log out?", "logout_button": "Confirm", - "logout_anon": "You cannot log out because you are not logged in.", + "logout_anon": "", "lock_title": "Locked", - "password_tip": "The server stores your password in an encrypted form; even administrators cannot read it.", - "cookie_tip": "By submitting this form you give this wiki a permission to store cookies in your browser. It lets the engine associate your edits with you. You will stay logged in until you log out.", - "telegram_tip": "You can log in using Telegram. It only works if you have set your @username in Telegram and this username is free on this wiki.", + "password_tip": "", + "cookie_tip": "", + "telegram_tip": "", - "noauth": "Authentication is disabled. You can make edits anonymously.", + "noauth": "", "noregister": "Registrations are currently closed. Administrators can make an account for you by hand; contact them.", - "error_username": "Unknown username.", - "error_password": "Wrong password.", - "error_telegram": "Could not authorize using Telegram.", + "error_username": "", + "error_password": "", + "error_telegram": "", "go_back": "Go back", - "go_home": "Go home", + "go_home": "", "go_login": "Go to the login page", "try_again": "Try again" } diff --git a/l18n/ru/auth.json b/l18n/ru/auth.json index 008bb35..1b1943a 100644 --- a/l18n/ru/auth.json +++ b/l18n/ru/auth.json @@ -2,31 +2,31 @@ "username": "Логин", "password": "Пароль", - "register_title": "Регистрация", - "register_header": "Регистрация на «{{.name}}»", - "register_button": "Зарегистрироваться", + "register_title": "", + "register_header": "", + "register_button": "", "login_title": "Вход", - "login_header": "Вход в «{{.name}}»", - "login_button": "Войти", + "login_header": "", + "login_button": "", - "logout_title": "Выйти?", - "logout_header": "Выйти?", + "logout_title": "", + "logout_header": "?", "logout_button": "Подтвердить", - "logout_anon": "Вы не можете выйти, потому что ещё не вошли.", + "logout_anon": "", - "lock_title": "Доступ закрыт", + "lock_title": "", - "password_tip": "Сервер хранит ваш пароль в зашифрованном виде, даже администраторы не смогут его прочесть.", - "cookie_tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.", + "password_tip": "", + "cookie_tip": "", "telegram_tip": "Вы можете войти с помощью Телеграм. Это сработает, если у вашего профиля есть @имя, и оно не занято в этой вики.", - "noauth": "Аутентификация отключена. Вы можете делать правки анонимно.", + "noauth": "", "noregister": "Регистрация в текущее время недоступна. Администраторы могут вручную создать вам учётную запись, свяжитесь с ними.", - "error_username": "Неизвестное имя пользователя.", + "error_username": "", "error_password": "Неверный пароль.", - "error_telegram": "Не удалось авторизоваться через Телеграм.", + "error_telegram": "", "go_back": "Назад", "go_home": "Домой", diff --git a/main.go b/main.go index c0b4ba7..3ed5f5d 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ // //go:generate go run github.com/valyala/quicktemplate/qtc -dir=history //go:generate go run github.com/valyala/quicktemplate/qtc -dir=mycoopts -//go:generate go run github.com/valyala/quicktemplate/qtc -dir=auth package main import ( diff --git a/web/admin.go b/web/admin.go index e40aca7..c01abce 100644 --- a/web/admin.go +++ b/web/admin.go @@ -2,16 +2,17 @@ package web import ( "fmt" - "github.com/bouncepaw/mycorrhiza/internal/cfg" - user2 "github.com/bouncepaw/mycorrhiza/internal/user" - "github.com/bouncepaw/mycorrhiza/util" - viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil" - "github.com/gorilla/mux" "log" "mime" "net/http" "os" "sort" + + "github.com/bouncepaw/mycorrhiza/internal/cfg" + "github.com/bouncepaw/mycorrhiza/internal/user" + "github.com/bouncepaw/mycorrhiza/util" + "github.com/bouncepaw/mycorrhiza/web/viewutil" + "github.com/gorilla/mux" ) const adminTranslationRu = ` @@ -52,53 +53,53 @@ const adminTranslationRu = ` {{define "delete user warning"}}Вы уверены, что хотите удалить этого пользователя из базы данных? Это действие нельзя отменить.{{end}} ` -func viewPanel(meta viewutil2.Meta) { - viewutil2.ExecutePage(meta, panelChain, &viewutil2.BaseData{}) +func viewPanel(meta viewutil.Meta) { + viewutil.ExecutePage(meta, panelChain, &viewutil.BaseData{}) } type listData struct { - *viewutil2.BaseData + *viewutil.BaseData UserHypha string - Users []*user2.User + Users []*user.User } -func viewList(meta viewutil2.Meta, users []*user2.User) { - viewutil2.ExecutePage(meta, listChain, listData{ - BaseData: &viewutil2.BaseData{}, +func viewList(meta viewutil.Meta, users []*user.User) { + viewutil.ExecutePage(meta, listChain, listData{ + BaseData: &viewutil.BaseData{}, UserHypha: cfg.UserHypha, Users: users, }) } type newUserData struct { - *viewutil2.BaseData + *viewutil.BaseData Form util.FormData } -func viewNewUser(meta viewutil2.Meta, form util.FormData) { - viewutil2.ExecutePage(meta, newUserChain, newUserData{ - BaseData: &viewutil2.BaseData{}, +func viewNewUser(meta viewutil.Meta, form util.FormData) { + viewutil.ExecutePage(meta, newUserChain, newUserData{ + BaseData: &viewutil.BaseData{}, Form: form, }) } type editDeleteUserData struct { - *viewutil2.BaseData + *viewutil.BaseData Form util.FormData - U *user2.User + U *user.User } -func viewEditUser(meta viewutil2.Meta, form util.FormData, u *user2.User) { - viewutil2.ExecutePage(meta, editUserChain, editDeleteUserData{ - BaseData: &viewutil2.BaseData{}, +func viewEditUser(meta viewutil.Meta, form util.FormData, u *user.User) { + viewutil.ExecutePage(meta, editUserChain, editDeleteUserData{ + BaseData: &viewutil.BaseData{}, Form: form, U: u, }) } -func viewDeleteUser(meta viewutil2.Meta, form util.FormData, u *user2.User) { - viewutil2.ExecutePage(meta, deleteUserChain, editDeleteUserData{ - BaseData: &viewutil2.BaseData{}, +func viewDeleteUser(meta viewutil.Meta, form util.FormData, u *user.User) { + viewutil.ExecutePage(meta, deleteUserChain, editDeleteUserData{ + BaseData: &viewutil.BaseData{}, Form: form, U: u, }) @@ -108,12 +109,12 @@ func viewDeleteUser(meta viewutil2.Meta, form util.FormData, u *user2.User) { func handlerAdmin(w http.ResponseWriter, rq *http.Request) { w.Header().Set("Content-Type", "text/html;charset=utf-8") w.WriteHeader(http.StatusOK) - viewPanel(viewutil2.MetaFrom(w, rq)) + viewPanel(viewutil.MetaFrom(w, rq)) } // handlerAdminShutdown kills the wiki. func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) { - if user2.CanProceed(rq, "admin/shutdown") { + if user.CanProceed(rq, "admin/shutdown") { log.Println("An admin commanded the wiki to shutdown") os.Exit(0) } @@ -121,7 +122,7 @@ func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) { // handlerAdminReindexUsers reinitialises the user system. func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) { - user2.ReadUsersFromFilesystem() + user.ReadUsersFromFilesystem() redirectTo := rq.Referer() if redirectTo == "" { redirectTo = "/hypha/" + cfg.UserHypha @@ -131,8 +132,8 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) { func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) { // Get a sorted list of users - var users []*user2.User - for u := range user2.YieldUsers() { + var users []*user.User + for u := range user.YieldUsers() { users = append(users, u) } @@ -140,12 +141,12 @@ func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) { less := users[i].RegisteredAt.Before(users[j].RegisteredAt) return less }) - viewList(viewutil2.MetaFrom(w, rq), users) + viewList(viewutil.MetaFrom(w, rq), users) } func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) { vars := mux.Vars(rq) - u := user2.ByName(vars["username"]) + u := user.ByName(vars["username"]) if u == nil { util.HTTP404Page(w, "404 page not found") return @@ -157,9 +158,9 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) { oldGroup := u.Group newGroup := f.Get("group") - if user2.ValidGroup(newGroup) { + if user.ValidGroup(newGroup) { u.Group = newGroup - if err := user2.SaveUserDatabase(); err != nil { + if err := user.SaveUserDatabase(); err != nil { u.Group = oldGroup log.Println(err) f = f.WithError(err) @@ -179,12 +180,12 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) { } w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - viewEditUser(viewutil2.MetaFrom(w, rq), f, u) + viewEditUser(viewutil.MetaFrom(w, rq), f, u) } func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) { vars := mux.Vars(rq) - u := user2.ByName(vars["username"]) + u := user.ByName(vars["username"]) if u == nil { util.HTTP404Page(w, "404 page not found") return @@ -204,7 +205,7 @@ func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) { if err := u.ChangePassword(password); err != nil { f = f.WithError(err) } else { - if err := user2.SaveUserDatabase(); err != nil { + if err := user.SaveUserDatabase(); err != nil { u.Password = previousPassword f = f.WithError(err) } else { @@ -222,12 +223,12 @@ func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) { } w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - viewEditUser(viewutil2.MetaFrom(w, rq), f, u) + viewEditUser(viewutil.MetaFrom(w, rq), f, u) } func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) { vars := mux.Vars(rq) - u := user2.ByName(vars["username"]) + u := user.ByName(vars["username"]) if u == nil { util.HTTP404Page(w, "404 page not found") return @@ -236,7 +237,7 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) { f := util.NewFormData() if rq.Method == http.MethodPost { - f = f.WithError(user2.DeleteUser(u.Name)) + f = f.WithError(user.DeleteUser(u.Name)) if !f.HasError() { http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther) } else { @@ -248,23 +249,23 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) { w.WriteHeader(http.StatusBadRequest) } w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - viewDeleteUser(viewutil2.MetaFrom(w, rq), f, u) + viewDeleteUser(viewutil.MetaFrom(w, rq), f, u) } func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) { if rq.Method == http.MethodGet { w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - viewNewUser(viewutil2.MetaFrom(w, rq), util.NewFormData()) + viewNewUser(viewutil.MetaFrom(w, rq), util.NewFormData()) } else if rq.Method == http.MethodPost { // Create a user f := util.FormDataFromRequest(rq, []string{"name", "password", "group"}) - err := user2.Register(f.Get("name"), f.Get("password"), f.Get("group"), "local", true) + err := user.Register(f.Get("name"), f.Get("password"), f.Get("group"), "local", true) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Header().Set("Content-Type", mime.TypeByExtension(".html")) - viewNewUser(viewutil2.MetaFrom(w, rq), f.WithError(err)) + viewNewUser(viewutil.MetaFrom(w, rq), f.WithError(err)) } else { http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther) } diff --git a/web/newtmpl/newtmpl.go b/web/newtmpl/newtmpl.go index b73b1cc..b0184c5 100644 --- a/web/newtmpl/newtmpl.go +++ b/web/newtmpl/newtmpl.go @@ -107,6 +107,7 @@ func (p *Page) RenderTo(meta viewutil.Meta, data map[string]any) error { data["CommonScripts"] = cfg.CommonScripts data["EditScripts"] = cfg.EditScripts data["HeaderLinks"] = viewutil.HeaderLinks + data["UseAuth"] = cfg.UseAuth tmpl := p.TemplateEnglish if meta.LocaleIsRussian() { diff --git a/web/pages.go b/web/pages.go index 0598d3d..2b32276 100644 --- a/web/pages.go +++ b/web/pages.go @@ -12,6 +12,8 @@ var fs embed.FS var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page var pageHyphaDelete, pageHyphaEdit, pageHyphaEmpty, pageHypha *newtmpl.Page var pageRevision, pageMedia *newtmpl.Page +var pageAuthLock, pageAuthLogin, pageAuthLogout, pageAuthRegister, pageAuthTelegram *newtmpl.Page + var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain func initPages() { @@ -136,4 +138,42 @@ func initPages() { "remove tip": "Заметьте, чтобы заменить медиа, вам не нужно его перед этим откреплять.", "remove btn": "Открепить", }, "views/hypha-media.html") + + pageAuthLock = newtmpl.NewPage(fs, map[string]string{ + "lock title": "Доступ закрыт", + "username": "Логин", + "password": "Пароль", + "log in": "Войти", + }, "views/auth-telegram.html", "views/auth-lock.html") + + pageAuthLogin = newtmpl.NewPage(fs, map[string]string{ + "username": "Логин", + "password": "Пароль", + "log in": "Войти", + "cookie tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.", + "log in to x": "Войти в {{.}}", + "auth disabled": "Аутентификация отключена. Вы можете делать правки анонимно.", + "error username": "Неизвестное имя пользователя.", + "error password": "Неправильный пароль.", + "error telegram": "Не удалось войти через Телеграм.", + "go home": "Домой", + }, "views/auth-telegram.html", "views/auth-login.html") + + pageAuthLogout = newtmpl.NewPage(fs, map[string]string{ + "log out?": "Выйти?", + "log out": "Выйти", + "cannot log out anon": "Вы не можете выйти, потому что ещё не вошли.", + "log in": "Войти", + "go home": "Домой", + }, "views/auth-logout.html") + + pageAuthRegister = newtmpl.NewPage(fs, map[string]string{ + "username": "Логин", + "password": "Пароль", + "cookie tip": "Отправляя эту форму, вы разрешаете вики хранить cookie в вашем браузере. Это позволит движку связывать ваши правки с вашей учётной записью. Вы будете авторизованы, пока не выйдете из учётной записи.", + "password tip": "Сервер хранит ваш пароль в зашифрованном виде, даже администраторы не смогут его прочесть.", + "register btn": "Зарегистрироваться", + "register on x": "Регистрация на {{.}}", + }, "views/auth-telegram.html", "views/auth-register.html") + } diff --git a/web/views/auth-lock.html b/web/views/auth-lock.html new file mode 100644 index 0000000..0784400 --- /dev/null +++ b/web/views/auth-lock.html @@ -0,0 +1,33 @@ +{{define "title"}}{{block "lock title" .}}Locked{{end}}{{end}} +{{define "page"}} + + + + + + 🔒 {{template "lock title" .}} + + + + +
+
+

🔒

+

{{template "lock title" .}}

+ + {{template "telegram widget"}} +
+
+ + +{{end}} \ No newline at end of file diff --git a/web/views/auth-login.html b/web/views/auth-login.html new file mode 100644 index 0000000..73ecb8a --- /dev/null +++ b/web/views/auth-login.html @@ -0,0 +1,39 @@ +{{define "log in to x"}}Log in to {{.}}{{end}} +{{define "title"}}{{template "log in to x" .WikiName}}{{end}} +{{define "body"}} +
+
+ {{if .UseAuth}} + {{if .ErrUnknownUsername}} +

{{block "error username" .}}Unknown username.{{end}}

+ {{else if .ErrWrongPassword}} +

{{block "error password" .}}Wrong password.{{end}}

+ {{else if .ErrTelegram}} +

{{block "error telegram" .}}Could not authorize using Telegram.{{end}}

+ {{else if .Err}} +

{{.Err}}

+ {{end}} + + + {{template "telegram widget" .}} + {{else}} +

{{block "auth disabled" .}}Authentication is disabled. You can make edits anonymously.{{end}}

+

← {{block "go home" .}}Go home{{end}}

+ {{end}} +
+
+{{end}} \ No newline at end of file diff --git a/web/views/auth-logout.html b/web/views/auth-logout.html new file mode 100644 index 0000000..7a40f6c --- /dev/null +++ b/web/views/auth-logout.html @@ -0,0 +1,18 @@ +{{define "title"}}{{end}} +{{define "body"}} +
+
+ {{if .CanLogout}} +

{{block "log out?" .}}Log out?{{end}}

+
+ +

← {{block "go home" .}}Go home{{end}}

+
+ {{else}} +

{{block "cannot log out anon" .}}You cannot log out because you are not logged in.{{end}}

+

{{block "log in" .}}Log in{{end}}

+

← {{template "go home"}}

+ {{end}} +
+
+{{end}} \ No newline at end of file diff --git a/web/views/auth-register.html b/web/views/auth-register.html new file mode 100644 index 0000000..b2c44fa --- /dev/null +++ b/web/views/auth-register.html @@ -0,0 +1,35 @@ +{{define "register on x"}}Register on {{.}}{{end}} +{{define "title"}}{{template "register on x" .WikiName}}{{end}} +{{define "body"}} +
+
+ {{if .AllowRegistration}} + + {{template "telegram widget" .}} + {{else if .UseAuth}} +

{%s lc.Get("auth.noregister") %}

+

← {%s lc.Get("auth.go_back") %}

+ {{else}} +

{%s lc.Get("auth.noauth") %}

+

← {%s lc.Get("auth.go_back") %}

+ {{end}} +
+
+{{end}} \ No newline at end of file diff --git a/web/views/auth-telegram.html b/web/views/auth-telegram.html new file mode 100644 index 0000000..432f5f7 --- /dev/null +++ b/web/views/auth-telegram.html @@ -0,0 +1,11 @@ +{{define "telegram widget"}} + {{if .TelegramEnabled}} +

{{block "telegram tip" .}}You can log in using Telegram. It only works if you have set your @username in Telegram and this username is free on this wiki.{{end}}

+ + {{end}} +{{end}} diff --git a/web/web.go b/web/web.go index 44020ca..2887914 100644 --- a/web/web.go +++ b/web/web.go @@ -5,17 +5,17 @@ import ( "errors" "fmt" "github.com/bouncepaw/mycorrhiza/internal/cfg" - user2 "github.com/bouncepaw/mycorrhiza/internal/user" + "github.com/bouncepaw/mycorrhiza/internal/user" "github.com/bouncepaw/mycorrhiza/l18n" - viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil" + "github.com/bouncepaw/mycorrhiza/web/viewutil" "io" "log" + "log/slog" "mime" "net/http" "net/url" "strings" - "github.com/bouncepaw/mycorrhiza/auth" "github.com/bouncepaw/mycorrhiza/categories" "github.com/bouncepaw/mycorrhiza/help" "github.com/bouncepaw/mycorrhiza/history/histweb" @@ -63,7 +63,7 @@ func Handler() http.Handler { r := router.PathPrefix("").Subrouter() r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) { - user := user2.FromRequest(rq) + user := user.FromRequest(rq) if !user.ShowLockMaybe(w, rq) { next.ServeHTTP(w, rq) } @@ -117,7 +117,7 @@ func Handler() http.Handler { func groupMiddleware(group string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) { - if cfg.UseAuth && user2.CanProceed(rq, group) { + if cfg.UseAuth && user.CanProceed(rq, group) { next.ServeHTTP(w, rq) return } @@ -133,8 +133,8 @@ func groupMiddleware(group string) func(http.Handler) http.Handler { // Auth func handlerUserList(w http.ResponseWriter, rq *http.Request) { - admins, moderators, editors, readers := user2.UsersInGroups() - _ = pageUserList.RenderTo(viewutil2.MetaFrom(w, rq), + admins, moderators, editors, readers := user.UsersInGroups() + _ = pageUserList.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{ "Admins": admins, "Moderators": moderators, @@ -144,53 +144,47 @@ func handlerUserList(w http.ResponseWriter, rq *http.Request) { } func handlerLock(w http.ResponseWriter, rq *http.Request) { - _, _ = io.WriteString(w, auth.Lock(l18n.FromRequest(rq))) + _ = pageAuthLock.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{}) } // handlerRegister displays the register form (GET) or registers the user (POST). func handlerRegister(w http.ResponseWriter, rq *http.Request) { - lc := l18n.FromRequest(rq) util.PrepareRq(rq) if rq.Method == http.MethodGet { - _, _ = io.WriteString( - w, - viewutil2.Base( - viewutil2.MetaFrom(w, rq), - lc.Get("auth.register_title"), - auth.Register(rq), - map[string]string{}, - ), - ) + slog.Info("Showing registration form") + _ = pageAuthRegister.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{ + "UseAuth": cfg.UseAuth, + "AllowRegistration": cfg.AllowRegistration, + "RawQuery": rq.URL.RawQuery, + "WikiName": cfg.WikiName, + }) return } var ( username = rq.PostFormValue("username") password = rq.PostFormValue("password") - err = user2.Register(username, password, "editor", "local", false) + err = user.Register(username, password, "editor", "local", false) ) if err != nil { - log.Printf("Failed to register ‘%s’: %s", username, err.Error()) + slog.Info("Failed to register", "username", username, "err", err.Error()) w.Header().Set("Content-Type", mime.TypeByExtension(".html")) w.WriteHeader(http.StatusBadRequest) - _, _ = io.WriteString( - w, - viewutil2.Base( - viewutil2.MetaFrom(w, rq), - lc.Get("auth.register_title"), - fmt.Sprintf( - `

%s

%s

`, - err.Error(), - lc.Get("auth.try_again"), - ), - map[string]string{}, - ), - ) + _ = pageAuthRegister.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{ + "UseAuth": cfg.UseAuth, + "AllowRegistration": cfg.AllowRegistration, + "RawQuery": rq.URL.RawQuery, + "WikiName": cfg.WikiName, + + "Err": err, + "Username": username, + "Password": password, + }) return } - log.Printf("Successfully registered ‘%s’", username) - if err := user2.LoginDataHTTP(w, username, password); err != nil { + slog.Info("Registered user", "username", username) + if err := user.LoginDataHTTP(w, username, password); err != nil { return } http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther) @@ -198,71 +192,79 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) { // handlerLogout shows the logout form (GET) or logs the user out (POST). func handlerLogout(w http.ResponseWriter, rq *http.Request) { - if rq.Method == http.MethodGet { - var ( - u = user2.FromRequest(rq) - can = u != nil - lc = l18n.FromRequest(rq) - ) - w.Header().Set("Content-Type", "text/html;charset=utf-8") - if can { - log.Println("User", u.Name, "tries to log out") - w.WriteHeader(http.StatusOK) - } else { - log.Println("Unknown user tries to log out") - w.WriteHeader(http.StatusForbidden) - } - _, _ = io.WriteString( - w, - viewutil2.Base(viewutil2.MetaFrom(w, rq), lc.Get("auth.logout_title"), auth.Logout(can, lc), map[string]string{}), - ) - } else if rq.Method == http.MethodPost { - user2.LogoutFromRequest(w, rq) + if rq.Method == http.MethodPost { + slog.Info("Somebody logged out") + user.LogoutFromRequest(w, rq) http.Redirect(w, rq, "/", http.StatusSeeOther) + return } + + var ( + u = user.FromRequest(rq) + can = u != nil + ) + w.Header().Set("Content-Type", "text/html;charset=utf-8") + if can { + slog.Info("Logging out", "username", u.Name) + w.WriteHeader(http.StatusOK) + } else { + slog.Info("Unknown user logging out") + w.WriteHeader(http.StatusForbidden) + } + _ = pageAuthLogout.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{ + "CanLogout": can, + }) } // handlerLogin shows the login form (GET) or logs the user in (POST). func handlerLogin(w http.ResponseWriter, rq *http.Request) { - lc := l18n.FromRequest(rq) if rq.Method == http.MethodGet { - w.Header().Set("Content-Type", "text/html;charset=utf-8") w.WriteHeader(http.StatusOK) - _, _ = io.WriteString( - w, - viewutil2.Base( - viewutil2.MetaFrom(w, rq), - lc.Get("auth.login_title"), - auth.Login(lc), - map[string]string{}, - ), - ) - } else if rq.Method == http.MethodPost { - var ( - username = util.CanonicalName(rq.PostFormValue("username")) - password = rq.PostFormValue("password") - err = user2.LoginDataHTTP(w, username, password) - ) - if err != nil { - w.Header().Set("Content-Type", "text/html;charset=utf-8") - w.WriteHeader(http.StatusInternalServerError) - _, _ = io.WriteString(w, viewutil2.Base(viewutil2.MetaFrom(w, rq), err.Error(), auth.LoginError(err.Error(), lc), map[string]string{})) - return - } - http.Redirect(w, rq, "/", http.StatusSeeOther) + _ = pageAuthLogin.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{ + "UseAuth": cfg.UseAuth, + "ErrUnknownUsername": false, + "ErrWrongPassword": false, + "ErrTelegram": false, + "Err": nil, + "WikiName": cfg.WikiName, + }) + slog.Info("Somebody logging in") + return } + + var ( + username = util.CanonicalName(rq.PostFormValue("username")) + password = rq.PostFormValue("password") + err = user.LoginDataHTTP(w, username, password) + ) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _ = pageAuthLogin.RenderTo(viewutil.MetaFrom(w, rq), map[string]any{ + "UseAuth": cfg.UseAuth, + "ErrUnknownUsername": errors.Is(err, user.ErrUnknownUsername), + "ErrWrongPassword": errors.Is(err, user.ErrWrongPassword), + "ErrTelegram": false, // TODO: ? + "Err": err.Error(), + "WikiName": cfg.WikiName, + "Username": username, + }) + slog.Info("Failed to log in", "username", username, "err", err.Error()) + return + } + http.Redirect(w, rq, "/", http.StatusSeeOther) + slog.Info("Logged in", "username", username) } func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { // Note there is no lock here. lc := l18n.FromRequest(rq) w.Header().Set("Content-Type", "text/html;charset=utf-8") - rq.ParseForm() + _ = rq.ParseForm() var ( values = rq.URL.Query() username = strings.ToLower(values.Get("username")) - seemsValid = user2.TelegramAuthParamsAreValid(values) - err = user2.Register( + seemsValid = user.TelegramAuthParamsAreValid(values) + err = user.Register( username, "", // Password matters not "editor", @@ -272,7 +274,7 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { ) // If registering a user via Telegram failed, because a Telegram user with this name // has already registered, then everything is actually ok! - if user2.HasUsername(username) && user2.ByName(username).Source == "telegram" { + if user.HasUsername(username) && user.ByName(username).Source == "telegram" { err = nil } @@ -281,12 +283,12 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { } if err != nil { - log.Printf("Failed to register ‘%s’ using Telegram: %s", username, err.Error()) + slog.Info("Failed to register", "username", username, "err", err.Error(), "method", "telegram") w.WriteHeader(http.StatusBadRequest) _, _ = io.WriteString( w, - viewutil2.Base( - viewutil2.MetaFrom(w, rq), + viewutil.Base( + viewutil.MetaFrom(w, rq), lc.Get("ui.error"), fmt.Sprintf( `

%s

%s

%s

`, @@ -300,14 +302,14 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { return } - errmsg := user2.LoginDataHTTP(w, username, "") + errmsg := user.LoginDataHTTP(w, username, "") if errmsg != nil { log.Printf("Failed to login ‘%s’ using Telegram: %s", username, err.Error()) w.WriteHeader(http.StatusBadRequest) _, _ = io.WriteString( w, - viewutil2.Base( - viewutil2.MetaFrom(w, rq), + viewutil.Base( + viewutil.MetaFrom(w, rq), "Error", fmt.Sprintf( `

%s

%s

%s

`, @@ -320,6 +322,6 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) { ) return } - log.Printf("Authorize ‘%s’ from Telegram", username) http.Redirect(w, rq, "/", http.StatusSeeOther) + slog.Info("Logged in", "username", username, "method", "telegram") }