1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 21:40:26 +00:00

Adjsut terminology and add MOTDs

This commit is contained in:
osmarks 2024-10-24 10:06:44 +01:00
parent 55d8dbd7be
commit 1fbef60857
10 changed files with 36 additions and 157 deletions

View File

@ -1,13 +1,11 @@
package auth package auth
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
"mime" "mime"
"net/http" "net/http"
"strings"
"github.com/bouncepaw/mycorrhiza/viewutil" "github.com/bouncepaw/mycorrhiza/viewutil"
@ -29,9 +27,7 @@ func InitAuth(r *mux.Router) {
if cfg.AllowRegistration { if cfg.AllowRegistration {
r.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet) r.HandleFunc("/register", handlerRegister).Methods(http.MethodPost, http.MethodGet)
} }
if cfg.TelegramEnabled {
r.HandleFunc("/telegram-login", handlerTelegramLogin)
}
r.HandleFunc("/login", handlerLogin) r.HandleFunc("/login", handlerLogin)
r.HandleFunc("/logout", handlerLogout) r.HandleFunc("/logout", handlerLogout)
} }
@ -152,74 +148,3 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
http.Redirect(w, rq, "/", http.StatusSeeOther) http.Redirect(w, rq, "/", http.StatusSeeOther)
} }
} }
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()
var (
values = rq.URL.Query()
username = strings.ToLower(values.Get("username"))
seemsValid = user.TelegramAuthParamsAreValid(values)
err = user.Register(
username,
"", // Password matters not
"editor",
"telegram",
false,
)
)
// If registering a user via Telegram failed, because a Telegram user with this name
// has already registered, then everything is actually ok!
if user.HasUsername(username) && user.ByName(username).Source == "telegram" {
err = nil
}
if !seemsValid {
err = errors.New("Wrong parameters")
}
if err != nil {
log.Printf("Failed to register %s using Telegram: %s", username, err.Error())
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
lc.Get("ui.error"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
lc.Get("auth.error_telegram"),
err.Error(),
lc.Get("auth.go_login"),
),
map[string]string{},
),
)
return
}
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,
viewutil.Base(
viewutil.MetaFrom(w, rq),
"Error",
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
lc.Get("auth.error_telegram"),
err.Error(),
lc.Get("auth.go_login"),
),
map[string]string{},
),
)
return
}
log.Printf("Authorize %s from Telegram", username)
http.Redirect(w, rq, "/", http.StatusSeeOther)
}

View File

