diff --git a/bloat.conf b/bloat.conf index e4ea384..ae2f007 100644 --- a/bloat.conf +++ b/bloat.conf @@ -25,6 +25,13 @@ client_scope=read write follow # Example: "http://localhost:8080", "https://mydomain.com" client_website=http://localhost:8080 +# In single instance mode, bloat will not ask for instance domain name and +# user will be directly redirected to login form. User login from other +# instances is not allowed in this mode. +# Empty value disables single instance mode. +# Example: "mydomain.com" +single_instance= + # Path of database directory. It's used to store session information. database_path=database diff --git a/config/config.go b/config/config.go index 2d4fb8d..d6140e5 100644 --- a/config/config.go +++ b/config/config.go @@ -11,16 +11,17 @@ import ( ) type config struct { - ListenAddress string - ClientName string - ClientScope string - ClientWebsite string - StaticDirectory string - TemplatesPath string - DatabasePath string - CustomCSS string - PostFormats []model.PostFormat - LogFile string + ListenAddress string + ClientName string + ClientScope string + ClientWebsite string + SingleInstance string + StaticDirectory string + TemplatesPath string + DatabasePath string + CustomCSS string + PostFormats []model.PostFormat + LogFile string } func (c *config) IsValid() bool { @@ -68,6 +69,8 @@ func Parse(r io.Reader) (c *config, err error) { c.ClientScope = val case "client_website": c.ClientWebsite = val + case "single_instance": + c.SingleInstance = val case "static_directory": c.StaticDirectory = val case "templates_path": diff --git a/main.go b/main.go index aac7741..99adb7c 100644 --- a/main.go +++ b/main.go @@ -101,7 +101,7 @@ func main() { s := service.NewService(config.ClientName, config.ClientScope, config.ClientWebsite, customCSS, config.PostFormats, renderer, - sessionRepo, appRepo) + sessionRepo, appRepo, config.SingleInstance) s = service.NewAuthService(sessionRepo, appRepo, s) s = service.NewLoggingService(logger, s) handler := service.NewHandler(s, config.StaticDirectory) diff --git a/service/service.go b/service/service.go index be83bd0..02c55cb 100644 --- a/service/service.go +++ b/service/service.go @@ -35,6 +35,7 @@ type Service interface { ServeSearchPage(ctx context.Context, c *model.Client, q string, qType string, offset int) (err error) ServeUserSearchPage(ctx context.Context, c *model.Client, id string, q string, offset int) (err error) ServeSettingsPage(ctx context.Context, c *model.Client) (err error) + SingleInstance(ctx context.Context) (instance string, ok bool) NewSession(ctx context.Context, instance string) (redirectUrl string, sessionID string, err error) Signin(ctx context.Context, c *model.Client, sessionID string, code string) (token string, userID string, err error) @@ -62,14 +63,15 @@ type Service interface { } type service struct { - clientName string - clientScope string - clientWebsite string - customCSS string - postFormats []model.PostFormat - renderer renderer.Renderer - sessionRepo model.SessionRepo - appRepo model.AppRepo + clientName string + clientScope string + clientWebsite string + customCSS string + postFormats []model.PostFormat + renderer renderer.Renderer + sessionRepo model.SessionRepo + appRepo model.AppRepo + singleInstance string } func NewService(clientName string, @@ -80,16 +82,18 @@ func NewService(clientName string, renderer renderer.Renderer, sessionRepo model.SessionRepo, appRepo model.AppRepo, + singleInstance string, ) Service { return &service{ - clientName: clientName, - clientScope: clientScope, - clientWebsite: clientWebsite, - customCSS: customCSS, - postFormats: postFormats, - renderer: renderer, - sessionRepo: sessionRepo, - appRepo: appRepo, + clientName: clientName, + clientScope: clientScope, + clientWebsite: clientWebsite, + customCSS: customCSS, + postFormats: postFormats, + renderer: renderer, + sessionRepo: sessionRepo, + appRepo: appRepo, + singleInstance: singleInstance, } } @@ -622,6 +626,14 @@ func (svc *service) ServeSettingsPage(ctx context.Context, c *model.Client) (err return svc.renderer.Render(rCtx, c.Writer, renderer.SettingsPage, data) } +func (svc *service) SingleInstance(ctx context.Context) (instance string, ok bool) { + if len(svc.singleInstance) > 0 { + instance = svc.singleInstance + ok = true + } + return +} + func (svc *service) NewSession(ctx context.Context, instance string) ( redirectUrl string, sessionID string, err error) { diff --git a/service/transport.go b/service/transport.go index 69b28ec..f52cca0 100644 --- a/service/transport.go +++ b/service/transport.go @@ -14,12 +14,24 @@ import ( "github.com/gorilla/mux" ) +const ( + sessionExp = 365 * 24 * time.Hour +) + func newClient(w io.Writer) *model.Client { return &model.Client{ Writer: w, } } +func setSessionCookie(w http.ResponseWriter, sessionID string, exp time.Duration) { + http.SetCookie(w, &http.Cookie{ + Name: "session_id", + Value: sessionID, + Expires: time.Now().Add(exp), + }) +} + func newCtxWithSesion(req *http.Request) context.Context { ctx := context.Background() sessionID, err := req.Cookie("session_id") @@ -93,11 +105,25 @@ func NewHandler(s Service, staticDir string) http.Handler { signinPage := func(w http.ResponseWriter, req *http.Request) { c := newClient(w) ctx := context.Background() - err := s.ServeSigninPage(ctx, c) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - s.ServeErrorPage(ctx, c, err) - return + instance, ok := s.SingleInstance(ctx) + if ok { + url, sessionID, err := s.NewSession(ctx, instance) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + s.ServeErrorPage(ctx, c, err) + return + } + + setSessionCookie(w, sessionID, sessionExp) + w.Header().Add("Location", url) + w.WriteHeader(http.StatusFound) + } else { + err := s.ServeSigninPage(ctx, c) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + s.ServeErrorPage(ctx, c, err) + return + } } } @@ -291,12 +317,7 @@ func NewHandler(s Service, staticDir string) http.Handler { return } - http.SetCookie(w, &http.Cookie{ - Name: "session_id", - Value: sessionID, - Expires: time.Now().Add(365 * 24 * time.Hour), - }) - + setSessionCookie(w, sessionID, sessionExp) w.Header().Add("Location", url) w.WriteHeader(http.StatusFound) } @@ -689,12 +710,8 @@ func NewHandler(s Service, staticDir string) http.Handler { ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token")) s.Signout(ctx, c) - http.SetCookie(w, &http.Cookie{ - Name: "session_id", - Value: "", - Expires: time.Now(), - }) + setSessionCookie(w, "", 0) w.Header().Add("Location", "/") w.WriteHeader(http.StatusFound) }