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:
parent
55d8dbd7be
commit
1fbef60857
77
auth/web.go
77
auth/web.go
@ -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)
|
|
||||||
}
|
|
||||||
|
@ -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 == "" {
|
||||||
|
@ -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">
|
||||||
|
@ -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
|
|
@ -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 profile′s hypha'),
|
new Shortcut('g u', $('.auth-links__user-link'), 'Your profile′s 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 hypha′s 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 hypha′s 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'),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
37
user/net.go
37
user/net.go
@ -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")
|
|
||||||
}
|
|
||||||
|
10
util/util.go
10
util/util.go
@ -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))]
|
||||||
|
}
|
||||||
|
@ -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">
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user