Inital commit

This commit is contained in:
2022-02-24 01:07:09 +13:00
commit 8bc3cee328
55 changed files with 2292 additions and 0 deletions

59
misc/auth/auth.go Normal file
View 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
View 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
View 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
View 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
}

View File

@@ -0,0 +1,7 @@
package config
import "testing"
func TestGetConfigFile(t *testing.T) {
t.Log(Get())
}

31
misc/cookie/cookie.go Normal file
View 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)})
}

View 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
View 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
}

View 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
View 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
View 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
View 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()
}