added org and open registration
This commit is contained in:
parent
ebd547deac
commit
53eac09f34
5 changed files with 341 additions and 165 deletions
|
@ -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 == "" {
|
||||
|
|
|
@ -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
161
server/login.go
Normal 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"`
|
||||
}
|
|
@ -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")
|
||||
|
|
293
web/login.go
293
web/login.go
|
@ -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"`
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue