Attempt to add HTTP authentication for #40

This commit is contained in:
Ian Kent 2015-05-27 18:08:44 +01:00
parent 9dc6ad8ed4
commit dcc60fb961
6 changed files with 100 additions and 2 deletions

View file

@ -1,4 +1,4 @@
VERSION=0.1.7 VERSION=0.2.0
all: deps fmt combined all: deps fmt combined
@ -25,7 +25,7 @@ deps:
go get labix.org/v2/mgo go get labix.org/v2/mgo
# added to fix travis issues # added to fix travis issues
go get code.google.com/p/go-uuid/uuid go get code.google.com/p/go-uuid/uuid
go get code.google.com/p/go.crypto/bcrypt go get golang.org/x/crypto/bcrypt
test-deps: test-deps:
go get github.com/smartystreets/goconvey go get github.com/smartystreets/goconvey

View file

@ -41,6 +41,7 @@ MailHog is an email testing tool for developers:
* See [Introduction to Jim](/docs/JIM.md) for more information * See [Introduction to Jim](/docs/JIM.md) for more information
* HTTP API to list, retrieve and delete messages * HTTP API to list, retrieve and delete messages
* See [APIv1](/docs/APIv1.md) and [APIv2](/docs/APIv2.md) documentation for more information * See [APIv1](/docs/APIv1.md) and [APIv2](/docs/APIv2.md) documentation for more information
* [HTTP basic authentication](docs/Auth.md) for MailHog UI and API
* Multipart MIME support * Multipart MIME support
* Download individual MIME parts * Download individual MIME parts
* In-memory message storage * In-memory message storage

27
config/config.go Normal file
View file

@ -0,0 +1,27 @@
package config
import (
"flag"
"github.com/ian-kent/envconf"
)
func DefaultConfig() *Config {
return &Config{
AuthFile: "",
}
}
type Config struct {
AuthFile string
}
var cfg = DefaultConfig()
func Configure() *Config {
return cfg
}
func RegisterFlags() {
flag.StringVar(&cfg.AuthFile, "auth-file", envconf.FromEnvP("MH_AUTH_FILE", "").(string), "A username:bcryptpw mapping file")
}

43
docs/Auth.md Normal file
View file

@ -0,0 +1,43 @@
Authentication
==============
HTTP basic authentication is supported using a password file.
See [example-auth](example-auth) for an example.
Authentication applies to all HTTP requests, including static content
and API endpoints.
### Password file format
The password file format is:
* One user per line
* `username:password`
* Password is bcrypted
By default, a bcrypt difficulty of 4 is used to reduce page load times.
### Generating a bcrypted password
You can use a MailHog shortcut to generate a bcrypted password:
MailHog bcrypt <password>
### Enabling HTTP authentication
To enable authentication, pass an `-auth-file` flag to MailHog:
MailHog -auth-file=docs/example-auth
This also works if you're running MailHog-UI and MailHog-Server separately:
MailHog-Server -auth-file=docs/example-auth
MailHog-UI -auth-file=docs/example-auth
## Future compatibility
Authentication has been a bit of an experiment.
The exact implementation may change over time, e.g. using sessions in the UI
and tokens for the API to avoid frequently bcrypting passwords.

1
docs/example-auth Normal file
View file

@ -0,0 +1 @@
test:$2a$04$qxRo.ftFoNep7ld/5jfKtuBTnGqff/fZVyj53mUC5sVf9dtDLAi/S

26
main.go
View file

@ -2,6 +2,7 @@ package main
import ( import (
"flag" "flag"
"fmt"
"os" "os"
gohttp "net/http" gohttp "net/http"
@ -14,20 +15,25 @@ import (
"github.com/mailhog/MailHog-UI/assets" "github.com/mailhog/MailHog-UI/assets"
cfgui "github.com/mailhog/MailHog-UI/config" cfgui "github.com/mailhog/MailHog-UI/config"
"github.com/mailhog/MailHog-UI/web" "github.com/mailhog/MailHog-UI/web"
cfgcom "github.com/mailhog/MailHog/config"
"github.com/mailhog/http" "github.com/mailhog/http"
"github.com/mailhog/mhsendmail/cmd" "github.com/mailhog/mhsendmail/cmd"
"golang.org/x/crypto/bcrypt"
) )
var apiconf *cfgapi.Config var apiconf *cfgapi.Config
var uiconf *cfgui.Config var uiconf *cfgui.Config
var comconf *cfgcom.Config
var exitCh chan int var exitCh chan int
func configure() { func configure() {
cfgcom.RegisterFlags()
cfgapi.RegisterFlags() cfgapi.RegisterFlags()
cfgui.RegisterFlags() cfgui.RegisterFlags()
flag.Parse() flag.Parse()
apiconf = cfgapi.Configure() apiconf = cfgapi.Configure()
uiconf = cfgui.Configure() uiconf = cfgui.Configure()
comconf = cfgcom.Configure()
} }
func main() { func main() {
@ -41,8 +47,28 @@ func main() {
return return
} }
if len(os.Args) > 1 && os.Args[1] == "bcrypt" {
var pw string
if len(os.Args) > 2 {
pw = os.Args[2]
} else {
// TODO: read from stdin
}
b, err := bcrypt.GenerateFromPassword([]byte(pw), 4)
if err != nil {
log.Fatalf("error bcrypting password: %s", err)
os.Exit(1)
}
fmt.Println(string(b))
os.Exit(0)
}
configure() configure()
if comconf.AuthFile != "" {
http.AuthFile(comconf.AuthFile)
}
exitCh = make(chan int) exitCh = make(chan int)
if uiconf.UIBindAddr == apiconf.APIBindAddr { if uiconf.UIBindAddr == apiconf.APIBindAddr {
cb := func(r gohttp.Handler) { cb := func(r gohttp.Handler) {