1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-07 02:10:26 +00:00

Drop fixed authorization

Important changes:
- UseFixedAuth is now UseAuth and toggles all kinds of authorization and
  registration
- UseRegistration is now AllowRegistration to better reflect the meaning
- LimitRegistration is now RegistrationLimit because it's not a boolean,
  it's a value (not "limit registration?", but "registration limit is
  ...")
- registered-users.json is now users.json, because all users are stored
  there
- user.AuthUsed is dropped in favor of new cfg.UseAuth which has the
  same meaning

I hope I have not forgotten anything.
This commit is contained in:
handlerug 2021-07-02 15:20:03 +07:00
parent e6265c9ad9
commit b87583ef28
No known key found for this signature in database
GPG Key ID: 38009F0605051491
19 changed files with 211 additions and 335 deletions

View File

@ -11,7 +11,7 @@ HTTPPort = 8080
URL = https://wiki URL = https://wiki
[Authorization] [Authorization]
UseFixedAuth = true UseAuth = true
UseRegistration = true AllowRegistration = true
LimitRegistration = 10 RegistrationLimit = 10

View File

@ -11,7 +11,7 @@ HTTPPort = 1737
URL = http://localhost:1737 URL = http://localhost:1737
[Authorization] [Authorization]
UseFixedAuth = true UseAuth = true
UseRegistration = true AllowRegistration = true
LimitRegistration = 3 RegistrationLimit = 3

View File

@ -26,9 +26,9 @@ var (
URL string URL string
GeminiCertificatePath string GeminiCertificatePath string
UseFixedAuth bool UseAuth bool
UseRegistration bool AllowRegistration bool
LimitRegistration int RegistrationLimit uint64
CommonScripts []string CommonScripts []string
ViewScripts []string ViewScripts []string
@ -79,9 +79,9 @@ type CustomScripts struct {
// Authorization is a section of Config that has fields related to // Authorization is a section of Config that has fields related to
// authorization and authentication. // authorization and authentication.
type Authorization struct { type Authorization struct {
UseFixedAuth bool UseAuth bool
UseRegistration bool AllowRegistration bool
LimitRegistration uint64 `comment:"This field controls the maximum amount of allowed registrations."` RegistrationLimit uint64 `comment:"This field controls the maximum amount of allowed registrations."`
} }
// ReadConfigFile reads a config on the given path and stores the // ReadConfigFile reads a config on the given path and stores the
@ -101,9 +101,9 @@ func ReadConfigFile(path string) error {
GeminiCertificatePath: "", GeminiCertificatePath: "",
}, },
Authorization: Authorization{ Authorization: Authorization{
UseFixedAuth: false, UseAuth: false,
UseRegistration: false, AllowRegistration: false,
LimitRegistration: 0, RegistrationLimit: 0,
}, },
CustomScripts: CustomScripts{ CustomScripts: CustomScripts{
CommonScripts: []string{}, CommonScripts: []string{},
@ -164,9 +164,9 @@ func ReadConfigFile(path string) error {
HTTPPort = strconv.FormatUint(cfg.HTTPPort, 10) HTTPPort = strconv.FormatUint(cfg.HTTPPort, 10)
URL = cfg.URL URL = cfg.URL
GeminiCertificatePath = cfg.GeminiCertificatePath GeminiCertificatePath = cfg.GeminiCertificatePath
UseFixedAuth = cfg.UseFixedAuth UseAuth = cfg.UseAuth
UseRegistration = cfg.UseRegistration AllowRegistration = cfg.AllowRegistration
LimitRegistration = int(cfg.LimitRegistration) RegistrationLimit = cfg.RegistrationLimit
CommonScripts = cfg.CommonScripts CommonScripts = cfg.CommonScripts
ViewScripts = cfg.ViewScripts ViewScripts = cfg.ViewScripts
EditScripts = cfg.EditScripts EditScripts = cfg.EditScripts

View File

@ -9,13 +9,12 @@ import (
) )
var paths struct { var paths struct {
gitRepo string gitRepo string
cacheDir string cacheDir string
staticFiles string staticFiles string
configPath string configPath string
tokensJSON string tokensJSON string
registrationCredentialsJSON string userCredentialsJSON string
fixedCredentialsJSON string
} }
// HyphaeDir returns the path to hyphae storage. // HyphaeDir returns the path to hyphae storage.
@ -36,12 +35,8 @@ func ConfigPath() string { return paths.configPath }
// TokensJSON returns the path to the JSON user tokens storage. // TokensJSON returns the path to the JSON user tokens storage.
func TokensJSON() string { return paths.tokensJSON } func TokensJSON() string { return paths.tokensJSON }
// RegistrationCredentialsJSON returns the path to the JSON registration // UserCredentialsJSON returns the path to the JSON user credentials storage.
// credentials storage. func UserCredentialsJSON() string { return paths.userCredentialsJSON }
func RegistrationCredentialsJSON() string { return paths.registrationCredentialsJSON }
// FixedCredentialsJSON returns the path to the JSON fixed credentials storage.
func FixedCredentialsJSON() string { return paths.fixedCredentialsJSON }
// PrepareWikiRoot ensures all needed directories and files exist and have // PrepareWikiRoot ensures all needed directories and files exist and have
// correct permissions. // correct permissions.
@ -66,10 +61,9 @@ func PrepareWikiRoot() error {
} }
paths.configPath = filepath.Join(cfg.WikiDir, "config.ini") paths.configPath = filepath.Join(cfg.WikiDir, "config.ini")
paths.userCredentialsJSON = filepath.Join(cfg.WikiDir, "users.json")
paths.tokensJSON = filepath.Join(paths.cacheDir, "tokens.json") paths.tokensJSON = filepath.Join(paths.cacheDir, "tokens.json")
paths.fixedCredentialsJSON = filepath.Join(cfg.WikiDir, "fixed-users.json")
paths.registrationCredentialsJSON = filepath.Join(cfg.WikiDir, "registered-users.json")
return nil return nil
} }

