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 { func setupCache(c *cli.Context) cache.Cache {
return cache.NewTTL( return cache.NewTTL(
c.Duration("cache-ttl"), 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 { func printSecret(c *cli.Context) error {
secret := c.String("agent-secret") secret := c.String("agent-secret")
if secret == "" { if secret == "" {

View file

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

View file

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