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  }