mirror of
https://github.com/jech/galene.git
synced 2024-11-10 02:35:58 +01:00
Move administrator password to data/config.json.
This commit is contained in:
parent
c64ec4ccd0
commit
c0b30c8557
5 changed files with 98 additions and 39 deletions
9
INSTALL
9
INSTALL
|
@ -12,15 +12,6 @@ On Windows, do
|
||||||
go build -ldflags="-s -w"
|
go build -ldflags="-s -w"
|
||||||
|
|
||||||
|
|
||||||
## Set the server administrator credentials
|
|
||||||
|
|
||||||
This step is optional, it is currently only relevant for access to the
|
|
||||||
`/stats.html` and `/stats.json` locations.
|
|
||||||
|
|
||||||
mkdir data
|
|
||||||
echo 'god:topsecret' > data/passwd
|
|
||||||
|
|
||||||
|
|
||||||
## Set up a group
|
## Set up a group
|
||||||
|
|
||||||
Set up a group called *test* by creating a file `groups/test.json`:
|
Set up a group called *test* by creating a file `groups/test.json`:
|
||||||
|
|
15
README
15
README
|
@ -37,6 +37,21 @@ available commands; the output depends on whether you are an operator or
|
||||||
not.
|
not.
|
||||||
|
|
||||||
|
|
||||||
|
# The global configuration file
|
||||||
|
|
||||||
|
The server may be configured in the JSON file `data/config.json`. This
|
||||||
|
file may look as follows:
|
||||||
|
|
||||||
|
{
|
||||||
|
"admin":[{"username":"root","password":"secret"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
The fields are as follows:
|
||||||
|
|
||||||
|
- `admin` defines the users allowed to look at the `/stats.html` file; it
|
||||||
|
has the same syntax as user definitions in groups (see below).
|
||||||
|
|
||||||
|
|
||||||
# Group definitions
|
# Group definitions
|
||||||
|
|
||||||
Groups are defined by files in the `./groups` directory (this may be
|
Groups are defined by files in the `./groups` directory (this may be
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var cpuprofile, memprofile, mutexprofile, httpAddr, dataDir string
|
var cpuprofile, memprofile, mutexprofile, httpAddr string
|
||||||
var udpRange string
|
var udpRange string
|
||||||
|
|
||||||
flag.StringVar(&httpAddr, "http", ":8443", "web server `address`")
|
flag.StringVar(&httpAddr, "http", ":8443", "web server `address`")
|
||||||
|
@ -31,7 +31,7 @@ func main() {
|
||||||
"redirect to canonical `host`")
|
"redirect to canonical `host`")
|
||||||
flag.BoolVar(&webserver.Insecure, "insecure", false,
|
flag.BoolVar(&webserver.Insecure, "insecure", false,
|
||||||
"act as an HTTP server rather than HTTPS")
|
"act as an HTTP server rather than HTTPS")
|
||||||
flag.StringVar(&dataDir, "data", "./data/",
|
flag.StringVar(&group.DataDirectory, "data", "./data/",
|
||||||
"data `directory`")
|
"data `directory`")
|
||||||
flag.StringVar(&group.Directory, "groups", "./groups/",
|
flag.StringVar(&group.Directory, "groups", "./groups/",
|
||||||
"group description `directory`")
|
"group description `directory`")
|
||||||
|
@ -112,7 +112,7 @@ func main() {
|
||||||
log.Printf("File descriptor limit is %v, please increase it!", n)
|
log.Printf("File descriptor limit is %v, please increase it!", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
ice.ICEFilename = filepath.Join(dataDir, "ice-servers.json")
|
ice.ICEFilename = filepath.Join(group.DataDirectory, "ice-servers.json")
|
||||||
|
|
||||||
// make sure the list of public groups is updated early
|
// make sure the list of public groups is updated early
|
||||||
go group.Update()
|
go group.Update()
|
||||||
|
@ -123,7 +123,7 @@ func main() {
|
||||||
|
|
||||||
serverDone := make(chan struct{})
|
serverDone := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
err := webserver.Serve(httpAddr, dataDir)
|
err := webserver.Serve(httpAddr, group.DataDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Server: %v", err)
|
log.Printf("Server: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/pion/webrtc/v3"
|
"github.com/pion/webrtc/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Directory string
|
var Directory, DataDirectory string
|
||||||
var UseMDNS bool
|
var UseMDNS bool
|
||||||
var UDPMin, UDPMax uint16
|
var UDPMin, UDPMax uint16
|
||||||
|
|
||||||
|
@ -802,8 +802,67 @@ func matchClient(group string, creds ClientCredentials, users []ClientPattern) (
|
||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type Description represents a group description together with some
|
// Configuration represents the contents of the data/config.json file.
|
||||||
// metadata about the JSON file it was deserialised from.
|
type Configuration struct {
|
||||||
|
// The modtime and size of the file. These are used to detect
|
||||||
|
// when a file has changed on disk.
|
||||||
|
modTime time.Time `json:"-"`
|
||||||
|
fileSize int64 `json:"-"`
|
||||||
|
|
||||||
|
Admin []ClientPattern `json:"admin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var configuration struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
configuration *Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfiguration() (*Configuration, error) {
|
||||||
|
configuration.mu.Lock()
|
||||||
|
defer configuration.mu.Unlock()
|
||||||
|
|
||||||
|
if configuration.configuration == nil {
|
||||||
|
configuration.configuration = &Configuration{}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := filepath.Join(DataDirectory, "config.json")
|
||||||
|
fi, err := os.Stat(filename)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if !configuration.configuration.modTime.Equal(
|
||||||
|
time.Time{},
|
||||||
|
) {
|
||||||
|
configuration.configuration = &Configuration{}
|
||||||
|
return configuration.configuration, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if configuration.configuration.modTime.Equal(fi.ModTime()) &&
|
||||||
|
configuration.configuration.fileSize == fi.Size() {
|
||||||
|
return configuration.configuration, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
d := json.NewDecoder(f)
|
||||||
|
d.DisallowUnknownFields()
|
||||||
|
var conf Configuration
|
||||||
|
err = d.Decode(&conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
configuration.configuration = &conf
|
||||||
|
return configuration.configuration, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description represents a group description together with some metadata
|
||||||
|
// about the JSON file it was deserialised from.
|
||||||
type Description struct {
|
type Description struct {
|
||||||
// The file this was deserialised from. This is not necessarily
|
// The file this was deserialised from. This is not necessarily
|
||||||
// the name of the group, for example in case of a subgroup.
|
// the name of the group, for example in case of a subgroup.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package webserver
|
package webserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -327,26 +326,20 @@ func publicHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPassword(dataDir string) (string, string, error) {
|
func adminMatch(username, password string) (bool, error) {
|
||||||
f, err := os.Open(filepath.Join(dataDir, "passwd"))
|
conf, err := group.GetConfiguration()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return false, err
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
r := bufio.NewReader(f)
|
|
||||||
|
|
||||||
s, err := r.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l := strings.SplitN(strings.TrimSpace(s), ":", 2)
|
for _, cred := range conf.Admin {
|
||||||
if len(l) != 2 {
|
if cred.Username == "" || cred.Username == username {
|
||||||
return "", "", errors.New("couldn't parse passwords")
|
if ok, _ := cred.Password.Match(password); ok {
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return l[0], l[1], nil
|
}
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func failAuthentication(w http.ResponseWriter, realm string) {
|
func failAuthentication(w http.ResponseWriter, realm string) {
|
||||||
|
@ -356,15 +349,16 @@ func failAuthentication(w http.ResponseWriter, realm string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) {
|
func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) {
|
||||||
u, p, err := getPassword(dataDir)
|
username, password, ok := r.BasicAuth()
|
||||||
if err != nil {
|
if !ok {
|
||||||
log.Printf("Passwd: %v", err)
|
|
||||||
failAuthentication(w, "stats")
|
failAuthentication(w, "stats")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
username, password, ok := r.BasicAuth()
|
if ok, err := adminMatch(username, password); !ok {
|
||||||
if !ok || username != u || password != p {
|
if err != nil {
|
||||||
|
log.Printf("Administrator password: %v", err)
|
||||||
|
}
|
||||||
failAuthentication(w, "stats")
|
failAuthentication(w, "stats")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -377,7 +371,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) {
|
||||||
|
|
||||||
ss := stats.GetGroups()
|
ss := stats.GetGroups()
|
||||||
e := json.NewEncoder(w)
|
e := json.NewEncoder(w)
|
||||||
err = e.Encode(ss)
|
err := e.Encode(ss)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("stats.json: %v", err)
|
log.Printf("stats.json: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue