Exemple #1
0
// Use adds the session capability on router.
func Use(router *wcg.Router, configure func()) {
	middleware.SessionConfigIni.StoreFactory = gae.SessionStoreFactory
	sessionBefore, sessionAfter := middleware.SessionSupport()
	csrf := middleware.CSRFSupport()

	router.Before(wcg.NewNamedHandler("session.before", func(res *wcg.Response, req *wcg.Request) {
		if canSkipSessionMiddleware(req) {
			return
		}
		sessionBefore.Process(res, req)
		if lib.IsOnGAE() { // Check only on GAE environment
			if req.Method() != "GET" && req.Method() != "HEAD" {
				csrf.Process(res, req)
			}
		}
	}))

	configure()

	router.After(wcg.NewNamedHandler("session.after", func(res *wcg.Response, req *wcg.Request) {
		if canSkipSessionMiddleware(req) {
			return
		}
		sessionAfter.Process(res, req)
	}))
}
Exemple #2
0
// StaticFile hosting static for prefix path.
func ServeFile(filename string) wcg.Handler {
	h := wcg.HandlerFunc(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, filename)
	}))
	return wcg.NewNamedHandler("ServeFile", func(res *wcg.Response, req *wcg.Request) {
		h(res, req)
		res.Close()
	})
}
Exemple #3
0
// NewInstance creates *Instance
func NewInstance() *Instance {
	instance := &Instance{
		Server: gae.NewServer(),
	}
	instance.Routes().Before(wcg.NewNamedHandler("init", func(res *wcg.Response, req *wcg.Request) {
		instance.once.Do(func() {
			req.Logger.Infof("----------- Server initialization ----------- ")
			if instance.Init != nil {
				instance.Init(req)
			}
			req.Logger.Infof("----------- Apps initialization ----------- ")
			for _, app := range instance.apps {
				if app.Init != nil {
					app.Init(req)
				}
			}
			req.Logger.Infof("----------- All init processes finished ----------- ")
		})
	}))
	auth.Prepare(instance.Routes(), func() {
		instance.Routes().GET("/cron.yaml", wcg.NewNamedHandler("cron.yaml", func(res *wcg.Response, req *wcg.Request) {
			res.Header().Add("Content-Type", "text/yaml")
			res.WriteString("cron:\n")
			for _, app := range instance.apps {
				res.WriteString(app.Cron().ToYAML())
			}
		}))
		instance.Routes().GET("/queue.yaml", wcg.NewNamedHandler("queue.yaml", func(res *wcg.Response, req *wcg.Request) {
			res.Header().Add("Content-Type", "text/yaml")
			res.WriteString("queue:\n")
			for _, app := range instance.apps {
				res.WriteString(app.Queue().ToYAML())
			}
		}))
	})

	instance.Routes().NotFound = Handler(func(req *wcg.Request) response.Response {
		return response.NotFound(req)
	})
	return instance
}
Exemple #4
0
func byHeader(router *wcg.Router, configure func()) {
	router.Before(wcg.NewNamedHandler("HeaderAuth", func(res *wcg.Response, req *wcg.Request) {
		if !request.ByGuest(req) { // already authenticated
			req.Logger.Warnf("request.Authorize is called more than once.")
			return
		}
		// req.Logger.Infof("Token Authorization: %s", tokenString)
		if request.IsTask(req) {
			req.User = request.NewTaskUser(req)
		} else if request.IsCron(req) {
			req.User = request.CronUser
		} else {
			authorizeByAPIToken(req)
		}
	}))
	configure()
}
Exemple #5
0
// AccessLog middleware writes access logs into `out` writer with the specified `format`.
// You can use the following placeholder variables in format string with '$var'.
//
//   - IP ip address of the client.
//   - User an authenticated user name
//   - Time access time like '02/Nov/2013:17:29:48 +0900'
//   - Method http method
//   - Path path string including query string.
//   - Version version string such as 'HTTP/1.1'
//   - Status HTTP status code number
//   - Size response body size.
//   - Referer referer string
//   - Agent user agent string
//   - ResponseTime seconds taken for the response.
//
// if format string is "", it would be "$IP - $User [$Time] \"$Method $Path $Version\" $Status $Size \"$Referer\" \"$Agent\" $ResponseTime".
func AccessLog(out io.Writer, format string) wcg.Handler {
	if format == "" {
		format = defaultAccsesLogFormat
	}
	repl := func(name string, v ...interface{}) []byte {
		req := v[0].(*wcg.Response)
		res := v[1].(*wcg.Request)
		fun := convFun[name]
		if fun != nil {
			return []byte(fun(req, res))
		}
		return []byte{}
	}
	logger := wcg.NewCompiledFormatter(format, repl)
	// Return a function called in every request in the final phase of routers.
	return wcg.NewNamedHandler("AccessLog", func(res *wcg.Response, req *wcg.Request) {
		out.Write(logger.Format(res, req))
		out.Write([]byte{'\n'})
	})
}
Exemple #6
0
// CSRFSupport to add a helper method csrf() to generate a hidden input for CSRF, and
// returns validator handler
func CSRFSupport() wcg.Handler {
	wcg.AddViewHelper("csrf", func(req *wcg.Request) template.HTML {
		if s := req.Session; s != nil {
			tok, _ := s.CSRFToken()
			return template.HTML("<input type=\"hidden\" name=\"" + CSRFTokenParamName + "\" value=\"" + tok + "\"></input>")
		}
		return ""
	})
	wcg.AddViewHelper("csrf_token", func(req *wcg.Request) string {
		if s := req.Session; s != nil {
			tok, salt := s.CSRFToken()
			req.Logger.Debugf(
				"Generating CSRF Token: salt=%q, secret=%q, tok=%q",
				salt, s.CSRFSecret, tok,
			)
			return tok
		}
		return ""
	})

	var csrferror = map[string]string{
		"error":       "CSRF error",
		"description": "An invalid CSRF token was passed to the server.",
	}
	return wcg.NewNamedHandler("csrf", func(res *wcg.Response, req *wcg.Request) {
		if res.IsClosed() {
			return
		}

		token := req.Form(CSRFTokenParamName)
		if token == "" {
			// try to seek header
			token = req.Header(CSRFTokenHeaderName)
		}
		if err := req.Session.ValidateToken(token); err != nil {
			res.WriteJSONWithStatus(403, nil, csrferror)
		}
	})
}
Exemple #7
0
func bySession(router *wcg.Router, configure func()) {
	fbconfig := facebook.NewAuthConfig("dummy", "dumyy", "")
	fbconfig.RedirectURL = "/login/facebook/callback"
	fbconfig.ContextFactory = func(res *wcg.Response, req *wcg.Request) context.Context {
		return gae.NewContext(req)
	}
	fbconfig.UnauthorizedHandler = wcg.AnonymousHandler(func(res *wcg.Response, req *wcg.Request) {
		res.TemplatesWithStatus(401, nil, "permrejected.html", "header.html", "footer.html")
	})
	fbconfig.AuthorizedHandler = wcg.AnonymousHandler(func(res *wcg.Response, req *wcg.Request) {
		ref, _ := req.Session.Get("LoginRef")
		if ref != "" && strings.HasPrefix(ref, "/") {
			res.Redirect(wcg.AbsoluteURL(req, ref), http.StatusFound)
		} else {
			res.Redirect("/", http.StatusFound)
		}
	})
	fbconfig.InvalidatedHandler = wcg.AnonymousHandler(func(res *wcg.Response, req *wcg.Request) {
		req.Logger.Debugf("Guest user access.")
	})
	fbconfig.Scopes = []string{}
	fbauth, fbcallback, fbvalidates, fblogout := middleware.OAuth2(fbconfig)

	// set routes
	router.Before(wcg.NewNamedHandler("facebook.validate", func(res *wcg.Response, req *wcg.Request) {
		if !request.ByGuest(req) { // already authenticated
			return
		}
		if req.Session == nil {
			return
		}
		// Check the fbconfig from ServerConfig
		fbapp := configs.GetMultiValues(
			req,
			"facebook_app_id",
			"facebook_app_secret",
			"facebook_page_id",
		)
		if fbapp[0] != "" && fbapp[1] != "" {
			fbconfig.ClientID = fbapp[0]
			fbconfig.ClientSecret = fbapp[1]
			fbvalidates.Process(res, req)
		}
	}))

	router.GET("/login/facebook", wcg.NewNamedHandler("facebook.login.auth", func(res *wcg.Response, req *wcg.Request) {
		if !isFBConfigured(fbconfig) {
			return
		}
		req.Session.Set("LoginRef", req.Query("ref"))
		fbauth.Process(res, req)
	}))

	router.GET("/login/facebook/callback", wcg.NewNamedHandler("facebook.login.callback", func(res *wcg.Response, req *wcg.Request) {
		if !isFBConfigured(fbconfig) {
			return
		}
		fbcallback.Process(res, req)
	}))

	router.POST("/logout/facebook", wcg.NewNamedHandler("facebook.logout", func(res *wcg.Response, req *wcg.Request) {
		if !isFBConfigured(fbconfig) {
			return
		}
		fblogout.Process(res, req)
		res.Redirect("/", http.StatusFound)
	}))

	configure()
}
Exemple #8
0
// SessionSupportWithConfig returns two middleware functions for session support, which need to be registered
// on route.Before and route.After.
//
// Example:
//
//     sprepare, scomplete := SessionSupport()
//
//     route.Before(sprepare)
//     route.After(scomplete)
//
func SessionSupportWithConfig(cfg *SessionConfig) (wcg.Handler, wcg.Handler) {
	if cfg.StoreFactory == nil {
		memorystore := wcg.NewMemorySessionStore()
		cfg.StoreFactory = func(req *wcg.Request) wcg.SessionStore {
			req.Logger.Warnf("Memory SessionStore is used so that the session data would lost suddenly.")
			req.Logger.Warnf("If you are in GAE environment (including devserver), you must use GAESessionStoreFactory")
			return memorystore
		}
	}

	// load handler loads the session data from backend storage.
	load := func(res *wcg.Response, req *wcg.Request) {
		if res.IsClosed() {
			return
		}
		var sess *wcg.Session
		var err error
		store := cfg.StoreFactory(req)
		c, err := req.SignedCookie(cfg.CookieName, cfg.Key)
		if err != nil {
			req.Logger.Debugf("Could not decode the signed cookie on %s: %v", cfg.Key, err)
		}
		if c != nil {
			if wcg.IsUUID(c.Value) {
				sess, err = store.Load(wcg.UUID(c.Value))
			}
			if err != nil {
				req.Logger.Warnf(
					"Could not load the session (id: %q) from the store (%v), use a new session.",
					err, c.Value)
				sess = nil
				c = nil
			} else {
				if sess == nil {
					req.Logger.Debugf("Session data for %q was not found on backend.", c.Value)
				} else {
					req.Logger.Debugf("Session data for %q found.", c.Value)
					expiredAt := sess.Timestamp.Add(time.Duration(cfg.MaxAge) * time.Second)
					if expiredAt.Before(time.Now()) {
						// expired
						req.Logger.Infof("Session data for %q is found but expired.", c.Value)
						if _, err := store.Delete(sess.ID); err != nil {
							req.Logger.Errorf("An error occurred while deleting the expired session (%q): %v", sess.ID, err)
						}
						sess = nil
					}
				}
			}
		}
		if c == nil {
			// no cookie found
			c = &http.Cookie{}
			c.Name = cfg.CookieName
			c.HttpOnly = cfg.HTTPOnly
			c.MaxAge = cfg.MaxAge
			if cfg.Domain != "" {
				c.Domain = cfg.Domain
			}
		}

		c.Path = cfg.Path

		// No session found
		if sess == nil {
			sess = wcg.NewSession(req)
			sess.Timestamp = time.Now()
			req.Logger.Debugf("Creating the new session.")
			store := cfg.StoreFactory(req)
			err := store.Save(sess)
			if err != nil {
				res.RenderInternalError("New Session could not be created: %v", err)
				return
			}
		} else {
			// Update the timestamp.
			sess.Timestamp = time.Now()
		}

		// Set the cookie data
		c.Value = string(sess.ID)
		c.Expires = sess.Timestamp.Add(time.Duration(cfg.MaxAge) * time.Second)
		c.MaxAge = cfg.MaxAge
		req.Session = sess
		encoded, _ := sess.Encode()
		req.SetLocal("__session_support", encoded)
		res.SetSignedCookie(c, cfg.Key)
	}

	// save handler store the cookie data into backend storage.
	save := func(res *wcg.Response, req *wcg.Request) {
		if encoded, ok := req.Local("__session_support").(string); ok {
			nencoded, _ := req.Session.Encode()
			// Store session if it is changed.
			if encoded != nencoded {
				store := cfg.StoreFactory(req)
				err := store.Save(req.Session)
				if err != nil {
					// TODO: log session error
					req.Logger.Errorf("Could not save the session data on backend: %v", err)
				} else {
					req.Logger.Debugf("Successfully stored the session data on backend.")
				}
			} else {
				req.Logger.Debugf("Session is not changed, skipped to be stored.")
			}
		}
	}
	return wcg.NewNamedHandler("Session.Load", load), wcg.NewNamedHandler("Session.Save", save)
}
Exemple #9
0
// OAuth2 composes a list of middleware functions to implement oauth2 handlers.
//
//  - 1st: would be a middleware that redicts to the authorize URL.
//  - 2nd: would be a middleware that serves the callback from OAuth URL. if the client authorize the access,
//         it would call AuthorizedHandler after registering the token with the session.
//         otherwise, it would call UnauthorizedHandler.
//  - 3rd: would be a middleware that checks the token stored in the session and validate it.
//         if the token is not found or invalidated, it would call InvalidateHandler.
//  - 4th: would be a middleware that perform logout by cleaning up the current oauth token from a sessions
//
// This support function also adds some view helpers on your view system.
//
// oauth2_by(providerName string) : returns true if the user is authoriezed
//
func OAuth2(cfg *OAuth2Config) (
	redirector wcg.Handler, // middleware to redirect authorize URL
	callbackHandler wcg.Handler, // middleware to serve the callback from OAuth URL
	validator wcg.Handler, // middleware to validate the token in session.
	logoutHandler wcg.Handler, // middleware to perform logout.
) {
	registerViewHelpers()

	if cfg.ClientID == "" || cfg.ClientSecret == "" {
		panic("ClietnID or ClietnSecret is empty: please check your configuration is corret and loaded.")
	}

	u, err := url.Parse(cfg.Endpoint.AuthURL)
	if err != nil {
		panic(err)
	}
	host := u.Host
	pname := cfg.ParameterName
	authh := cfg.AuthorizedHandler
	unauthh := cfg.UnauthorizedHandler
	unvalidh := cfg.InvalidatedHandler
	contextFactory := cfg.ContextFactory

	if pname == "" {
		pname = DefaultParameterName
	}

	if authh == nil {
		authh = DefaultAuthorizedHandler
	}

	if unauthh == nil {
		unauthh = DefaultUnauthorizedHandler
	}

	if contextFactory == nil {
		contextFactory = DefaultContextFactory
	}

	redirector = wcg.NewNamedHandler("OAuth2.redirector",
		func(res *wcg.Response, req *wcg.Request) {
			if req.Session == nil {
				res.RenderInternalError("OAuth2: Session is nil")
				res.End()
				return
			}
			res.Redirect(cfg.Use(req).AuthCodeURL(""), http.StatusFound)
		})

	callbackHandler = wcg.NewNamedHandler("OAUth2.callback",
		func(res *wcg.Response, req *wcg.Request) {
			if req.Session == nil {
				res.RenderInternalError("OAuth2: Session is nil")
				res.End()
				return
			}

			// get a code and exchange to fetch the access token
			code := req.Form(pname)
			if code == "" {
				unauthh.Process(res, req)
				return
			}

			req.Logger.Debugf("Exchange OAuth2 token using code:%q", code)
			token, err := cfg.Use(req).Exchange(contextFactory(res, req), code)
			if err != nil || token == nil {
				req.Logger.Errorf("Token exchange failed: %v", err)
				unauthh.Process(res, req)
				return
			}

			req.Logger.Debugf("Got an oauth2 token: %v", token)
			// token is validated so fetch the user using UserFactory.
			req.Logger.Debugf("OAuth2: code authorization finished. Now retriving an user object.")
			user, err := cfg.UserFactory(res, req, token)
			if err != nil {
				req.Logger.Errorf("We could not fetch the user profilde from the token: %v", err)
				unauthh.Process(res, req)
				return
			}
			user.lastLogin = time.Now()
			user.Token = token
			req.User = user
			req.Logger.Infof("OAuth2 completed successfully (authed via %q)", cfg.Endpoint.AuthURL)

			// store token with session.
			storeOAuth2User(host, user, req)
			authh.Process(res, req)
		})

	validator = wcg.NewNamedHandler("OAuth2.validator",
		func(res *wcg.Response, req *wcg.Request) {
			if user := FindOAuth2UserInSession(req.Session, host); user != nil {
				req.Logger.Debugf("OAuth2 token is found in this session.")
				req.User = user
			} else {
				req.Logger.Debugf("OAuth2 token is not found or expired in this session.")
				req.Logger.Debugf("Session dump: %v", req.Session)
				unvalidh.Process(res, req)
			}
		})

	logoutHandler = wcg.NewNamedHandler("OAuth2.logout",
		func(res *wcg.Response, req *wcg.Request) {
			removeOAuth2User(host, req)
		})

	return redirector, callbackHandler, validator, logoutHandler
}
Exemple #10
0
	return &oauth2.Config{
		ClientID:     r.ClientID,
		ClientSecret: r.ClientSecret,
		Scopes:       r.Scopes,
		Endpoint:     r.Endpoint,
		RedirectURL:  redirect,
	}
}

// DefaultParameterName is a parameter name for oauth code. ("code" by default)
var DefaultParameterName = "code"

// DefaultUnauthorizedHandler is a defualt http handler when unauthorlized.
var DefaultUnauthorizedHandler = wcg.NewNamedHandler("OAuth2.DefaultUnauthorized",
	func(res *wcg.Response, req *wcg.Request) {
		res.WriteHeader(403)
		res.WriteString("Unauthorized.")
		res.End()
	})

// DefaultAuthorizedHandler is a defualt http handler when authorlized.
var DefaultAuthorizedHandler = wcg.NewNamedHandler("OAuth2.DefaultAutorized",
	func(res *wcg.Response, req *wcg.Request) {
		res.WriteString("Authorized.")
		res.End()
	})

// DefaultInvalidatedHandler is a defualt http handler when invalidated.
var DefaultInvalidatedHandler = wcg.NewNamedHandler("OAuth2.DefaultInvalidated",
	func(res *wcg.Response, req *wcg.Request) {
		res.WriteHeader(403)
		res.WriteString("Invalidated.")