diff --git a/authboss.go b/authboss.go index fe9f2f4..3c990a6 100644 --- a/authboss.go +++ b/authboss.go @@ -6,3 +6,46 @@ races without having to think about the hard questions like how to store Remember Me tokens, or passwords. */ package authboss // import "gopkg.in/authboss.v0" + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "path" +) + +var logger io.Writer = ioutil.Discard + +// Init authboss and it's loaded modules with a configuration. +func Init(config *Config) error { + if config.Storer == nil { + return errors.New("Configuration must provide a storer.") + } + + logger = config.LogWriter + + for name, mod := range modules { + fmt.Fprintf(logger, "[%-10s] Initializing\n", name) + if err := mod.Initialize(config); err != nil { + return fmt.Errorf("[%s] Error Initializing: %v", name, err) + } + } + + return nil +} + +// Router returns a router to be mounted at some mountpoint. +func Router(config *Config) http.Handler { + mux := http.NewServeMux() + + for name, mod := range modules { + for route, handler := range mod.Routes() { + fmt.Fprintf(logger, "[%-10s] Register Route: %s\n", name) + mux.HandleFunc(path.Join(config.MountPath, route), handler) + } + } + + return http.StripPrefix(config.MountPath, mux) +} diff --git a/authboss_test.go b/authboss_test.go new file mode 100644 index 0000000..794a961 --- /dev/null +++ b/authboss_test.go @@ -0,0 +1,52 @@ +package authboss + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +func TestMain(main *testing.M) { + RegisterModule("testmodule", testMod) + code := main.Run() + os.Exit(code) +} + +func TestAuthBossInit(t *testing.T) { + c := NewConfig() + + err := Init(c) + if err == nil || !strings.Contains(err.Error(), "storer") { + t.Error("Expected error about a storer, got:", err) + } + + c.Storer = testStorer(0) + err = Init(c) + if err != nil { + t.Error("Unexpected error:", err) + } + if testMod.c == nil { + t.Error("Expected the modules to be passed the config.") + } +} + +func TestAuthBossRouter(t *testing.T) { + t.Parallel() + + c := NewConfig() + c.MountPath = "/candycanes" + c.LogWriter = os.Stdout + + router := Router(c) + + r, _ := http.NewRequest("GET", "/candycanes/testroute", nil) + response := httptest.NewRecorder() + + router.ServeHTTP(response, r) + + if response.Header().Get("testmodule") != "test" { + t.Error("Expected a header to have been set.") + } +} diff --git a/config.go b/config.go index 8718df6..cc4eb3d 100644 --- a/config.go +++ b/config.go @@ -1,14 +1,24 @@ package authboss -// Config is a map to provide configuration key-values. -// +import ( + "io" + "io/ioutil" +) + +// Config holds all the configuration for both authboss and it's modules. type Config struct { MountPath string `json:"mountPath" xml:"mountPath"` AuthLoginPageURI string `json:"authLoginPage" xml:"authLoginPage"` AuthLogoutRedirect string `json:"authLogoutRedirect" xml:"authLogoutRedirect"` + + Storer Storer `json:"-" xml:"-"` + LogWriter io.Writer `json:"-" xml:"-"` } -func NewConfig() { - +// NewConfig creates a new config full of default values ready to override. +func NewConfig() *Config { + return &Config{ + LogWriter: ioutil.Discard, + } } diff --git a/core.go b/core.go deleted file mode 100644 index 4434dd2..0000000 --- a/core.go +++ /dev/null @@ -1,18 +0,0 @@ -package authboss - -import ( - "net/http" -) - -type Routes map[string]http.HandlerFunc - -type Modularizer interface { - Initialize(Config) error - Routes() Routes - Style() ([]byte, error) - Storage() -} - -func Register(m Modularizer) { - -} diff --git a/core/core.go b/core/core.go deleted file mode 100644 index be56541..0000000 --- a/core/core.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -Package core is essentially just a namespacing for the module system. This -allows the main package for user consumption to remain free of cruft. -*/ -package core // import "gopkg.in/authboss.v0/core - -// dataType represents the various types that clients must be able to store. -// This type is duplicated from storer.go that we might avoid having users -// importing the core package. -type dataType int - -const ( - Integer dataType = iota - String - DateTime -) - -var modules = make(map[string]module) - -type module struct { - Name string - Storage StorageOptions - RequiredConfig []string -} - -// StorageOptions is a map depicting the things a module must be able to store. -type StorageOptions map[string]DataType - -// Register a module with the core providing all the necessary information to -// integrate into authboss. -func Register(name string, storage StorageOptions, requiredConfig ...string) { -} - -// LoadedModules returns a list of modules that are currently loaded. -func LoadedModules() []string { - mods := make([]string, len(modules)) - i := 0 - for k, _ := range modules { - mods[i] = k - i++ - } - - return mods -} - -// IsLoaded checks if a specific module is loaded. -func IsLoaded(mod string) bool { - _, ok := modules[mod] - return ok -} diff --git a/module.go b/module.go new file mode 100644 index 0000000..5c87deb --- /dev/null +++ b/module.go @@ -0,0 +1,43 @@ +package authboss + +import ( + "net/http" +) + +var modules = make(map[string]Modularizer) + +// RouteTable is a routing table from a path to a handlerfunc. +type RouteTable map[string]http.HandlerFunc + +// StorageOptions is a map depicting the things a module must be able to store. +type StorageOptions map[string]DataType + +type Modularizer interface { + Initialize(*Config) error + Routes() RouteTable + Storage() StorageOptions +} + +// RegisterModule with the core providing all the necessary information to +// integrate into authboss. +func RegisterModule(name string, m Modularizer) { + modules[name] = m +} + +// LoadedModules returns a list of modules that are currently loaded. +func LoadedModules() []string { + mods := make([]string, len(modules)) + i := 0 + for k, _ := range modules { + mods[i] = k + i++ + } + + return mods +} + +// IsLoaded checks if a specific module is loaded. +func IsLoaded(mod string) bool { + _, ok := modules[mod] + return ok +} diff --git a/module_test.go b/module_test.go new file mode 100644 index 0000000..1a736f3 --- /dev/null +++ b/module_test.go @@ -0,0 +1,60 @@ +package authboss + +import ( + "net/http" + "testing" +) + +const testModName = "testmodule" + +type testModule struct { + c *Config + s StorageOptions + r RouteTable +} + +var testMod = &testModule{ + r: RouteTable{ + "/testroute": testHandler, + }, +} + +func testHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("testhandler", "test") +} + +func (t *testModule) Initialize(c *Config) error { + t.c = c + return nil +} + +func (t *testModule) Routes() RouteTable { + return t.r +} + +func (t *testModule) Storage() StorageOptions { + return t.s +} + +func TestRegister(t *testing.T) { + t.Parallel() + + // RegisterModule called by TestMain. + + if _, ok := modules["testmodule"]; !ok { + t.Error("Expected module to be saved.") + } +} + +func TestLoadedModules(t *testing.T) { + t.Parallel() + + // RegisterModule called by TestMain. + + loadedMods := LoadedModules() + if len(loadedMods) != 1 { + t.Error("Expected only a single module to be loaded.") + } else if loadedMods[0] != "testmodule" { + t.Error("Expected testmodule to be loaded.") + } +} diff --git a/storer_test.go b/storer_test.go index 9b5a707..6d19206 100644 --- a/storer_test.go +++ b/storer_test.go @@ -6,6 +6,12 @@ import ( "time" ) +type testStorer int + +func (t testStorer) Create(key string, attr Attributes) error { return nil } +func (t testStorer) Put(key string, attr Attributes) error { return nil } +func (t testStorer) Get(key string, attrMeta AttributeMeta) (interface{}, error) { return nil, nil } + func TestAttributes_Bind(t *testing.T) { anInteger := 5 aString := "string"