View File

@ -1,24 +0,0 @@
[
{
"name": "admin",
"password": "mycorrhiza",
"group": "admin"
},
{
"name": "weird_fish",
"password": "DeepestOcean",
"group": "moderator"
},
{
"name": "king_of_limbs",
"password": "ambush",
"group": "trusted"
},
{
"name": "paranoid_android",
"password": "ok computer",
"group": "editor"
}
]

View File

@ -2,8 +2,7 @@ package user
import ( import (
"encoding/json" "encoding/json"
"golang.org/x/crypto/bcrypt" "errors"
"io/ioutil"
"log" "log"
"os" "os"
@ -12,64 +11,25 @@ import (
"github.com/bouncepaw/mycorrhiza/util" "github.com/bouncepaw/mycorrhiza/util"
) )
// InitUserDatabase checks the configuration for auth methods and loads users // InitUserDatabase loads users, if necessary. Call it during initialization.
// if necessary. Call it during initialization.
func InitUserDatabase() { func InitUserDatabase() {
AuthUsed = cfg.UseFixedAuth || cfg.UseRegistration ReadUsersFromFilesystem()
if AuthUsed {
ReadUsersFromFilesystem()
}
} }
// ReadUsersFromFilesystem reads all user information from filesystem and stores it internally. // ReadUsersFromFilesystem reads all user information from filesystem and
// stores it internally.
func ReadUsersFromFilesystem() { func ReadUsersFromFilesystem() {
if cfg.UseFixedAuth { if cfg.UseAuth {
// This one will be removed. rememberUsers(usersFromFile())
rememberUsers(usersFromFixedCredentials()) readTokensToUsers()
}
// And this one will be renamed to just "users" in the future.
rememberUsers(usersFromRegistrationCredentials())
// Migrate fixed users to registered
tryToMigrate()
readTokensToUsers()
}
func tryToMigrate() {
// Fixed authorization should be removed by the next release (1.13).
// So let's try to help fixed users and migrate them over!
migrated := 0
for user := range YieldUsers() {
if user.Source == SourceFixed {
hashedPasswd, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
if err != nil {
log.Fatal("Failed to migrate fixed users:", err)
}
user.Password = ""
user.HashedPassword = string(hashedPasswd)
user.Source = SourceRegistration
migrated++
}
}
if migrated > 0 {
if err := dumpRegistrationCredentials(); err != nil {
log.Fatal("Failed to migrate fixed users:", err)
}
log.Printf("Migrated %d users", migrated)
} }
} }
func usersFromFile(path string, source UserSource) (users []*User) { func usersFromFile() []*User {
contents, err := ioutil.ReadFile(path) var users []*User
if os.IsNotExist(err) { contents, err := os.ReadFile(files.UserCredentialsJSON())
return if errors.Is(err, os.ErrNotExist) {
return users
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -80,20 +40,8 @@ func usersFromFile(path string, source UserSource) (users []*User) {
} }
for _, u := range users { for _, u := range users {
u.Name = util.CanonicalName(u.Name) u.Name = util.CanonicalName(u.Name)
u.Source = source
} }
return users log.Println("Found", len(users), "users")
}
func usersFromFixedCredentials() []*User {
users := usersFromFile(files.FixedCredentialsJSON(), SourceFixed)
log.Println("Found", len(users), "fixed users")
return users
}
func usersFromRegistrationCredentials() []*User {
users := usersFromFile(files.RegistrationCredentialsJSON(), SourceRegistration)
log.Println("Found", len(users), "registered users")
return users return users
} }
@ -104,7 +52,7 @@ func rememberUsers(userList []*User) {
} }
func readTokensToUsers() { func readTokensToUsers() {
contents, err := ioutil.ReadFile(files.TokensJSON()) contents, err := os.ReadFile(files.TokensJSON())
if os.IsNotExist(err) { if os.IsNotExist(err) {
return return
} }
@ -119,37 +67,36 @@ func readTokensToUsers() {
} }
for token, username := range tmp { for token, username := range tmp {
commenceSession(username, token) tokens.Store(token, username)
// commenceSession(username, token)
} }
log.Println("Found", len(tmp), "active sessions") log.Println("Found", len(tmp), "active sessions")
} }
func SaveUserDatabase() error { func SaveUserDatabase() error {
return dumpRegistrationCredentials() return dumpUserCredentials()
} }
func dumpRegistrationCredentials() error { func dumpUserCredentials() error {
tmp := []*User{} userList := []*User{}
// TODO: lock the map during saving to prevent corruption
for u := range YieldUsers() { for u := range YieldUsers() {
if u.Source != SourceRegistration { userList = append(userList, u)
continue
}
copiedUser := u
copiedUser.Password = ""
tmp = append(tmp, copiedUser)
} }
blob, err := json.MarshalIndent(tmp, "", "\t") blob, err := json.MarshalIndent(userList, "", "\t")
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return err return err
} }
err = ioutil.WriteFile(files.RegistrationCredentialsJSON(), blob, 0644)
err = os.WriteFile(files.UserCredentialsJSON(), blob, 0666)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return err return err
} }
return nil return nil
} }
@ -166,7 +113,7 @@ func dumpTokens() {
blob, err := json.MarshalIndent(tmp, "", "\t") blob, err := json.MarshalIndent(tmp, "", "\t")
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} else { return
ioutil.WriteFile(files.TokensJSON(), blob, 0644)
} }
os.WriteFile(files.TokensJSON(), blob, 0666)
} }

View File

@ -1,11 +1,9 @@
package user package user
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/bouncepaw/mycorrhiza/cfg" "github.com/bouncepaw/mycorrhiza/cfg"
@ -41,13 +39,12 @@ func Register(username, password string) error {
username = util.CanonicalName(username) username = util.CanonicalName(username)
log.Println("Attempt to register user", username) log.Println("Attempt to register user", username)
switch { switch {
case CountRegistered() >= cfg.LimitRegistration && cfg.LimitRegistration > 0: case cfg.RegistrationLimit > 0 && Count() >= cfg.RegistrationLimit:
i := strconv.Itoa(cfg.LimitRegistration) log.Printf("Limit reached: %d", cfg.RegistrationLimit)
log.Println("Limit reached: " + i) return fmt.Errorf("Reached the limit of registered users: %d", cfg.RegistrationLimit)
return errors.New("Reached the limit of registered users: " + i)
case HasUsername(username): case HasUsername(username):
log.Println("Username taken") log.Println("Username taken")
return errors.New("Username " + username + " is taken already.") return fmt.Errorf("Username \"%s\" is taken already.", username)
case !util.IsPossibleUsername(username): case !util.IsPossibleUsername(username):
log.Println("Illegal username:", username) log.Println("Illegal username:", username)
return fmt.Errorf("Illegal username \"%s\".", username) return fmt.Errorf("Illegal username \"%s\".", username)
@ -57,14 +54,13 @@ func Register(username, password string) error {
return err return err
} }
u := User{ u := User{
Name: username, Name: username,
Group: "editor", Group: "editor",
HashedPassword: string(hash), Password: string(hash),
RegisteredAt: time.Now(), RegisteredAt: time.Now(),
Source: SourceRegistration,
} }
users.Store(username, &u) users.Store(username, &u)
err = dumpRegistrationCredentials() err = SaveUserDatabase()
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,29 +4,17 @@ import (
"sync" "sync"
"time" "time"
"github.com/bouncepaw/mycorrhiza/cfg"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
// UserSource shows where is the user data gotten from.
type UserSource int
const (
SourceUnknown UserSource = iota
// SourceFixed is used with users that are predefined using fixed auth
SourceFixed
// SourceRegistration is used with users that are registered through the register form
SourceRegistration
)
// User is a user. // User is a user.
type User struct { type User struct {
// Name is a username. It must follow hypha naming rules. // Name is a username. It must follow hypha naming rules.
Name string `json:"name"` Name string `json:"name"`
Group string `json:"group"` Group string `json:"group"`
Password string `json:"password"` // for fixed Password string `json:"hashed_password"`
HashedPassword string `json:"hashed_password"` // for registered RegisteredAt time.Time `json:"registered_on"`
RegisteredAt time.Time `json:"registered_on"`
Source UserSource `json:"-"`
sync.RWMutex sync.RWMutex
// A note about why HashedPassword is string and not []byte. The reason is // A note about why HashedPassword is string and not []byte. The reason is
@ -86,7 +74,7 @@ func EmptyUser() *User {
} }
func (user *User) CanProceed(route string) bool { func (user *User) CanProceed(route string) bool {
if !AuthUsed { if !cfg.UseAuth {
return true return true
} }
@ -105,12 +93,6 @@ func (user *User) isCorrectPassword(password string) bool {
user.RLock() user.RLock()
defer user.RUnlock() defer user.RUnlock()
switch user.Source { err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
case SourceFixed: return err == nil
return password == user.Password
case SourceRegistration:
err := bcrypt.CompareHashAndPassword([]byte(user.HashedPassword), []byte(password))
return err == nil
}
return false
} }

View File

@ -1,10 +1,7 @@
package user package user
import ( import "sync"
"sync"
)
var AuthUsed bool
var users sync.Map var users sync.Map
var tokens sync.Map var tokens sync.Map
@ -21,28 +18,16 @@ func YieldUsers() chan *User {
} }
func ListUsersWithGroup(group string) []string { func ListUsersWithGroup(group string) []string {
usersWithTheGroup := []string{} filtered := []string{}
for u := range YieldUsers() { for u := range YieldUsers() {
if u.Group == group { if u.Group == group {
usersWithTheGroup = append(usersWithTheGroup, u.Name) filtered = append(filtered, u.Name)
} }
} }
return usersWithTheGroup return filtered
} }
func CountRegistered() int { func Count() (i uint64) {
i := 0
users.Range(func(k, v interface{}) bool {
if v.(*User).Source == SourceRegistration {
i++
}
return true
})
return i
}
func Count() int {
i := 0
users.Range(func(k, v interface{}) bool { users.Range(func(k, v interface{}) bool {
i++ i++
return true return true

View File

@ -1,12 +1,11 @@
{% import "net/http" %} {% import "net/http" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %} {% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% func RegisterHTML(rq *http.Request) %} {% func RegisterHTML(rq *http.Request) %}
<div class="layout"> <div class="layout">
<main class="main-width"> <main class="main-width">
<section> <section>
{% if cfg.UseRegistration %} {% if cfg.AllowRegistration %}
<form class="modal" method="post" action="/register?{%s rq.URL.RawQuery %}" id="register-form" enctype="multipart/form-data" autocomplete="off"> <form class="modal" method="post" action="/register?{%s rq.URL.RawQuery %}" id="register-form" enctype="multipart/form-data" autocomplete="off">
<fieldset class="modal__fieldset"> <fieldset class="modal__fieldset">
<legend class="modal__title">Register on {%s cfg.WikiName %}</legend> <legend class="modal__title">Register on {%s cfg.WikiName %}</legend>
@ -24,11 +23,11 @@
<a class="btn btn_weak" href="/{%s rq.URL.RawQuery %}">Cancel</a> <a class="btn btn_weak" href="/{%s rq.URL.RawQuery %}">Cancel</a>
</fieldset> </fieldset>
</form> </form>
{% elseif cfg.UseFixedAuth %} {% elseif cfg.UseAuth %}
<p>Administrators have forbidden registration for this wiki. Administrators can make an account for you by hand; contact them.</p> <p>Registrations are currently closed. Administrators can make an account for you by hand; contact them.</p>
<p><a href="/{%s rq.URL.RawQuery %}">← Go back</a></p> <p><a href="/{%s rq.URL.RawQuery %}">← Go back</a></p>
{% else %} {% else %}
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p> <p>Authentication is disabled. You can make edits anonymously.</p>
<p><a href="/{%s rq.URL.RawQuery %}">← Go back</a></p> <p><a href="/{%s rq.URL.RawQuery %}">← Go back</a></p>
{% endif %} {% endif %}
</section> </section>
@ -40,7 +39,7 @@
<div class="layout"> <div class="layout">
<main class="main-width"> <main class="main-width">
<section> <section>
{% if user.AuthUsed %} {% if cfg.UseAuth %}
<form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on"> <form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on">
<fieldset class="modal__fieldset"> <fieldset class="modal__fieldset">
<legend class="modal__title">Log in to {%s cfg.WikiName %}</legend> <legend class="modal__title">Log in to {%s cfg.WikiName %}</legend>
@ -57,7 +56,7 @@
</fieldset> </fieldset>
</form> </form>
{% else %} {% else %}
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p> <p>Authentication is disabled. You can make edits anonymously.</p>
<p><a class="modal__cancel" href="/">← Go home</a></p> <p><a class="modal__cancel" href="/">← Go home</a></p>
{% endif %} {% endif %}
</section> </section>

View File

@ -8,46 +8,43 @@ package views
import "net/http" import "net/http"
//line views/auth.qtpl:2 //line views/auth.qtpl:2
import "github.com/bouncepaw/mycorrhiza/user"
//line views/auth.qtpl:3
import "github.com/bouncepaw/mycorrhiza/cfg" import "github.com/bouncepaw/mycorrhiza/cfg"
//line views/auth.qtpl:5 //line views/auth.qtpl:4
import ( import (
qtio422016 "io" qtio422016 "io"
qt422016 "github.com/valyala/quicktemplate" qt422016 "github.com/valyala/quicktemplate"
) )
//line views/auth.qtpl:5 //line views/auth.qtpl:4
var ( var (
_ = qtio422016.Copy _ = qtio422016.Copy
_ = qt422016.AcquireByteBuffer _ = qt422016.AcquireByteBuffer
) )
//line views/auth.qtpl:5 //line views/auth.qtpl:4
func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) { func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) {
//line views/auth.qtpl:5 //line views/auth.qtpl:4
qw422016.N().S(` qw422016.N().S(`
<div class="layout"> <div class="layout">
<main class="main-width"> <main class="main-width">
<section> <section>
`) `)
//line views/auth.qtpl:9 //line views/auth.qtpl:8
if cfg.UseRegistration { if cfg.AllowRegistration {
//line views/auth.qtpl:9 //line views/auth.qtpl:8
qw422016.N().S(` qw422016.N().S(`
<form class="modal" method="post" action="/register?`) <form class="modal" method="post" action="/register?`)
//line views/auth.qtpl:10 //line views/auth.qtpl:9
qw422016.E().S(rq.URL.RawQuery) qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:10 //line views/auth.qtpl:9
qw422016.N().S(`" id="register-form" enctype="multipart/form-data" autocomplete="off"> qw422016.N().S(`" id="register-form" enctype="multipart/form-data" autocomplete="off">
<fieldset class="modal__fieldset"> <fieldset class="modal__fieldset">
<legend class="modal__title">Register on `) <legend class="modal__title">Register on `)
//line views/auth.qtpl:12 //line views/auth.qtpl:11
qw422016.E().S(cfg.WikiName) qw422016.E().S(cfg.WikiName)
//line views/auth.qtpl:12 //line views/auth.qtpl:11
qw422016.N().S(`</legend> qw422016.N().S(`</legend>
<label for="register-form__username">Username</label> <label for="register-form__username">Username</label>
@ -61,90 +58,90 @@ func StreamRegisterHTML(qw422016 *qt422016.Writer, rq *http.Request) {
<p>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.</p> <p>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.</p>
<input class="btn" type="submit" value="Register"> <input class="btn" type="submit" value="Register">
<a class="btn btn_weak" href="/`) <a class="btn btn_weak" href="/`)
//line views/auth.qtpl:24 //line views/auth.qtpl:23
qw422016.E().S(rq.URL.RawQuery) qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:24 //line views/auth.qtpl:23
qw422016.N().S(`">Cancel</a> qw422016.N().S(`">Cancel</a>
</fieldset> </fieldset>
</form> </form>
`) `)
//line views/auth.qtpl:27 //line views/auth.qtpl:26
} else if cfg.UseFixedAuth { } else if cfg.UseAuth {
//line views/auth.qtpl:27 //line views/auth.qtpl:26
qw422016.N().S(` qw422016.N().S(`
<p>Administrators have forbidden registration for this wiki. Administrators can make an account for you by hand; contact them.</p> <p>Registrations are currently closed. Administrators can make an account for you by hand; contact them.</p>
<p><a href="/`) <p><a href="/`)
//line views/auth.qtpl:29 //line views/auth.qtpl:28
qw422016.E().S(rq.URL.RawQuery) qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:29 //line views/auth.qtpl:28
qw422016.N().S(`"> Go back</a></p> qw422016.N().S(`"> Go back</a></p>
`) `)
//line views/auth.qtpl:30 //line views/auth.qtpl:29
} else { } else {
//line views/auth.qtpl:30 //line views/auth.qtpl:29
qw422016.N().S(` qw422016.N().S(`
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p> <p>Authentication is disabled. You can make edits anonymously.</p>
<p><a href="/`) <p><a href="/`)
//line views/auth.qtpl:32 //line views/auth.qtpl:31
qw422016.E().S(rq.URL.RawQuery) qw422016.E().S(rq.URL.RawQuery)
//line views/auth.qtpl:32 //line views/auth.qtpl:31
qw422016.N().S(`"> Go back</a></p> qw422016.N().S(`"> Go back</a></p>
`) `)
//line views/auth.qtpl:33 //line views/auth.qtpl:32
} }
//line views/auth.qtpl:33 //line views/auth.qtpl:32
qw422016.N().S(` qw422016.N().S(`
</section> </section>
</main> </main>
</div> </div>
`) `)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
} }
//line views/auth.qtpl:37 //line views/auth.qtpl:36
func WriteRegisterHTML(qq422016 qtio422016.Writer, rq *http.Request) { func WriteRegisterHTML(qq422016 qtio422016.Writer, rq *http.Request) {
//line views/auth.qtpl:37 //line views/auth.qtpl:36
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
StreamRegisterHTML(qw422016, rq) StreamRegisterHTML(qw422016, rq)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
} }
//line views/auth.qtpl:37 //line views/auth.qtpl:36
func RegisterHTML(rq *http.Request) string { func RegisterHTML(rq *http.Request) string {
//line views/auth.qtpl:37 //line views/auth.qtpl:36
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:37 //line views/auth.qtpl:36
WriteRegisterHTML(qb422016, rq) WriteRegisterHTML(qb422016, rq)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:37 //line views/auth.qtpl:36
return qs422016 return qs422016
//line views/auth.qtpl:37 //line views/auth.qtpl:36
} }
//line views/auth.qtpl:39 //line views/auth.qtpl:38
func StreamLoginHTML(qw422016 *qt422016.Writer) { func StreamLoginHTML(qw422016 *qt422016.Writer) {
//line views/auth.qtpl:39 //line views/auth.qtpl:38
qw422016.N().S(` qw422016.N().S(`
<div class="layout"> <div class="layout">
<main class="main-width"> <main class="main-width">
<section> <section>
`) `)
//line views/auth.qtpl:43 //line views/auth.qtpl:42
if user.AuthUsed { if cfg.UseAuth {
//line views/auth.qtpl:43 //line views/auth.qtpl:42
qw422016.N().S(` qw422016.N().S(`
<form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on"> <form class="modal" method="post" action="/login-data" id="login-form" enctype="multipart/form-data" autocomplete="on">
<fieldset class="modal__fieldset"> <fieldset class="modal__fieldset">
<legend class="modal__title">Log in to `) <legend class="modal__title">Log in to `)
//line views/auth.qtpl:46 //line views/auth.qtpl:45
qw422016.E().S(cfg.WikiName) qw422016.E().S(cfg.WikiName)
//line views/auth.qtpl:46 //line views/auth.qtpl:45
qw422016.N().S(`</legend> qw422016.N().S(`</legend>
<label for="login-form__username">Username</label> <label for="login-form__username">Username</label>
<br> <br>
@ -159,177 +156,177 @@ func StreamLoginHTML(qw422016 *qt422016.Writer) {
</fieldset> </fieldset>
</form> </form>
`) `)
//line views/auth.qtpl:59 //line views/auth.qtpl:58
} else { } else {
//line views/auth.qtpl:59 //line views/auth.qtpl:58
qw422016.N().S(` qw422016.N().S(`
<p>Administrators of this wiki have not configured any authorization method. You can make edits anonymously.</p> <p>Authentication is disabled. You can make edits anonymously.</p>
<p><a class="modal__cancel" href="/"> Go home</a></p> <p><a class="modal__cancel" href="/"> Go home</a></p>
`) `)
//line views/auth.qtpl:62 //line views/auth.qtpl:61
} }
//line views/auth.qtpl:62 //line views/auth.qtpl:61
qw422016.N().S(` qw422016.N().S(`
</section> </section>
</main> </main>
</div> </div>
`) `)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
} }
//line views/auth.qtpl:66 //line views/auth.qtpl:65
func WriteLoginHTML(qq422016 qtio422016.Writer) { func WriteLoginHTML(qq422016 qtio422016.Writer) {
//line views/auth.qtpl:66 //line views/auth.qtpl:65
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
StreamLoginHTML(qw422016) StreamLoginHTML(qw422016)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
} }
//line views/auth.qtpl:66 //line views/auth.qtpl:65
func LoginHTML() string { func LoginHTML() string {
//line views/auth.qtpl:66 //line views/auth.qtpl:65
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:66 //line views/auth.qtpl:65
WriteLoginHTML(qb422016) WriteLoginHTML(qb422016)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:66 //line views/auth.qtpl:65
return qs422016 return qs422016
//line views/auth.qtpl:66 //line views/auth.qtpl:65
} }
//line views/auth.qtpl:68 //line views/auth.qtpl:67
func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) { func StreamLoginErrorHTML(qw422016 *qt422016.Writer, err string) {
//line views/auth.qtpl:68 //line views/auth.qtpl:67
qw422016.N().S(` qw422016.N().S(`
<div class="layout"> <div class="layout">
<main class="main-width"> <main class="main-width">
<section> <section>
`) `)
//line views/auth.qtpl:72 //line views/auth.qtpl:71
switch err { switch err {
//line views/auth.qtpl:73 //line views/auth.qtpl:72
case "unknown username": case "unknown username":
//line views/auth.qtpl:73 //line views/auth.qtpl:72
qw422016.N().S(` qw422016.N().S(`
<p class="error">Unknown username.</p> <p class="error">Unknown username.</p>
`) `)
//line views/auth.qtpl:75 //line views/auth.qtpl:74
case "wrong password": case "wrong password":
//line views/auth.qtpl:75 //line views/auth.qtpl:74
qw422016.N().S(` qw422016.N().S(`
<p class="error">Wrong password.</p> <p class="error">Wrong password.</p>
`) `)
//line views/auth.qtpl:77 //line views/auth.qtpl:76
default: default:
//line views/auth.qtpl:77 //line views/auth.qtpl:76
qw422016.N().S(` qw422016.N().S(`
<p class="error">`) <p class="error">`)
//line views/auth.qtpl:78 //line views/auth.qtpl:77
qw422016.E().S(err) qw422016.E().S(err)
//line views/auth.qtpl:78 //line views/auth.qtpl:77
qw422016.N().S(`</p> qw422016.N().S(`</p>
`) `)
//line views/auth.qtpl:79 //line views/auth.qtpl:78
} }
//line views/auth.qtpl:79 //line views/auth.qtpl:78
qw422016.N().S(` qw422016.N().S(`
<p><a href="/login"> Try again</a></p> <p><a href="/login"> Try again</a></p>
</section> </section>
</main> </main>
</div> </div>
`) `)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
} }
//line views/auth.qtpl:84 //line views/auth.qtpl:83
func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) { func WriteLoginErrorHTML(qq422016 qtio422016.Writer, err string) {
//line views/auth.qtpl:84 //line views/auth.qtpl:83
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
StreamLoginErrorHTML(qw422016, err) StreamLoginErrorHTML(qw422016, err)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
} }
//line views/auth.qtpl:84 //line views/auth.qtpl:83
func LoginErrorHTML(err string) string { func LoginErrorHTML(err string) string {
//line views/auth.qtpl:84 //line views/auth.qtpl:83
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:84 //line views/auth.qtpl:83
WriteLoginErrorHTML(qb422016, err) WriteLoginErrorHTML(qb422016, err)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:84 //line views/auth.qtpl:83
return qs422016 return qs422016
//line views/auth.qtpl:84 //line views/auth.qtpl:83
} }
//line views/auth.qtpl:86 //line views/auth.qtpl:85
func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) { func StreamLogoutHTML(qw422016 *qt422016.Writer, can bool) {
//line views/auth.qtpl:86 //line views/auth.qtpl:85
qw422016.N().S(` qw422016.N().S(`
<div class="layout"> <div class="layout">
<main class="main-width"> <main class="main-width">
<section> <section>
`) `)
//line views/auth.qtpl:90 //line views/auth.qtpl:89
if can { if can {
//line views/auth.qtpl:90 //line views/auth.qtpl:89
qw422016.N().S(` qw422016.N().S(`
<h1>Log out?</h1> <h1>Log out?</h1>
<p><a href="/logout-confirm"><strong>Confirm</strong></a></p> <p><a href="/logout-confirm"><strong>Confirm</strong></a></p>
<p><a href="/">Cancel</a></p> <p><a href="/">Cancel</a></p>
`) `)
//line views/auth.qtpl:94 //line views/auth.qtpl:93
} else { } else {
//line views/auth.qtpl:94 //line views/auth.qtpl:93
qw422016.N().S(` qw422016.N().S(`
<p>You cannot log out because you are not logged in.</p> <p>You cannot log out because you are not logged in.</p>
<p><a href="/login">Login</a></p> <p><a href="/login">Login</a></p>
<p><a href="/login"> Home</a></p> <p><a href="/login"> Home</a></p>
`) `)
//line views/auth.qtpl:98 //line views/auth.qtpl:97
} }
//line views/auth.qtpl:98 //line views/auth.qtpl:97
qw422016.N().S(` qw422016.N().S(`
</section> </section>
</main> </main>
</div> </div>
`) `)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
} }
//line views/auth.qtpl:102 //line views/auth.qtpl:101
func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) { func WriteLogoutHTML(qq422016 qtio422016.Writer, can bool) {
//line views/auth.qtpl:102 //line views/auth.qtpl:101
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
StreamLogoutHTML(qw422016, can) StreamLogoutHTML(qw422016, can)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
} }
//line views/auth.qtpl:102 //line views/auth.qtpl:101
func LogoutHTML(can bool) string { func LogoutHTML(can bool) string {
//line views/auth.qtpl:102 //line views/auth.qtpl:101
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line views/auth.qtpl:102 //line views/auth.qtpl:101
WriteLogoutHTML(qb422016, can) WriteLogoutHTML(qb422016, can)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line views/auth.qtpl:102 //line views/auth.qtpl:101
return qs422016 return qs422016
//line views/auth.qtpl:102 //line views/auth.qtpl:101
} }

View File

@ -9,11 +9,11 @@
{% func nonExistentHyphaNotice(h *hyphae.Hypha, u *user.User) %} {% func nonExistentHyphaNotice(h *hyphae.Hypha, u *user.User) %}
<section class="non-existent-hypha"> <section class="non-existent-hypha">
<h2 class="non-existent-hypha__title">This hypha does not exist</h2> <h2 class="non-existent-hypha__title">This hypha does not exist</h2>
{% if user.AuthUsed && u.Group == "anon" %} {% if cfg.UseAuth && u.Group == "anon" %}
<p>You are not authorized to create new hyphae. Here is what you can do:</p> <p>You are not authorized to create new hyphae. Here is what you can do:</p>
<ul> <ul>
<li><a href="/login">Log in to your account, if you have one</a></li> <li><a href="/login">Log in to your account, if you have one</a></li>
{% if cfg.UseRegistration %}<li><a href="/register">Register a new account</a></li>{% endif %} {% if cfg.AllowRegistration %}<li><a href="/register">Register a new account</a></li>{% endif %}
</ul> </ul>
{% else %} {% else %}

View File

@ -43,7 +43,7 @@ func streamnonExistentHyphaNotice(qw422016 *qt422016.Writer, h *hyphae.Hypha, u
<h2 class="non-existent-hypha__title">This hypha does not exist</h2> <h2 class="non-existent-hypha__title">This hypha does not exist</h2>
`) `)
//line views/hypha.qtpl:12 //line views/hypha.qtpl:12
if user.AuthUsed && u.Group == "anon" { if cfg.UseAuth && u.Group == "anon" {
//line views/hypha.qtpl:12 //line views/hypha.qtpl:12
qw422016.N().S(` qw422016.N().S(`
<p>You are not authorized to create new hyphae. Here is what you can do:</p> <p>You are not authorized to create new hyphae. Here is what you can do:</p>
@ -51,7 +51,7 @@ func streamnonExistentHyphaNotice(qw422016 *qt422016.Writer, h *hyphae.Hypha, u
<li><a href="/login">Log in to your account, if you have one</a></li> <li><a href="/login">Log in to your account, if you have one</a></li>
`) `)
//line views/hypha.qtpl:16 //line views/hypha.qtpl:16
if cfg.UseRegistration { if cfg.AllowRegistration {
//line views/hypha.qtpl:16 //line views/hypha.qtpl:16
qw422016.N().S(`<li><a href="/register">Register a new account</a></li>`) qw422016.N().S(`<li><a href="/register">Register a new account</a></li>`)
//line views/hypha.qtpl:16 //line views/hypha.qtpl:16

View File

@ -48,7 +48,7 @@ var navEntries = []navEntry{
{% endfunc %} {% endfunc %}
{% func UserMenuHTML(u *user.User) %} {% func UserMenuHTML(u *user.User) %}
{% if user.AuthUsed %} {% if cfg.UseAuth %}
<li class="header-links__entry header-links__entry_user"> <li class="header-links__entry header-links__entry_user">
{% if u.Group == "anon" %} {% if u.Group == "anon" %}
<a href="/login" class="header-links__link">Login</a> <a href="/login" class="header-links__link">Login</a>
@ -57,7 +57,7 @@ var navEntries = []navEntry{
{% endif %} {% endif %}
</li> </li>
{% endif %} {% endif %}
{% if user.AuthUsed && cfg.UseRegistration && u.Group == "anon" %} {% if cfg.UseAuth && cfg.AllowRegistration && u.Group == "anon" %}
<li class="header-links__entry header-links__entry_register"> <li class="header-links__entry header-links__entry_register">
<a href="/register" class="header-links__link">Register</a> <a href="/register" class="header-links__link">Register</a>
</li> </li>

View File

@ -150,7 +150,7 @@ func StreamUserMenuHTML(qw422016 *qt422016.Writer, u *user.User) {
qw422016.N().S(` qw422016.N().S(`
`) `)
//line views/nav.qtpl:51 //line views/nav.qtpl:51
if user.AuthUsed { if cfg.UseAuth {
//line views/nav.qtpl:51 //line views/nav.qtpl:51
qw422016.N().S(` qw422016.N().S(`
<li class="header-links__entry header-links__entry_user"> <li class="header-links__entry header-links__entry_user">
@ -191,7 +191,7 @@ func StreamUserMenuHTML(qw422016 *qt422016.Writer, u *user.User) {
qw422016.N().S(` qw422016.N().S(`
`) `)
//line views/nav.qtpl:60 //line views/nav.qtpl:60
if user.AuthUsed && cfg.UseRegistration && u.Group == "anon" { if cfg.UseAuth && cfg.AllowRegistration && u.Group == "anon" {
//line views/nav.qtpl:60 //line views/nav.qtpl:60
qw422016.N().S(` qw422016.N().S(`
<li class="header-links__entry header-links__entry_register"> <li class="header-links__entry header-links__entry_register">

View File

@ -113,8 +113,8 @@ for u := range user.YieldUsers() {
<h1>About {%s cfg.WikiName %}</h1> <h1>About {%s cfg.WikiName %}</h1>
<ul> <ul>
<li><b><a href="https://mycorrhiza.lesarbr.es">Mycorrhiza Wiki</a> version:</b> 1.3.0</li> <li><b><a href="https://mycorrhiza.lesarbr.es">Mycorrhiza Wiki</a> version:</b> 1.3.0</li>
{%- if user.AuthUsed -%} {%- if cfg.UseAuth -%}
<li><b>User count:</b> {%d user.Count() %}</li> <li><b>User count:</b> {%dul user.Count() %}</li>
<li><b>Home page:</b> <a href="/">{%s cfg.HomeHypha %}</a></li> <li><b>Home page:</b> <a href="/">{%s cfg.HomeHypha %}</a></li>
<li><b>Administrators:</b> {%- for i, username := range user.ListUsersWithGroup("admin") -%} <li><b>Administrators:</b> {%- for i, username := range user.ListUsersWithGroup("admin") -%}
{%- if i > 0 -%}<span aria-hidden="true">, </span> {%- if i > 0 -%}<span aria-hidden="true">, </span>

View File

@ -376,11 +376,11 @@ func StreamAboutHTML(qw422016 *qt422016.Writer) {
<li><b><a href="https://mycorrhiza.lesarbr.es">Mycorrhiza Wiki</a> version:</b> 1.3.0</li> <li><b><a href="https://mycorrhiza.lesarbr.es">Mycorrhiza Wiki</a> version:</b> 1.3.0</li>
`) `)
//line views/stuff.qtpl:116 //line views/stuff.qtpl:116
if user.AuthUsed { if cfg.UseAuth {
//line views/stuff.qtpl:116 //line views/stuff.qtpl:116
qw422016.N().S(` <li><b>User count:</b> `) qw422016.N().S(` <li><b>User count:</b> `)
//line views/stuff.qtpl:117 //line views/stuff.qtpl:117
qw422016.N().D(user.Count()) qw422016.N().DUL(user.Count())
//line views/stuff.qtpl:117 //line views/stuff.qtpl:117
qw422016.N().S(`</li> qw422016.N().S(`</li>
<li><b>Home page:</b> <a href="/">`) <li><b>Home page:</b> <a href="/">`)

View File

@ -17,7 +17,7 @@ import (
// initAdmin sets up /admin routes if auth is used. Call it after you have decided if you want to use auth. // initAdmin sets up /admin routes if auth is used. Call it after you have decided if you want to use auth.
func initAdmin() { func initAdmin() {
if user.AuthUsed { if cfg.UseAuth {
http.HandleFunc("/admin/", handlerAdmin) http.HandleFunc("/admin/", handlerAdmin)
http.HandleFunc("/admin/shutdown/", handlerAdminShutdown) http.HandleFunc("/admin/shutdown/", handlerAdminShutdown)
http.HandleFunc("/admin/reindex-users/", handlerAdminReindexUsers) http.HandleFunc("/admin/reindex-users/", handlerAdminReindexUsers)

View File

@ -14,10 +14,10 @@ import (
) )
func initAuth() { func initAuth() {
if !user.AuthUsed { if !cfg.UseAuth {
return return
} }
if cfg.UseRegistration { if cfg.AllowRegistration {
http.HandleFunc("/register", handlerRegister) http.HandleFunc("/register", handlerRegister)
} }
http.HandleFunc("/login", handlerLogin) http.HandleFunc("/login", handlerLogin)
@ -29,7 +29,7 @@ func initAuth() {
// handlerRegister both displays the register form (GET) and registers users (POST). // handlerRegister both displays the register form (GET) and registers users (POST).
func handlerRegister(w http.ResponseWriter, rq *http.Request) { func handlerRegister(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
if !cfg.UseRegistration { if !cfg.AllowRegistration {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
} }
if rq.Method == http.MethodGet { if rq.Method == http.MethodGet {
@ -97,7 +97,7 @@ func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
func handlerLogin(w http.ResponseWriter, rq *http.Request) { func handlerLogin(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq) util.PrepareRq(rq)
w.Header().Set("Content-Type", "text/html;charset=utf-8") w.Header().Set("Content-Type", "text/html;charset=utf-8")
if user.AuthUsed { if cfg.UseAuth {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} else { } else {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)