github.com/volatiletech/authboss@v2.4.1+incompatible/module.go (about) 1 package authboss 2 3 import ( 4 "context" 5 "net/http" 6 "reflect" 7 ) 8 9 var registeredModules = make(map[string]Moduler) 10 11 // Moduler should be implemented by all the authboss modules. 12 type Moduler interface { 13 // Init the module 14 Init(*Authboss) error 15 } 16 17 // RegisterModule with the core providing all the necessary information to 18 // integrate into authboss. 19 func RegisterModule(name string, m Moduler) { 20 registeredModules[name] = m 21 } 22 23 // RegisteredModules returns a list of modules that are currently registered. 24 func RegisteredModules() []string { 25 mods := make([]string, len(registeredModules)) 26 i := 0 27 for k := range registeredModules { 28 mods[i] = k 29 i++ 30 } 31 32 return mods 33 } 34 35 // LoadedModules returns a list of modules that are currently loaded. 36 func (a *Authboss) LoadedModules() []string { 37 mods := make([]string, len(a.loadedModules)) 38 i := 0 39 for k := range a.loadedModules { 40 mods[i] = k 41 i++ 42 } 43 44 return mods 45 } 46 47 // IsLoaded checks if a specific module is loaded. 48 func (a *Authboss) IsLoaded(mod string) bool { 49 _, ok := a.loadedModules[mod] 50 return ok 51 } 52 53 // loadModule loads a particular module. It uses reflection to create a new 54 // instance of the module type. The original value is copied, but not deep 55 // copied so care should be taken to make sure most initialization happens 56 // inside the Initialize() method of the module. 57 // 58 // This method exists so many copies of authboss can be loaded and initialized 59 // at the same time if we didn't use this, then the registeredModules 60 // instances of the modules would end up used by the first instance of authboss. 61 func (a *Authboss) loadModule(name string) error { 62 module, ok := registeredModules[name] 63 if !ok { 64 panic("could not find module: " + name) 65 } 66 67 var wasPtr bool 68 modVal := reflect.ValueOf(module) 69 if modVal.Kind() == reflect.Ptr { 70 wasPtr = true 71 modVal = modVal.Elem() 72 } 73 74 modType := modVal.Type() 75 value := reflect.New(modType) 76 if !wasPtr { 77 value = value.Elem() 78 value.Set(modVal) 79 } else { 80 value.Elem().Set(modVal) 81 } 82 83 mod := value.Interface().(Moduler) 84 a.loadedModules[name] = mod 85 return mod.Init(a) 86 } 87 88 // ModuleListMiddleware puts a map in the data that can be used 89 // to provide the renderer with information about which pieces of the 90 // views to show. The bool is extraneous, as presence in the map is 91 // the indication of wether or not the module is loaded. 92 // Data looks like: 93 // map[modulename] = true 94 // 95 // oauth2 providers are also listed here using the syntax: 96 // oauth2.google for an example. Be careful since this doesn't actually mean 97 // that the oauth2 module has been loaded so you should do a conditional 98 // that checks for both. 99 func ModuleListMiddleware(ab *Authboss) func(http.Handler) http.Handler { 100 return func(next http.Handler) http.Handler { 101 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 102 var data HTMLData 103 104 ctx := r.Context() 105 dataIntf := ctx.Value(CTXKeyData) 106 if dataIntf != nil { 107 data = dataIntf.(HTMLData) 108 } else { 109 data = HTMLData{} 110 } 111 112 loaded := make(map[string]bool, len(ab.loadedModules)) 113 for k := range ab.loadedModules { 114 loaded[k] = true 115 } 116 117 for provider := range ab.Config.Modules.OAuth2Providers { 118 loaded["oauth2."+provider] = true 119 } 120 121 data[DataModules] = loaded 122 r = r.WithContext(context.WithValue(ctx, CTXKeyData, data)) 123 next.ServeHTTP(w, r) 124 }) 125 } 126 }