added org and open registration

This commit is contained in:
Brad Rydzewski 2016-05-01 17:33:22 -07:00
parent ebd547deac
commit 53eac09f34
5 changed files with 341 additions and 165 deletions

View file

@ -334,16 +334,6 @@ func start(c *cli.Context) error {
)
}
func setupConfig(c *cli.Context) *server.Config {
return &server.Config{
Open: c.Bool("open"),
Yaml: c.String("yaml"),
Secret: c.String("agent-secret"),
Admins: c.StringSlice("admin"),
Orgs: c.StringSlice("orgs"),
}
}
func setupCache(c *cli.Context) cache.Cache {
return cache.NewTTL(
c.Duration("cache-ttl"),
@ -444,6 +434,24 @@ func setupGithub(c *cli.Context) (remote.Remote, error) {
)
}
func setupConfig(c *cli.Context) *server.Config {
return &server.Config{
Open: c.Bool("open"),
Yaml: c.String("yaml"),
Secret: c.String("agent-secret"),
Admins: sliceToMap(c.StringSlice("admin")),
Orgs: sliceToMap(c.StringSlice("orgs")),
}
}
func sliceToMap(s []string) map[string]bool {
v := map[string]bool{}
for _, ss := range s {
v[ss] = true
}
return v
}
func printSecret(c *cli.Context) error {
secret := c.String("agent-secret")
if secret == "" {

View file

@ -77,3 +77,9 @@ func HandlerAgent(v string) gin.HandlerFunc {
c.Set(k, v)
}
}
// ToConfig returns the config from the Context
func ToConfig(c *gin.Context) *Config {
v := c.MustGet("config")
return v.(*Config)
}

161
server/login.go Normal file
View file

@ -0,0 +1,161 @@
package server
import (
"net/http"
"time"
"github.com/drone/drone/model"
"github.com/drone/drone/remote"
"github.com/drone/drone/shared/crypto"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
)
func GetLogin(c *gin.Context) {
// when dealing with redirects we may need to adjust the content type. I
// cannot, however, remember why, so need to revisit this line.
c.Writer.Header().Del("Content-Type")
tmpuser, err := remote.Login(c, c.Writer, c.Request)
if err != nil {
logrus.Errorf("cannot authenticate user. %s", err)
c.Redirect(303, "/login?error=oauth_error")
return
}
// this will happen when the user is redirected by the remote provider as
// part of the authorization workflow.
if tmpuser == nil {
return
}
config := ToConfig(c)
// get the user from the database
u, err := store.GetUserLogin(c, tmpuser.Login)
if err != nil {
// if self-registration is disabled we should return a not authorized error
if !config.Open {
logrus.Errorf("cannot register %s. registration closed", tmpuser.Login)
c.Redirect(303, "/login?error=access_denied")
return
}
// create the user account
u = &model.User{
Login: tmpuser.Login,
Token: tmpuser.Token,
Secret: tmpuser.Secret,
Email: tmpuser.Email,
Avatar: tmpuser.Avatar,
Hash: crypto.Rand(),
}
// insert the user into the database
if err := store.CreateUser(c, u); err != nil {
logrus.Errorf("cannot insert %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
}
// update the user meta data and authorization data.
u.Token = tmpuser.Token
u.Secret = tmpuser.Secret
u.Email = tmpuser.Email
u.Avatar = tmpuser.Avatar
if err := store.UpdateUser(c, u); err != nil {
logrus.Errorf("cannot update %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
if len(config.Orgs) != 0 {
teams, terr := remote.Teams(c, u)
if terr != nil {
logrus.Errorf("cannot verify team membership for %s. %s.", tmpuser.Login, terr)
c.Redirect(303, "/login?error=access_denied")
return
}
var member bool
for _, team := range teams {
if config.Orgs[team.Login] {
member = true
break
}
}
if !member {
logrus.Errorf("cannot verify team membership for %s. %s.", tmpuser.Login, terr)
c.Redirect(303, "/login?error=access_denied")
return
}
}
exp := time.Now().Add(time.Hour * 72).Unix()
token := token.New(token.SessToken, u.Login)
tokenstr, err := token.SignExpires(u.Hash, exp)
if err != nil {
logrus.Errorf("cannot create token for %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr)
redirect := httputil.GetCookie(c.Request, "user_last")
if len(redirect) == 0 {
redirect = "/"
}
c.Redirect(303, redirect)
}
func GetLogout(c *gin.Context) {
httputil.DelCookie(c.Writer, c.Request, "user_sess")
httputil.DelCookie(c.Writer, c.Request, "user_last")
c.Redirect(303, "/login")
}
func GetLoginToken(c *gin.Context) {
in := &tokenPayload{}
err := c.Bind(in)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
login, err := remote.Auth(c, in.Access, in.Refresh)
if err != nil {
c.AbortWithError(http.StatusUnauthorized, err)
return
}
user, err := store.GetUserLogin(c, login)
if err != nil {
c.AbortWithError(http.StatusNotFound, err)
return
}
exp := time.Now().Add(time.Hour * 72).Unix()
token := token.New(token.SessToken, user.Login)
tokenstr, err := token.SignExpires(user.Hash, exp)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.IndentedJSON(http.StatusOK, &tokenPayload{
Access: tokenstr,
Expires: exp - time.Now().Unix(),
})
}
type tokenPayload struct {
Access string `json:"access_token,omitempty"`
Refresh string `json:"refresh_token,omitempty"`
Expires int64 `json:"expires_in,omitempty"`
}

View file

@ -27,11 +27,11 @@ import (
// Config defines system configuration parameters.
type Config struct {
Open bool // Enables open registration
Yaml string // Customize the Yaml configuration file name
Secret string // Secret token used to authenticate agents
Admins []string // Administrative users
Orgs []string // Organization whitelist
Open bool // Enables open registration
Yaml string // Customize the Yaml configuration file name
Secret string // Secret token used to authenticate agents
Admins map[string]bool // Administrative users
Orgs map[string]bool // Organization whitelist
}
// Server defines the server configuration.
@ -75,7 +75,7 @@ func (s *Server) Handler() http.Handler {
e.GET("/repos", web.ShowAllRepos)
e.GET("/login", web.ShowLogin)
e.GET("/login/form", web.ShowLoginForm)
e.GET("/logout", web.GetLogout)
e.GET("/logout", GetLogout)
// TODO below will Go away with React UI
settings := e.Group("/settings")
@ -172,9 +172,9 @@ func (s *Server) Handler() http.Handler {
auth := e.Group("/authorize")
{
auth.GET("", web.GetLogin)
auth.POST("", web.GetLogin)
auth.POST("/token", web.GetLoginToken)
auth.GET("", GetLogin)
auth.POST("", GetLogin)
auth.POST("/token", GetLoginToken)
}
queue := e.Group("/api/queue")

View file

@ -1,148 +1,149 @@
package web
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
log "github.com/Sirupsen/logrus"
"github.com/drone/drone/model"
"github.com/drone/drone/remote"
"github.com/drone/drone/shared/crypto"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
)
func GetLogin(c *gin.Context) {
remote := remote.FromContext(c)
// when dealing with redirects we may need
// to adjust the content type. I cannot, however,
// remember why, so need to revisit this line.
c.Writer.Header().Del("Content-Type")
tmpuser, err := remote.Login(c.Writer, c.Request)
if err != nil {
log.Errorf("cannot authenticate user. %s", err)
c.Redirect(303, "/login?error=oauth_error")
return
}
// this will happen when the user is redirected by
// the remote provide as part of the oauth dance.
if tmpuser == nil {
return
}
var open = false // TODO get this from context
// get the user from the database
u, err := store.GetUserLogin(c, tmpuser.Login)
if err != nil {
// if self-registration is disabled we should
// return a notAuthorized error. the only exception
// is if no users exist yet in the system we'll proceed.
if !open {
log.Errorf("cannot register %s. registration closed", tmpuser.Login)
c.Redirect(303, "/login?error=access_denied")
return
}
// create the user account
u = &model.User{}
u.Login = tmpuser.Login
u.Token = tmpuser.Token
u.Secret = tmpuser.Secret
u.Email = tmpuser.Email
u.Avatar = tmpuser.Avatar
u.Hash = crypto.Rand()
// insert the user into the database
if err := store.CreateUser(c, u); err != nil {
log.Errorf("cannot insert %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
}
// update the user meta data and authorization
// data and cache in the datastore.
u.Token = tmpuser.Token
u.Secret = tmpuser.Secret
u.Email = tmpuser.Email
u.Avatar = tmpuser.Avatar
if err := store.UpdateUser(c, u); err != nil {
log.Errorf("cannot update %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
exp := time.Now().Add(time.Hour * 72).Unix()
token := token.New(token.SessToken, u.Login)
tokenstr, err := token.SignExpires(u.Hash, exp)
if err != nil {
log.Errorf("cannot create token for %s. %s", u.Login, err)
c.Redirect(303, "/login?error=internal_error")
return
}
httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr)
redirect := httputil.GetCookie(c.Request, "user_last")
if len(redirect) == 0 {
redirect = "/"
}
c.Redirect(303, redirect)
}
func GetLogout(c *gin.Context) {
httputil.DelCookie(c.Writer, c.Request, "user_sess")
httputil.DelCookie(c.Writer, c.Request, "user_last")
c.Redirect(303, "/login")
}
func GetLoginToken(c *gin.Context) {
remote := remote.FromContext(c)
in := &tokenPayload{}
err := c.Bind(in)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
login, err := remote.Auth(in.Access, in.Refresh)
if err != nil {
c.AbortWithError(http.StatusUnauthorized, err)
return
}
user, err := store.GetUserLogin(c, login)
if err != nil {
c.AbortWithError(http.StatusNotFound, err)
return
}
exp := time.Now().Add(time.Hour * 72).Unix()
token := token.New(token.SessToken, user.Login)
tokenstr, err := token.SignExpires(user.Hash, exp)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.IndentedJSON(http.StatusOK, &tokenPayload{
Access: tokenstr,
Expires: exp - time.Now().Unix(),
})
}
type tokenPayload struct {
Access string `json:"access_token,omitempty"`
Refresh string `json:"refresh_token,omitempty"`
Expires int64 `json:"expires_in,omitempty"`
}
//
// import (
// "net/http"
// "time"
//
// "github.com/drone/drone/model"
// "github.com/drone/drone/remote"
// "github.com/drone/drone/shared/crypto"
// "github.com/drone/drone/shared/httputil"
// "github.com/drone/drone/shared/token"
// "github.com/drone/drone/store"
//
// "github.com/Sirupsen/logrus"
// "github.com/gin-gonic/gin"
// )
//
// func GetLogin(c *gin.Context) {
// remote := remote.FromContext(c)
//
// // when dealing with redirects we may need
// // to adjust the content type. I cannot, however,
// // remember why, so need to revisit this line.
// c.Writer.Header().Del("Content-Type")
//
// tmpuser, err := remote.Login(c.Writer, c.Request)
// if err != nil {
// logrus.Errorf("cannot authenticate user. %s", err)
// c.Redirect(303, "/login?error=oauth_error")
// return
// }
// // this will happen when the user is redirected by
// // the remote provide as part of the oauth dance.
// if tmpuser == nil {
// return
// }
//
// var open = false // TODO get this from context
//
// // get the user from the database
// u, err := store.GetUserLogin(c, tmpuser.Login)
// if err != nil {
//
// // if self-registration is disabled we should
// // return a notAuthorized error. the only exception
// // is if no users exist yet in the system we'll proceed.
// if !open {
// logrus.Errorf("cannot register %s. registration closed", tmpuser.Login)
// c.Redirect(303, "/login?error=access_denied")
// return
// }
//
// // create the user account
// u = &model.User{}
// u.Login = tmpuser.Login
// u.Token = tmpuser.Token
// u.Secret = tmpuser.Secret
// u.Email = tmpuser.Email
// u.Avatar = tmpuser.Avatar
// u.Hash = crypto.Rand()
//
// // insert the user into the database
// if err := store.CreateUser(c, u); err != nil {
// logrus.Errorf("cannot insert %s. %s", u.Login, err)
// c.Redirect(303, "/login?error=internal_error")
// return
// }
// }
//
// // update the user meta data and authorization
// // data and cache in the datastore.
// u.Token = tmpuser.Token
// u.Secret = tmpuser.Secret
// u.Email = tmpuser.Email
// u.Avatar = tmpuser.Avatar
//
// if err := store.UpdateUser(c, u); err != nil {
// logrus.Errorf("cannot update %s. %s", u.Login, err)
// c.Redirect(303, "/login?error=internal_error")
// return
// }
//
// exp := time.Now().Add(time.Hour * 72).Unix()
// token := token.New(token.SessToken, u.Login)
// tokenstr, err := token.SignExpires(u.Hash, exp)
// if err != nil {
// logrus.Errorf("cannot create token for %s. %s", u.Login, err)
// c.Redirect(303, "/login?error=internal_error")
// return
// }
//
// httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr)
// redirect := httputil.GetCookie(c.Request, "user_last")
// if len(redirect) == 0 {
// redirect = "/"
// }
// c.Redirect(303, redirect)
//
// }
//
// func GetLogout(c *gin.Context) {
//
// httputil.DelCookie(c.Writer, c.Request, "user_sess")
// httputil.DelCookie(c.Writer, c.Request, "user_last")
// c.Redirect(303, "/login")
// }
//
// func GetLoginToken(c *gin.Context) {
// remote := remote.FromContext(c)
//
// in := &tokenPayload{}
// err := c.Bind(in)
// if err != nil {
// c.AbortWithError(http.StatusBadRequest, err)
// return
// }
//
// login, err := remote.Auth(in.Access, in.Refresh)
// if err != nil {
// c.AbortWithError(http.StatusUnauthorized, err)
// return
// }
//
// user, err := store.GetUserLogin(c, login)
// if err != nil {
// c.AbortWithError(http.StatusNotFound, err)
// return
// }
//
// exp := time.Now().Add(time.Hour * 72).Unix()
// token := token.New(token.SessToken, user.Login)
// tokenstr, err := token.SignExpires(user.Hash, exp)
// if err != nil {
// c.AbortWithError(http.StatusInternalServerError, err)
// return
// }
//
// c.IndentedJSON(http.StatusOK, &tokenPayload{
// Access: tokenstr,
// Expires: exp - time.Now().Unix(),
// })
// }
//
// type tokenPayload struct {
// Access string `json:"access_token,omitempty"`
// Refresh string `json:"refresh_token,omitempty"`
// Expires int64 `json:"expires_in,omitempty"`
// }