Commit ee149dc0 by Liza

* Move util into common

* Start tentative auth implementation (separate web and cli auth plugins, getting started with cli Auth0 provider only)
parent 28c1417e
\.idea/workspace\.xml
config/dev/auth\.json
lib/infrastructure/auth/cli/auth0/tests/config_test\.go
lib/infrastructure/auth/cli/cli\.so
package main
import "fmt"
import "gitlab.com/drakonka/gosnaillife/lib/infrastructure"
import (
"gitlab.com/drakonka/gosnaillife/lib/infrastructure"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/env"
"gitlab.com/drakonka/gosnaillife/lib/common"
)
var App infrastructure.Application
var App env.Application
func main() {
fmt.Println("Hello, Snail")
App = infrastructure.Init("./config")
App = infrastructure.Init("./config", common.CLI)
}
\ No newline at end of file
package common
type ClientType uint8
const (
CLI ClientType = iota + 1
Web
)
\ No newline at end of file
package util
import (
"crypto/sha1"
"io"
"encoding/base32"
)
func base64Encode(str []byte) string {
return base32.StdEncoding.EncodeToString([]byte(str))
}
func sha256(str string) []byte {
hasher := sha1.New()
io.WriteString(hasher, str)
sha := hasher.Sum(nil)
return sha
}
package auth
import (
"gitlab.com/drakonka/gosnaillife/lib/common"
)
func NewAuthenticator(clientType common.ClientType) *Authenticator {
a := Authenticator{}
a.loadPlugin(clientType)
return &a
}
package auth
import (
"plugin"
"fmt"
"gitlab.com/drakonka/gosnaillife/lib/common"
"os"
"runtime"
"path/filepath"
"gitlab.com/drakonka/gosnaillife/lib/common/util"
)
type Authenticator struct {
Plugin *plugin.Plugin
Providers []Provider
}
func (a *Authenticator) PrepAuthProvider(name string, properties map[string]string) (provider Provider, err error) {
sym := a.getProviderSymbol(name)
provider = a.getProvider(sym)
provider.Configure(properties)
return provider, err
}
func (a *Authenticator) loadPlugin(clientType common.ClientType) {
var pluginPath string
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
switch clientType {
case common.CLI:
pluginPath = dir + "/cli/cli.so"
break
case common.Web:
pluginPath = dir + "/web/web.so"
break
}
plug, err := plugin.Open(pluginPath)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
a.Plugin = plug
}
func (a *Authenticator) getProviderSymbol(name string) plugin.Symbol {
funcName := name + "Provider"
sym, err := a.Plugin.Lookup(funcName)
if err != nil {
util.HandleErr(err, "Auth Symbol")
}
return sym
}
func (a *Authenticator) getProvider(sym plugin.Symbol) Provider {
provider, ok := sym.(Provider)
if !ok {
fmt.Println("unexpected type from module symbol")
os.Exit(1)
}
return provider
}
package auth0
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
"encoding/json"
"errors"
)
type Auth0 struct {
accessToken string
clientId string
clientSecret string
domain string
}
func (ap *Auth0) Configure(credentials map[string]string) {
ap.clientId = credentials["client_id"]
ap.clientSecret = credentials["client_secret"]
ap.domain = credentials["domain"]
}
func (ap *Auth0) Register(email, password string) (id string, err error) {
url := ap.domain + "dbconnections/signup"
postSpec := fmt.Sprintf(
`{"client_id":"%s",
"email":"%s",
"password":"%s",
"connection":"Username-Password-Authentication"}`, ap.clientId, email, password)
payload := strings.NewReader(postSpec)
req, err := http.NewRequest("POST", url, payload)
if err != nil {
return id, err
}
req.Header.Add("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return id, err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return id, err
}
var data map[string]interface{}
err = json.Unmarshal([]byte(string(body)), &data)
if val, ok := data["_id"]; ok {
id = val.(string)
}
return id, err
}
func (ap *Auth0) Login(username, password string) (err error) {
url := ap.domain + "oauth/token"
postSpec := fmt.Sprintf(`{"grant_type":"password",
"username":"%s",
"password":"%s",
"client_secret": "%s",
"client_id":"%s"}`, username, password, ap.clientSecret, ap.clientId)
payload := strings.NewReader(postSpec)
req, err := http.NewRequest("POST", url, payload)
if err != nil {
return err
}
req.Header.Add("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
var data map[string]interface{}
err = json.Unmarshal([]byte(string(body)), &data)
if err != nil {
return err
}
if e, ok := data["error"]; ok {
msg := fmt.Sprintf("%s - %s", e.(string), data["error_description"].(string))
err = errors.New(msg)
return err
}
if token, ok := data["access_token"]; ok {
ap.accessToken = token.(string)
} else {
err = errors.New("Access token not found in response")
}
return err
}
func (ap *Auth0) HasAccessToken() bool {
if ap.accessToken != "" {
return true
}
return false
}
\ No newline at end of file
package tests
import (
"testing"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/auth/cli/auth0"
"fmt"
"os"
)
var creds testCredentials
func TestMain(m *testing.M) {
creds = GetCredentials()
ret := m.Run()
creds = testCredentials{}
os.Exit(ret)
}
func TestAuth0Signup(t *testing.T) {
fmt.Printf("Creating user with ID: %s, pass: %s", creds.email, creds.password)
provider := getProvider(creds)
id, err := provider.Register(creds.email, creds.password)
if err != nil {
t.Errorf("Signup test failed: ", err.Error())
} else if id == "0" {
t.Errorf("Signup test failed; unexpected user ID: " + string(id))
}
}
func TestAuth0Login(t *testing.T) {
fmt.Printf("Logging in with ID: %s, pass: %s", creds.email, creds.password)
provider := getProvider(creds)
err := provider.Login(creds.email, creds.password)
if err != nil {
t.Errorf("Login test failed: " + err.Error())
} else if (provider.HasAccessToken() == false) {
t.Errorf("Login test failed: Access token not found")
}
}
func getProvider(creds testCredentials) *auth0.Auth0 {
provider := auth0.Auth0{}
configCreds := map[string]string{}
configCreds["client_id"] = creds.clientId
configCreds["client_secret"] = creds.clientSecret
configCreds["domain"] = creds.domain
provider.Configure(configCreds)
return &provider
}
\ No newline at end of file
package main
import "gitlab.com/drakonka/gosnaillife/lib/infrastructure/auth/cli/auth0"
var Auth0Provider auth0.Auth0
func main() {
}
\ No newline at end of file
package auth
type Provider interface {
Configure(credentials map[string]string)
Register(email, password string) (id int, err error)
Login(username, password string)
}
\ No newline at end of file
......@@ -3,7 +3,7 @@ package mysql
import (
_ "github.com/go-sql-driver/mysql"
"fmt"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/util"
"gitlab.com/drakonka/gosnaillife/lib/common/util"
"github.com/jmoiron/sqlx"
)
......@@ -26,7 +26,7 @@ type Connection struct {
type Error string
// Configure sets the MySQL user and database details.
// Configure sets the MySQL auth and database details.
func (mysql *MySql) Configure(username, userpass, dbhost, dbname string) {
mysql.user = User{username, userpass}
mysql.connection = Connection{dbhost, dbname}
......
......@@ -2,16 +2,21 @@ package env
import (
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/databases"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/auth"
"gitlab.com/drakonka/gosnaillife/lib/common"
)
// App is the main application instance.
type Application struct {
ClientType common.ClientType
Configuration *Configuration
Database databases.Database
Authenticator *auth.Authenticator
}
type Configuration struct {
DBconfig DBconfig
DBconfig DBconfig
AuthConfig AuthConfig
}
type DBconfig struct {
......@@ -22,4 +27,12 @@ type DBconfig struct {
DBName string
}
type AuthConfig struct {
Providers []AuthProvider
}
type AuthProvider struct {
Name string
Properties map[string]string
}
var App Application
......@@ -4,18 +4,28 @@ import (
"encoding/json"
"os"
"io/ioutil"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/util"
"gitlab.com/drakonka/gosnaillife/lib/common/util"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/databases/mysql"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/env"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/auth"
"gitlab.com/drakonka/gosnaillife/lib/common"
)
// Init initializes the application by reading the Configuration files
// in the config folder.
func Init(confdir string) env.Application {
func Init(confdir string, clientType common.ClientType) env.Application {
app := env.Application{};
app.ClientType = clientType
app.Configuration = &env.Configuration{}
configure(app.Configuration, confdir)
app = prepDb(app)
app = prepAuthenticator(app)
env.App = app;
return app;
}
func prepDb(app env.Application) env.Application {
dbc := app.Configuration.DBconfig
switch dbc.DBType {
case "mysql":
......@@ -24,22 +34,47 @@ func Init(confdir string) env.Application {
break
}
app.Database.Connect()
env.App = app;
return app;
return app
}
func prepAuthenticator(app env.Application) env.Application {
authenticator := auth.NewAuthenticator(app.ClientType)
app.Authenticator = authenticator
authenticator.Providers = prepAuthProviders(app)
return app
}
func prepAuthProviders(app env.Application) []auth.Provider {
authenticator := app.Authenticator
providers := []auth.Provider{}
ac := app.Configuration.AuthConfig
for _, p := range(ac.Providers) {
provider, err := authenticator.PrepAuthProvider(p.Name, p.Properties)
if (err != nil) {
util.HandleErr(err, "Auth Provider Prep")
}
providers = append(providers, provider)
}
return providers
}
// configure reads the current environment from env.conf
// and loads the associated Configuration files.
func configure (c *env.Configuration, confdir string) error {
file, err := ioutil.ReadFile(confdir + "/env.conf")
if err != nil {
util.HandleErr(err, "")
return err
}
envFile := string(file)
// db
dbc, err := configureDB(confdir, envFile)
c.DBconfig = dbc
// auth
authconfig, err := configureAuthProviders(confdir, envFile)
c.AuthConfig = authconfig
return err
}
......@@ -60,4 +95,21 @@ func configureDB(confdir, envFile string) (env.DBconfig, error) {
util.HandleErr(err, "")
}
return dbc, err
}
func configureAuthProviders(confdir, envFile string) (env.AuthConfig, error) {
authPath := confdir + "/" + envFile + "/auth.json"
file, err := os.Open(authPath)
if err != nil {
util.HandleErr(err, "")
}
defer file.Close()
decoder := json.NewDecoder(file)
conf := env.AuthConfig{}
err = decoder.Decode(&conf)
if err != nil {
util.HandleErr(err, "")
}
return conf, err
}
\ No newline at end of file
......@@ -4,11 +4,12 @@ import (
"testing"
"reflect"
"gitlab.com/drakonka/gosnaillife/lib/infrastructure/env"
"gitlab.com/drakonka/gosnaillife/lib/common"
)
func TestAppInit(t *testing.T){
expected := reflect.TypeOf(env.Application{})
actual := Init("../../config")
actual := Init("../../config", common.CLI)
if reflect.TypeOf(actual) != expected {
t.Error("Test failed")
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment