Inital commit
This commit is contained in:
59
misc/auth/auth.go
Normal file
59
misc/auth/auth.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"git.1248.nz/1248/Otfe/models"
|
||||
)
|
||||
|
||||
type auth func(http.ResponseWriter, *http.Request, models.User)
|
||||
|
||||
func User(h auth) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, _ := getUserSession(r)
|
||||
h(w, r, user)
|
||||
}
|
||||
}
|
||||
|
||||
func Perm(handler auth, fallback auth, perm string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := getUserSession(r)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, "/login", http.StatusFound)
|
||||
return
|
||||
}
|
||||
if user.HasPermission(perm) {
|
||||
handler(w, r, user)
|
||||
} else {
|
||||
if fallback == nil {
|
||||
UnAuth(w)
|
||||
} else {
|
||||
fallback(w, r, user)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getUserSession(r *http.Request) (models.User, error) {
|
||||
var session models.Session
|
||||
var user models.User
|
||||
//Check for session in db
|
||||
err := session.Get(r)
|
||||
if err == nil {
|
||||
//Get user associated with the session
|
||||
err = user.Read("_id", session.UserID)
|
||||
if err == nil {
|
||||
return user, nil
|
||||
|
||||
}
|
||||
}
|
||||
return user, errors.New("User not logged in")
|
||||
}
|
||||
|
||||
func UnAuth(w http.ResponseWriter) {
|
||||
http.Error(w, "You are not authorized to view this page",
|
||||
http.StatusForbidden)
|
||||
}
|
113
misc/auth/auth_test.go
Normal file
113
misc/auth/auth_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.1248.nz/1248/Otfe/misc/helpers"
|
||||
"git.1248.nz/1248/Otfe/misc/helpers/cookie"
|
||||
"git.1248.nz/1248/Otfe/models"
|
||||
"github.com/globalsign/mgo/bson"
|
||||
)
|
||||
|
||||
func TestUser(t *testing.T) {
|
||||
//Setup user with session
|
||||
recorder := httptest.NewRecorder()
|
||||
user, session := userSession(t)
|
||||
request := request(t, session)
|
||||
u := User(handler)
|
||||
//Run
|
||||
u(recorder, request)
|
||||
//Check
|
||||
body := recorder.Body.String()
|
||||
if !strings.Contains(body, user.ID.Hex()) {
|
||||
t.Fail()
|
||||
}
|
||||
//Setup without session
|
||||
recorder = httptest.NewRecorder()
|
||||
request, _ = http.NewRequest("GET", "/", nil)
|
||||
//Run
|
||||
u(recorder, request)
|
||||
//Check
|
||||
helpers.Equals(t, recorder.Body.String(),
|
||||
"{ObjectIdHex(\"\") ObjectIdHex(\"\") []}")
|
||||
|
||||
}
|
||||
|
||||
func TestPerm(t *testing.T) {
|
||||
p := Perm(handler, UnAuth, "perm")
|
||||
recorder := httptest.NewRecorder()
|
||||
user, session := userSession(t)
|
||||
request := request(t, session)
|
||||
p(recorder, request)
|
||||
if !strings.Contains(recorder.Body.String(),
|
||||
"You are not authorized to view this page") {
|
||||
t.Log("Authorization fail")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
p = Perm(handler, UnAuth, "test")
|
||||
recorder = httptest.NewRecorder()
|
||||
p(recorder, request)
|
||||
if !strings.Contains(recorder.Body.String(), user.ID.Hex()) {
|
||||
t.Log("Has permission fail")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
recorder = httptest.NewRecorder()
|
||||
request, err := http.NewRequest("GET", "/", nil)
|
||||
helpers.Ok(t, err)
|
||||
p(recorder, request)
|
||||
if !strings.Contains(recorder.Body.String(), "login") {
|
||||
t.Log("Login fail")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetUserSession(t *testing.T) {
|
||||
user, session := userSession(t)
|
||||
request := request(t, session)
|
||||
//Test
|
||||
user2, err := getUserSession(request)
|
||||
helpers.Ok(t, err)
|
||||
helpers.Equals(t, user, user2)
|
||||
|
||||
}
|
||||
|
||||
func userSession(t *testing.T) (models.User, models.Session) {
|
||||
models.DBWipeCollection("user", "session", "group")
|
||||
|
||||
group := models.NewGroup("test")
|
||||
group.ID = bson.NewObjectId()
|
||||
group.Permissions["test"] = true
|
||||
//group.Admin = true
|
||||
helpers.Ok(t, group.Create())
|
||||
|
||||
user := models.User{Name: "test",
|
||||
Email: "test"}
|
||||
user.ID = bson.NewObjectId()
|
||||
user.PrimaryGroup = group.ID
|
||||
helpers.Ok(t, user.Create())
|
||||
|
||||
session := models.Session{UserID: user.ID}
|
||||
session.ID = bson.NewObjectId()
|
||||
helpers.Ok(t, session.Create())
|
||||
return user, session
|
||||
}
|
||||
|
||||
func request(t *testing.T, s models.Session) *http.Request {
|
||||
cookie := &http.Cookie{Name: "session",
|
||||
Value: cookie.Encode(s.ID.Hex())}
|
||||
request, err := http.NewRequest("GET", "/", nil)
|
||||
helpers.Ok(t, err)
|
||||
request.AddCookie(cookie)
|
||||
return request
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request, u models.User) {
|
||||
fmt.Fprint(w, u)
|
||||
}
|
12
misc/b64/b64.go
Normal file
12
misc/b64/b64.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package b64
|
||||
|
||||
import "encoding/base64"
|
||||
|
||||
func Encode(src string) string {
|
||||
return base64.URLEncoding.EncodeToString([]byte(src))
|
||||
}
|
||||
|
||||
func Decode(src string) (string, error) {
|
||||
value, err := base64.URLEncoding.DecodeString(src)
|
||||
return string(value), err
|
||||
}
|
60
misc/config/config.go
Normal file
60
misc/config/config.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"path/filepath"
|
||||
|
||||
"git.1248.nz/1248/Otfe/misc/helpers"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
//Configuration struct
|
||||
type Configuration struct {
|
||||
DB database `toml:"database"`
|
||||
Session session
|
||||
}
|
||||
|
||||
// Database stuct
|
||||
type database struct {
|
||||
Host string
|
||||
Name string
|
||||
User string
|
||||
Password string
|
||||
}
|
||||
|
||||
type session struct {
|
||||
SecretKey string
|
||||
Sessionkey string
|
||||
Timeout int
|
||||
}
|
||||
|
||||
var config *Configuration
|
||||
|
||||
func init() {
|
||||
Get()
|
||||
}
|
||||
|
||||
// Get config info from toml config file
|
||||
func Get() *Configuration {
|
||||
if config == nil {
|
||||
_, err := toml.DecodeFile(getConfigFile(), &config)
|
||||
helpers.CheckError(err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func getConfigFile() string {
|
||||
return filepath.Join(helpers.GetRootDir(), "config.toml")
|
||||
}
|
||||
|
||||
func GetSecretKey() []byte {
|
||||
config := Get()
|
||||
key, err := hex.DecodeString(config.Session.SecretKey)
|
||||
helpers.CheckError(err)
|
||||
return key
|
||||
}
|
||||
|
||||
func GetSessionKey() string {
|
||||
return Get().Session.Sessionkey
|
||||
}
|
7
misc/config/config_test.go
Normal file
7
misc/config/config_test.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetConfigFile(t *testing.T) {
|
||||
t.Log(Get())
|
||||
}
|
31
misc/cookie/cookie.go
Normal file
31
misc/cookie/cookie.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package cookie
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.1248.nz/1248/Otfe/misc/b64"
|
||||
)
|
||||
|
||||
func Create(w http.ResponseWriter, name string, value string) {
|
||||
c := &http.Cookie{Name: name, Value: b64.Encode(value)}
|
||||
http.SetCookie(w, c)
|
||||
}
|
||||
|
||||
func Read(r *http.Request, name string) (string, error) {
|
||||
c, err := r.Cookie(name)
|
||||
if err != nil {
|
||||
return "", errors.New("Cookie not found")
|
||||
}
|
||||
value, err := b64.Decode(c.Value)
|
||||
if err != nil {
|
||||
return "", errors.New("Failed to decode cookie")
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func Delete(w http.ResponseWriter, name string) {
|
||||
http.SetCookie(w, &http.Cookie{Name: name, MaxAge: -1, Expires: time.Unix(1, 0)})
|
||||
|
||||
}
|
39
misc/cookie/cookie_test.go
Normal file
39
misc/cookie/cookie_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package cookie
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.1248.nz/1248/Otfe/misc/b64"
|
||||
"git.1248.nz/1248/Otfe/misc/helpers"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
Create(recorder, "test", "test")
|
||||
request := &http.Request{Header: http.Header{
|
||||
"Cookie": recorder.HeaderMap["Set-Cookie"]}}
|
||||
cookie, err := request.Cookie("test")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
value, err := b64.Decode(cookie.Value)
|
||||
if err != nil || value != "test" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead(t *testing.T) {
|
||||
cookie := &http.Cookie{Name: "test", Value: b64.Encode("test")}
|
||||
|
||||
request, err := http.NewRequest("GET", "", nil)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
request.AddCookie(cookie)
|
||||
value, err := Read(request, "test")
|
||||
helpers.Equals(t, value, "test")
|
||||
}
|
55
misc/helpers/helpers.go
Normal file
55
misc/helpers/helpers.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
//CheckError checks for errors and logs them and stops the program
|
||||
func CheckError(err error) bool {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func GetRootDir() string {
|
||||
_, b, _, _ := runtime.Caller(0)
|
||||
dir := filepath.Dir(b)
|
||||
return filepath.Dir(filepath.Dir(dir))
|
||||
}
|
||||
|
||||
func GetAssets() string {
|
||||
return GetRootDir()
|
||||
}
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||
return string(hash), err
|
||||
}
|
||||
|
||||
func CheckPasswordHash(password, hash string) error {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err
|
||||
}
|
||||
|
||||
func RandHex() string {
|
||||
bytes := make([]byte, 12)
|
||||
rand.Read(bytes)
|
||||
return hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
func Bytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
35
misc/helpers/helpers_test.go
Normal file
35
misc/helpers/helpers_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package helpers_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.1248.nz/1248/Otfe/misc/helpers"
|
||||
"git.1248.nz/1248/Otfe/models"
|
||||
)
|
||||
|
||||
func TestGetRootDir(t *testing.T) {
|
||||
t.Log("Root path:", helpers.GetRootDir())
|
||||
}
|
||||
|
||||
func TestHashPassword(t *testing.T) {
|
||||
user := models.User{Email: "a@a.com", Username: "a"}
|
||||
user.Delete("username", "a")
|
||||
var err error
|
||||
password := "43539jgifdkvnm4935078uJKJR**$ufjqd98438uiAHFJean89q34JKDFJ"
|
||||
user.Password, err = helpers.HashPassword(password)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
user.Create()
|
||||
var user2 models.User
|
||||
user2.Read("username", "a")
|
||||
|
||||
t.Log(helpers.CheckPasswordHash(password, user2.Password))
|
||||
|
||||
}
|
||||
|
||||
func TestRandHex(t *testing.T) {
|
||||
for i := 0; i < 20; i++ {
|
||||
t.Log(helpers.RandHex())
|
||||
}
|
||||
}
|
36
misc/helpers/test.go
Normal file
36
misc/helpers/test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// assert fails the test if the condition is false.
|
||||
func Assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
|
||||
if !condition {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// ok fails the test if an err is not nil.
|
||||
func Ok(tb testing.TB, err error) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// equals fails the test if exp is not equal to act.
|
||||
func Equals(tb testing.TB, exp, act interface{}) {
|
||||
if !reflect.DeepEqual(exp, act) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
20
misc/rand/rand.go
Normal file
20
misc/rand/rand.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package rand
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
||||
"git.1248.nz/1248/Otfe/misc/b64"
|
||||
)
|
||||
|
||||
//Bytes generates an random set of bytes n long
|
||||
func Bytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
return b, err
|
||||
}
|
||||
|
||||
//B64String generates a base 64 string n bytess long
|
||||
func B64String(n int) (string, error) {
|
||||
b, err := Bytes(n)
|
||||
return b64.Encode(string(b)), err
|
||||
}
|
41
misc/test/seed.go
Normal file
41
misc/test/seed.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.1248.nz/1248/Otfe/misc/helpers"
|
||||
"git.1248.nz/1248/Otfe/models"
|
||||
"github.com/globalsign/mgo/bson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
models.DBWipeCollection("group", "user", "session")
|
||||
//admin user and group
|
||||
adminGroup := models.NewGroup("admin")
|
||||
adminGroup.Admin = true
|
||||
adminGroup.ID = bson.NewObjectId()
|
||||
adminGroup.Permissions["user.show"] = true
|
||||
|
||||
admin := models.User{}
|
||||
admin.Username = "admin"
|
||||
admin.Email = "admin"
|
||||
admin.ID = bson.NewObjectId()
|
||||
admin.Password, _ = helpers.HashPassword("admin")
|
||||
admin.PrimaryGroup = adminGroup.ID
|
||||
adminGroup.Users = append(adminGroup.Users, admin.ID)
|
||||
adminGroup.Create()
|
||||
admin.Create()
|
||||
|
||||
//user and user group
|
||||
userGroup := models.NewGroup("user")
|
||||
userGroup.ID = bson.NewObjectId()
|
||||
userGroup.Admin = false
|
||||
user := models.User{}
|
||||
user.ID = bson.NewObjectId()
|
||||
user.Username = "user"
|
||||
user.Email = "u"
|
||||
user.Password, _ = helpers.HashPassword("user")
|
||||
user.PrimaryGroup = userGroup.ID
|
||||
userGroup.Users = append(userGroup.Users, user.ID)
|
||||
user.Create()
|
||||
userGroup.Create()
|
||||
|
||||
}
|
Reference in New Issue
Block a user