Skip to content

Commit

Permalink
Attempt to add HTTP authentication for #40
Browse files Browse the repository at this point in the history
  • Loading branch information
ian-kent committed May 27, 2015
1 parent 9dc6ad8 commit dcc60fb
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=0.1.7
VERSION=0.2.0

all: deps fmt combined

Expand All @@ -25,7 +25,7 @@ deps:
go get labix.org/v2/mgo
# added to fix travis issues
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:
go get github.com/smartystreets/goconvey
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ MailHog is an email testing tool for developers:
* See [Introduction to Jim](/docs/JIM.md) for more information
* HTTP API to list, retrieve and delete messages
* 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
* Download individual MIME parts
* In-memory message storage
Expand Down
27 changes: 27 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions docs/Auth.md
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions docs/example-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test:$2a$04$qxRo.ftFoNep7ld/5jfKtuBTnGqff/fZVyj53mUC5sVf9dtDLAi/S
26 changes: 26 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"flag"
"fmt"
"os"

gohttp "net/http"
Expand All @@ -14,20 +15,25 @@ import (
"github.com/mailhog/MailHog-UI/assets"
cfgui "github.com/mailhog/MailHog-UI/config"
"github.com/mailhog/MailHog-UI/web"
cfgcom "github.com/mailhog/MailHog/config"
"github.com/mailhog/http"
"github.com/mailhog/mhsendmail/cmd"
"golang.org/x/crypto/bcrypt"
)

var apiconf *cfgapi.Config
var uiconf *cfgui.Config
var comconf *cfgcom.Config
var exitCh chan int

func configure() {
cfgcom.RegisterFlags()
cfgapi.RegisterFlags()
cfgui.RegisterFlags()
flag.Parse()
apiconf = cfgapi.Configure()
uiconf = cfgui.Configure()
comconf = cfgcom.Configure()
}

func main() {
Expand All @@ -41,8 +47,28 @@ func main() {
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()

if comconf.AuthFile != "" {
http.AuthFile(comconf.AuthFile)
}

exitCh = make(chan int)
if uiconf.UIBindAddr == apiconf.APIBindAddr {
cb := func(r gohttp.Handler) {
Expand Down

0 comments on commit dcc60fb

Please sign in to comment.