Commit 314a041

bryfry <bryon.fryer@gmail.com>
2014-10-10 21:47:07
fully functional
1 parent 11d57fe
Changed files (2)
proxy/proxy.go
@@ -0,0 +1,154 @@
+package proxy
+
+import (
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"os"
+
+	"github.com/gorilla/mux"
+)
+
+type Proxy struct {
+	Domains []Domain
+}
+
+type Domain struct {
+	Address string `json:"domain"`
+	Users   []User `json:"users"`
+}
+
+type User struct {
+	Username string `json:"username"`
+	Password string `json:"password"`
+}
+
+type Response struct {
+	Success bool   `json:"access_granted"`
+	Reason  string `json:"reason,omitempty"`
+}
+
+// generate and return base64 encoded sha256 digest of provided password
+func b64sha256(password string) string {
+	s256 := sha256.New()
+	s256.Write([]byte(password))
+	return base64.StdEncoding.EncodeToString(s256.Sum(nil))
+}
+
+// parse the json users file and return the proxy data type
+func NewProxy(filePath string) (*Proxy, error) {
+	usersJson, err := os.Open(filePath)
+	if err != nil {
+		return nil, err
+	}
+
+	p := &Proxy{}
+	err = json.NewDecoder(usersJson).Decode(&p.Domains)
+	if err != nil {
+		return nil, err
+	}
+
+	for i, d := range p.Domains {
+		for j, u := range d.Users {
+			p.Domains[i].Users[j].Password = "{SHA256}" + b64sha256(u.Password)
+		}
+	}
+	return p, nil
+
+}
+
+// search within the proxy for a domain
+func (p *Proxy) get(reqDomain string) (*Domain, error) {
+	for i, d := range p.Domains {
+		if d.Address == reqDomain {
+			return &p.Domains[i], nil
+		}
+	}
+	return nil, fmt.Errorf("No such domain")
+}
+
+// search within a domain for a user
+func (d *Domain) get(reqUser string) (*User, error) {
+	for i, u := range d.Users {
+		if u.Username == reqUser {
+			return &d.Users[i], nil
+		}
+	}
+	return nil, fmt.Errorf("No such user")
+}
+
+// Per policy: in case of authentication failure or validation errors.
+// The 'reason' is always is always same: "denied by policy".
+// Similary success is simply access_granted: true.
+// Having this separated as a function will be useful if different
+// responses are needed in the future
+// Assuption: 200 OK is golang default
+func writeSuccess(w http.ResponseWriter, success bool) {
+	var r *Response
+	out := json.NewEncoder(w)
+	if success {
+		r = &Response{
+			Success: success,
+		}
+	} else {
+		r = &Response{
+			Success: success,
+			Reason:  "denied by policy",
+		}
+	}
+	out.Encode(r)
+}
+
+func (p *Proxy) Authenticate() http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		// domain lookup
+		vars := mux.Vars(r)
+		urlDomain, ok := vars["domain"]
+		if !ok {
+			w.WriteHeader(500) // Server error
+			return
+		}
+		d, err := p.get(urlDomain)
+		if err != nil {
+			w.WriteHeader(404) // No such domain
+			return
+		}
+
+		// parse parameters
+		err = r.ParseForm()
+		if err != nil {
+			w.WriteHeader(500) // Server error
+			return
+		}
+		username := r.Form.Get("username")
+		if username == "" {
+			writeSuccess(w, false) // no username provided
+			return
+		}
+		password := r.Form.Get("password")
+		if password == "" {
+			writeSuccess(w, false) // no password
+			return
+		}
+
+		// user lookup
+		u, err := d.get(username)
+		if err != nil {
+			writeSuccess(w, false) // no such user
+			return
+		}
+
+		// password validation
+		if u.Password != password {
+			writeSuccess(w, false) // password mismatch
+			return
+		} else {
+			writeSuccess(w, true) // successful authenticaton
+			return
+		}
+
+	})
+}
main.go
@@ -1,107 +1,28 @@
 package main
 
 import (
-	"crypto/sha256"
-	"encoding/base64"
-	"encoding/json"
-	"fmt"
 	"log"
 	"net/http"
-	"os"
 
+	"./proxy"
 	"github.com/gorilla/mux"
 )
 
-type proxy struct {
-	Domains []domain
-}
-
-type domain struct {
-	Address string `json:"domain"`
-	Users   []user `json:"users"`
-}
-
-type user struct {
-	Username string `json:"username"`
-	Password string `json:"password"`
-}
+func main() {
 
-func checkErr(err error, m string) {
+	proxy, err := proxy.NewProxy("./users.json")
 	if err != nil {
-		log.Fatalln(m, ": ", err)
+		log.Fatalln("Proxy init failed: %s", err)
 	}
-}
-
-func b64sha256(password string) string {
-	s256 := sha256.New()
-	s256.Write([]byte(password))
-	return base64.StdEncoding.EncodeToString(s256.Sum(nil))
-}
-
-func (p *proxy) get(domain, username string) (*user, error) {
-	for i, d := range p.Domains {
-		if d.Address == domain {
-			for j, u := range d.Users {
-				if u.Username == username {
-					return &p.Domains[i].Users[j], nil
-				}
-			}
-		}
-	}
-	return nil, fmt.Errorf("user not found")
-}
 
-func (p *proxy) authenticate() http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		var username, password, domain string
-		vars := mux.Vars(r)
-		if val, ok := vars["domain"]; ok {
-			domain = val
-		}
-		err := r.ParseForm()
-		checkErr(err, "ParseForm: ")
-		if val := r.Header.Get("Content-Type"); val == "" {
-			// no content-type
-		}
-
-		if val := r.Form.Get("username"); val != "" {
-			username = val
-		} else {
-			// no username
-
-		}
-
-		if val := r.Form.Get("password"); val != "" {
-			password = val
-		} else {
-			// no password
-
-		}
-		fmt.Println(r.Header.Get("Content-Type"), domain, username, password)
-		if _, err := p.get(domain, username); err != nil {
-			fmt.Fprintf(w, "{'access_granted':true}")
-		}
-
-	})
-}
-
-func main() {
-	filePath := "./users.json"
-	usersJson, err := os.Open(filePath)
-	checkErr(err, "Open "+filePath+": ")
-
-	proxy := &proxy{}
-	err = json.NewDecoder(usersJson).Decode(&proxy.Domains)
-	checkErr(err, "Decode: ")
+	r := mux.NewRouter()
 
-	for i, d := range proxy.Domains {
-		for j, u := range d.Users {
-			proxy.Domains[i].Users[j].Password = "{SHA256}" + b64sha256(u.Password)
-		}
-	}
+	// define the api method expectations, useful for future api handles
+	api := r.
+		Methods("POST").
+		Headers("Content-Type", "application/x-www-form-urlencoded").
+		Subrouter()
 
-	r := mux.NewRouter()
-	get := r.Methods("POST").Subrouter()
-	get.Handle("/api/2/domains/{domain}/proxyauth/", proxy.authenticate())
+	api.Handle("/api/2/domains/{domain}/proxyauth/", proxy.Authenticate())
 	http.ListenAndServe(":8080", r)
 }