@ -46,6 +46,8 @@ var (
ReplaceFrom []string ReplaceFrom []string
ReplaceTo []string ReplaceTo []string
Motds []string
) )
// WikiDir is a full path to the wiki storage directory, which also must be a // WikiDir is a full path to the wiki storage directory, which also must be a
@ -64,6 +66,7 @@ type Config struct {
Telegram `comment:"You can enable Telegram authorization. Follow these instructions: https://core.telegram.org/widgets/login#setting-up-a-bot"` Telegram `comment:"You can enable Telegram authorization. Follow these instructions: https://core.telegram.org/widgets/login#setting-up-a-bot"`
ReplaceFrom []string ReplaceFrom []string
ReplaceTo []string ReplaceTo []string
Motds []string
} }
// Hyphae is a section of Config which has fields related to special hyphae. // Hyphae is a section of Config which has fields related to special hyphae.
@ -153,17 +156,17 @@ func ReadConfigFile(path string) error {
// Save the default configuration // Save the default configuration
err = f.ReflectFrom(cfg) err = f.ReflectFrom(cfg)
if err != nil { if err != nil {
return fmt.Errorf("Failed to serialize the config: %w", err) return fmt.Errorf("failed to serialize the config: %w", err)
} }
// Disable key-value auto-aligning, but retain spaces around '=' sign // Disable key-value auto-aligning, but retain spaces around '=' sign
ini.PrettyFormat = false ini.PrettyFormat = false
ini.PrettyEqual = true ini.PrettyEqual = true
if err = f.SaveTo(path); err != nil { if err = f.SaveTo(path); err != nil {
return fmt.Errorf("Failed to save the config file: %w", err) return fmt.Errorf("failed to save the config file: %w", err)
} }
} else { } else {
return fmt.Errorf("Failed to open the config file: %w", err) return fmt.Errorf("failed to open the config file: %w", err)
} }
} }
@ -198,6 +201,7 @@ func ReadConfigFile(path string) error {
TelegramEnabled = (TelegramBotToken != "") && (TelegramBotName != "") TelegramEnabled = (TelegramBotToken != "") && (TelegramBotName != "")
ReplaceFrom = cfg.ReplaceFrom ReplaceFrom = cfg.ReplaceFrom
ReplaceTo = cfg.ReplaceTo ReplaceTo = cfg.ReplaceTo
Motds = cfg.Motds
// This URL makes much more sense. If no URL is set or the protocol is forgotten, assume HTTP. // This URL makes much more sense. If no URL is set or the protocol is forgotten, assume HTTP.
if URL == "" { if URL == "" {

View File

@ -1,4 +1,4 @@
{{define "list of hyphae"}}List of hyphae{{end}} {{define "list of hyphae"}}List of pages{{end}}
{{define "title"}}{{template "list of hyphae"}}{{end}} {{define "title"}}{{template "list of hyphae"}}{{end}}
{{define "body"}} {{define "body"}}
<main class="main-width"> <main class="main-width">

View File

@ -1,25 +1,2 @@
User-agent: * User-agent: *
Allow: /help/ Allow: /
Allow: /hypha/
Allow: /recent-changes
Allow: /list
Disallow: /page/
Disallow: /admin/
Disallow: /lock
Disallow: /text/
Disallow: /binary/
Disallow: /rev/
Disallow: /rev-text/
Disallow: /primitive-diff/
Disallow: /media/
Disallow: /edit/
Disallow: /delete/
Disallow: /rename/
Disallow: /remove-media/
Disallow: /history/
Disallow: /orphans
Disallow: /random
Disallow: /title-search/
Disallow: /backlinks/
Disallow: /user-list
Crawl-delay: 5

View File

@ -290,24 +290,24 @@ function openHelp() {
rrh.shortcuts.addGroup(new ShortcutGroup('Common', null, [ rrh.shortcuts.addGroup(new ShortcutGroup('Common', null, [
new Shortcut('g', $$('.top-bar__highlight-link'), 'First 9 header links'), new Shortcut('g', $$('.top-bar__highlight-link'), 'First 9 header links'),
new Shortcut('g h', '/', 'Home'), new Shortcut('g h', '/', 'Home'),
new Shortcut('g l', '/list/', 'List of hyphae'), new Shortcut('g l', '/list/', 'List of page'),
new Shortcut('g r', '/recent-changes/', 'Recent changes'), new Shortcut('g r', '/recent-changes/', 'Recent changes'),
new Shortcut('g u', $('.auth-links__user-link'), 'Your profiles hypha'), new Shortcut('g u', $('.auth-links__user-link'), 'Your profiles page'),
new Shortcut(['?', isMac ? 'Meta+/' : 'Ctrl+/'], openHelp, 'Shortcut help', { force: true }), new Shortcut(['?', isMac ? 'Meta+/' : 'Ctrl+/'], openHelp, 'Shortcut help', { force: true }),
])) ]))
if (document.body.dataset.rrhAddr.startsWith('/hypha')) { if (document.body.dataset.rrhAddr.startsWith('/hypha')) {
rrh.shortcuts.addGroup(new ShortcutGroup('Hypha', null, [ rrh.shortcuts.addGroup(new ShortcutGroup('Page', null, [
new Shortcut('', $$('article .wikilink'), 'First 9 hyphas links'), new Shortcut('', $$('article .wikilink'), 'First 9 pages links'),
new Shortcut(['p', 'Alt+ArrowLeft', 'Ctrl+Alt+ArrowLeft'], $('.prevnext__prev'), 'Previous hypha'), new Shortcut(['p', 'Alt+ArrowLeft', 'Ctrl+Alt+ArrowLeft'], $('.prevnext__prev'), 'Previous page'),
new Shortcut(['n', 'Alt+ArrowRight', 'Ctrl+Alt+ArrowRight'], $('.prevnext__next'), 'Next hypha'), new Shortcut(['n', 'Alt+ArrowRight', 'Ctrl+Alt+ArrowRight'], $('.prevnext__next'), 'Next page'),
new Shortcut(['s', 'Alt+ArrowUp', 'Ctrl+Alt+ArrowUp'], $$('.navi-title a').slice(1, -1).slice(-1)[0], 'Parent hypha'), new Shortcut(['s', 'Alt+ArrowUp', 'Ctrl+Alt+ArrowUp'], $$('.navi-title a').slice(1, -1).slice(-1)[0], 'Parent page'),
new Shortcut(['c', 'Alt+ArrowDown', 'Ctrl+Alt+ArrowDown'], $('.subhyphae__link'), 'First child hypha'), new Shortcut(['c', 'Alt+ArrowDown', 'Ctrl+Alt+ArrowDown'], $('.subhyphae__link'), 'First child page'),
new Shortcut(['e', isMac ? 'Meta+Enter' : 'Ctrl+Enter'], $('.btn__link_navititle[href^="/edit/"]'), 'Edit this hypha'), new Shortcut(['e', isMac ? 'Meta+Enter' : 'Ctrl+Enter'], $('.btn__link_navititle[href^="/edit/"]'), 'Edit this page'),
new Shortcut('v', $('.hypha-info__link[href^="/hypha/"]'), 'Go to hyphas page'), new Shortcut('v', $('.hypha-info__link[href^="/hypha/"]'), 'Go to page overview'),
new Shortcut('a', $('.hypha-info__link[href^="/media/"]'), 'Go to media management'), new Shortcut('a', $('.hypha-info__link[href^="/media/"]'), 'Go to media management'),
new Shortcut('h', $('.hypha-info__link[href^="/history/"]'), 'Go to history'), new Shortcut('h', $('.hypha-info__link[href^="/history/"]'), 'Go to history'),
new Shortcut('r', $('.hypha-info__link[href^="/rename/"]'), 'Rename this hypha'), new Shortcut('r', $('.hypha-info__link[href^="/rename/"]'), 'Rename this page'),
new Shortcut('b', $('.hypha-info__link[href^="/backlinks/"]'), 'Backlinks'), new Shortcut('b', $('.hypha-info__link[href^="/backlinks/"]'), 'Backlinks'),
])) ]))
} }

View File

@ -1,15 +1,10 @@
package user package user
import ( import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"sort"
"strings"
"time" "time"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
@ -123,35 +118,3 @@ func cookie(nameSuffix, val string, t time.Time) *http.Cookie {
Path: "/", Path: "/",
} }
} }
// TelegramAuthParamsAreValid is true if the given params are ok.
func TelegramAuthParamsAreValid(params map[string][]string) bool {
// According to the Telegram documentation,
// > You can verify the authentication and the integrity of the data received by comparing the received hash parameter with the hexadecimal representation of the HMAC-SHA-256 signature of the data-check-string with the SHA256 hash of the bot's token used as a secret key.
tokenHash := sha256.New()
tokenHash.Write([]byte(cfg.TelegramBotToken))
secretKey := tokenHash.Sum(nil)
hash := hmac.New(sha256.New, secretKey)
hash.Write([]byte(telegramDataCheckString(params)))
hexHash := hex.EncodeToString(hash.Sum(nil))
passedHash := params["hash"][0]
return passedHash == hexHash
}
// According to the Telegram documentation,
// > Data-check-string is a concatenation of all received fields, sorted in alphabetical order, in the format key=<value> with a line feed character ('\n', 0x0A) used as separator e.g., 'auth_date=<auth_date>\nfirst_name=<first_name>\nid=<id>\nusername=<username>'.
//
// Note that hash is not used here.
func telegramDataCheckString(params map[string][]string) string {
var lines []string
for key, value := range params {
if key == "hash" {
continue
}
lines = append(lines, fmt.Sprintf("%s=%s", key, value[0]))
}
sort.Strings(lines)
return strings.Join(lines, "\n")
}

View File

@ -3,10 +3,12 @@ package util
import ( import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"github.com/bouncepaw/mycorrhiza/files"
"log" "log"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/bouncepaw/mycorrhiza/files"
"git.sr.ht/~bouncepaw/mycomarkup/v5/util" "git.sr.ht/~bouncepaw/mycomarkup/v5/util"
"github.com/bouncepaw/mycorrhiza/cfg" "github.com/bouncepaw/mycorrhiza/cfg"
@ -154,3 +156,9 @@ func IsRevHash(revHash string) bool {
} }
return true return true
} }
func GetMotd() string {
now := time.Now().UTC().Unix()
dayIndex := now / 86400
return cfg.Motds[dayIndex%int64(len(cfg.Motds))]
}

View File

@ -17,8 +17,8 @@
<body data-rrh-addr="{{if .Addr}}{{.Addr}}{{else}}{{.Meta.Addr}}{{end}}"{{range $key, $value := .BodyAttributes}} data-rrh-{{$key}}="{{$value}}"{{end}}> <body data-rrh-addr="{{if .Addr}}{{.Addr}}{{else}}{{.Meta.Addr}}{{end}}"{{range $key, $value := .BodyAttributes}} data-rrh-{{$key}}="{{$value}}"{{end}}>
<header> <header>
<div class="logo"> <div class="logo">
<img src="/static/favicon.ico" alt="A picture of Euler, stretched into a square" /> <img src="/static/favicon.ico" alt="A picture of mathematician Leonhard Euler, stretched into a square" />
<div class="ominous"><span>It's already far too late.</span></div> <div class="ominous"><span>{{.Motd}}</span></div>
</div> </div>
<nav class="main-width top-bar"> <nav class="main-width top-bar">
<ul class="top-bar__wrapper"> <ul class="top-bar__wrapper">

View File

@ -100,6 +100,7 @@ type BaseData struct {
Title string // TODO: remove Title string // TODO: remove
Body string // TODO: remove Body string // TODO: remove
BodyAttributes map[string]string BodyAttributes map[string]string
Motd string
} }
func (bd *BaseData) withBaseValues(meta Meta, headerLinks []HeaderLink, commonScripts []string) { func (bd *BaseData) withBaseValues(meta Meta, headerLinks []HeaderLink, commonScripts []string) {
@ -123,6 +124,7 @@ func Base(meta Meta, title, body string, bodyAttributes map[string]string, headE
EditScripts: cfg.EditScripts, EditScripts: cfg.EditScripts,
Body: body, Body: body,
BodyAttributes: bodyAttributes, BodyAttributes: bodyAttributes,
Motd: util.GetMotd(),
}) })
if err != nil { if err != nil {
log.Println(err) log.Println(err)

View File

@ -7,7 +7,6 @@ import (
"net/url" "net/url"
"github.com/bouncepaw/mycorrhiza/admin" "github.com/bouncepaw/mycorrhiza/admin"
"github.com/bouncepaw/mycorrhiza/settings"
"github.com/bouncepaw/mycorrhiza/auth" "github.com/bouncepaw/mycorrhiza/auth"
"github.com/bouncepaw/mycorrhiza/backlinks" "github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories" "github.com/bouncepaw/mycorrhiza/categories"
@ -16,6 +15,7 @@ import (
"github.com/bouncepaw/mycorrhiza/hypview" "github.com/bouncepaw/mycorrhiza/hypview"
"github.com/bouncepaw/mycorrhiza/interwiki" "github.com/bouncepaw/mycorrhiza/interwiki"
"github.com/bouncepaw/mycorrhiza/misc" "github.com/bouncepaw/mycorrhiza/misc"
"github.com/bouncepaw/mycorrhiza/settings"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -31,7 +31,7 @@ func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
w.Header().Add("Content-Security-Policy", w.Header().Add("Content-Security-Policy",
"default-src 'self' telegram.org *.telegram.org; "+ "default-src 'self'; "+
"img-src * data:; media-src *; style-src *; font-src * data:") "img-src * data:; media-src *; style-src *; font-src * data:")
next.ServeHTTP(w, rq) next.ServeHTTP(w, rq)
}) })