Skip to content

Commit

Permalink
Stub openid support
Browse files Browse the repository at this point in the history
Handle OpenID redirection
Implement the verfication routes

... still all to be architectured
  • Loading branch information
strk committed Dec 24, 2016
1 parent fa3abc2 commit cb2ce01
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 6 deletions.
1 change: 1 addition & 0 deletions cmd/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ func runWeb(ctx *cli.Context) error {
m.Group("/user", func() {
m.Get("/login", user.SignIn)
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
m.Get("/login/openid/verify", user.OpenIDVerify)
m.Get("/sign_up", user.SignUp)
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
m.Get("/reset_password", user.ResetPasswd)
Expand Down
16 changes: 16 additions & 0 deletions models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ func (err ErrUserNotExist) Error() string {
return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID)
}

// ErrDelegatedAuth is not a real error but notifies code that
// authentication is delegated to a second request.
type ErrDelegatedAuth struct {
OP string // OpenID Provider
}

// IsErrDelegatedAuth checks if an error is a ErrDelegatedAuth.
func IsErrDelegatedAuth(err error) bool {
_, ok := err.(ErrDelegatedAuth)
return ok
}

func (err ErrDelegatedAuth) Error() string {
return err.OP
}

// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error.
type ErrEmailAlreadyUsed struct {
Email string
Expand Down
88 changes: 83 additions & 5 deletions models/login_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ import (
"github.com/go-macaron/binding"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
"github.com/yohcop/openid-go"
//"github.com/akavel/go-openid"

"code.gitea.io/gitea/modules/auth/ldap"
"code.gitea.io/gitea/modules/auth/pam"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)

// LoginType represents an login type.
Expand All @@ -35,14 +38,16 @@ const (
LoginSMTP // 3
LoginPAM // 4
LoginDLDAP // 5
LoginOpenID // 6
)

// LoginNames contains the name of LoginType values.
var LoginNames = map[LoginType]string{
LoginLDAP: "LDAP (via BindDN)",
LoginDLDAP: "LDAP (simple auth)", // Via direct bind
LoginSMTP: "SMTP",
LoginPAM: "PAM",
LoginLDAP: "LDAP (via BindDN)",
LoginDLDAP: "LDAP (simple auth)", // Via direct bind
LoginSMTP: "SMTP",
LoginPAM: "PAM",
LoginOpenID: "OpenID",
}

// SecurityProtocolNames contains the name of SecurityProtocol values.
Expand All @@ -57,6 +62,7 @@ var (
_ core.Conversion = &LDAPConfig{}
_ core.Conversion = &SMTPConfig{}
_ core.Conversion = &PAMConfig{}
_ core.Conversion = &OpenIDConfig{}
)

// LDAPConfig holds configuration for LDAP login source.
Expand All @@ -80,6 +86,21 @@ func (cfg *LDAPConfig) SecurityProtocolName() string {
return SecurityProtocolNames[cfg.SecurityProtocol]
}

// OpenIDConfig holds an OpenID login source configuration.
type OpenIDConfig struct {
//*openid.Source
}

// FromDB fills up an OpenIDConfig from serialized format.
func (cfg *OpenIDConfig) FromDB(bs []byte) error {
return json.Unmarshal(bs, &cfg)
}

// ToDB exports an OpenIDConfig to a serialized format.
func (cfg *OpenIDConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}

// SMTPConfig holds configuration for the SMTP login source.
type SMTPConfig struct {
Auth string
Expand Down Expand Up @@ -162,6 +183,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
source.Cfg = new(SMTPConfig)
case LoginPAM:
source.Cfg = new(PAMConfig)
case LoginOpenID:
source.Cfg = new(OpenIDConfig)
default:
panic("unrecognized login source type: " + com.ToStr(*val))
}
Expand Down Expand Up @@ -526,6 +549,54 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
return user, CreateUser(user)
}

// ________ .___________
// \_____ \ ______ ____ ____ | \______ \
// / | \\____ \_/ __ \ / \| || | \
// / | \ |_> > ___/| | \ || ` \
// \_______ / __/ \___ >___| /___/_______ /
// \/|__| \/ \/ \/

// LoginViaOpenID authorizes against "id" (openid URL)
// and create a local user if success when enabled.
func LoginViaOpenID(user *User, id string, source *LoginSource, autoRegister bool) (*User, error) {

url, err := openid.RedirectURL(id, setting.AppURL+"user/login/openid/verify", setting.AppURL)
if err != nil {
return nil, err
}
return nil, ErrDelegatedAuth{OP: url}
}

var nonceStore = openid.NewSimpleNonceStore()
var discoveryCache = openid.NewSimpleDiscoveryCache()

// LoginViaOpenIDVerification verifies a given OpenID url.
func LoginViaOpenIDVerification(url string, autoRegister bool) (*User, error) {

var id, err = openid.Verify(url, discoveryCache, nonceStore)
if err != nil {
log.Fatal(1, "Error verifying: %v", err)
}
log.Trace("Verified ID: " + id)

/*
login := id
user = &User{
LowerName: strings.ToLower(login),
Name: login,
Email: login,
Passwd: nil,
LoginType: LoginOpenID,
LoginSource: sourceID,
LoginName: login,
IsActive: true,
}
return user, CreateUser(user)
*/
return nil, nil
}

// ExternalUserLogin attempts a login using external source types.
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
if !source.IsActived {
Expand All @@ -539,6 +610,8 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
case LoginPAM:
return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
case LoginOpenID:
return LoginViaOpenID(user, login, source, autoRegister)
}

return nil, ErrUnsupportedLoginType
Expand All @@ -549,6 +622,8 @@ func UserSignIn(username, password string) (*User, error) {
var user *User
if strings.Contains(username, "@") {
user = &User{Email: strings.ToLower(strings.TrimSpace(username))}
} else if strings.Contains(username, "://") {
user = &User{OpenID: strings.ToLower(username)}
} else {
user = &User{LowerName: strings.ToLower(strings.TrimSpace(username))}
}
Expand Down Expand Up @@ -580,7 +655,7 @@ func UserSignIn(username, password string) (*User, error) {
}
}

sources := make([]*LoginSource, 0, 3)
sources := make([]*LoginSource, 0, 4)
if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
return nil, err
}
Expand All @@ -590,6 +665,9 @@ func UserSignIn(username, password string) (*User, error) {
if err == nil {
return authUser, nil
}
if IsErrDelegatedAuth(err) {
return nil, err
}

log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
}
Expand Down
1 change: 1 addition & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ type User struct {
Repos []*Repository `xorm:"-"`
Location string
Website string
OpenID string
Rands string `xorm:"VARCHAR(10)"`
Salt string `xorm:"VARCHAR(10)"`

Expand Down
2 changes: 1 addition & 1 deletion modules/auth/auth_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
// AuthenticationForm form for authentication
type AuthenticationForm struct {
ID int64
Type int `binding:"Range(2,5)"`
Type int `binding:"Range(2,6)"`
Name string `binding:"Required;MaxSize(30)"`
Host string
Port int
Expand Down
48 changes: 48 additions & 0 deletions modules/auth/openid/openid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package openid provide functions & structure to authenticate users
// via OpenID
package openid

import (
"github.com/yohcop/openid-go"
)

// For the demo, we use in-memory infinite storage nonce and discovery
// cache. In your app, do not use this as it will eat up memory and
// never
// free it. Use your own implementation, on a better database system.
// If you have multiple servers for example, you may need to share at
// least
// the nonceStore between them.
var nonceStore = openid.NewSimpleNonceStore()
var discoveryCache = openid.NewSimpleDiscoveryCache()

// LoginViaOpenIDVerification verifies an OpenID URL claim
func LoginViaOpenIDVerification(url string, autoRegister bool) (*User, error) {

var id, err = openid.Verify(url, discoveryCache, nonceStore)
if err != nil {
log.Fatal(1, "Error verifying: %v", err)
}
log.Trace("Verified ID: " + id)

/*
login := id
user = &User{
LowerName: strings.ToLower(login),
Name: login,
Email: login,
Passwd: nil,
LoginType: LoginOpenID,
LoginSource: sourceID,
LoginName: login,
IsActive: true,
}
return user, CreateUser(user)
*/
return nil, nil
}
5 changes: 5 additions & 0 deletions routers/admin/auths.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var (
{models.LoginNames[models.LoginDLDAP], models.LoginDLDAP},
{models.LoginNames[models.LoginSMTP], models.LoginSMTP},
{models.LoginNames[models.LoginPAM], models.LoginPAM},
{models.LoginNames[models.LoginOpenID], models.LoginOpenID},
}
securityProtocols = []dropdownItem{
{models.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted], ldap.SecurityProtocolUnencrypted},
Expand Down Expand Up @@ -138,6 +139,10 @@ func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
config = &models.PAMConfig{
ServiceName: form.PAMServiceName,
}
case models.LoginOpenID:
config = &models.OpenIDConfig{
//ServiceName: form.PAMServiceName,
}
default:
ctx.Error(400)
return
Expand Down
15 changes: 15 additions & 0 deletions routers/user/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ func SignIn(ctx *context.Context) {
ctx.HTML(200, tplSignIn)
}

// OpenIDVerify handles response from OpenID provider
func OpenIDVerify(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("sign_in")

log.Trace("Incoming call to: " + ctx.Req.Request.URL.String())

fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:]
log.Trace(fullURL)
models.LoginViaOpenIDVerification(fullURL, true)

ctx.HTML(200, tplSignIn)
}

// SignInPost response for sign in request
func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["Title"] = ctx.Tr("sign_in")
Expand All @@ -113,6 +126,8 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
} else if models.IsErrDelegatedAuth(err) {
ctx.Redirect(err.Error())
} else {
ctx.Handle(500, "UserSignIn", err)
}
Expand Down
4 changes: 4 additions & 0 deletions templates/admin/auth/new.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@
<input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}" />
</div>

<!-- OpenID -->
<div class="openid required field {{if not (eq .type 6)}}hide{{end}}">
</div>

<div class="ldap field">
<div class="ui checkbox">
<label><strong>{{.i18n.Tr "admin.auths.attributes_in_bind"}}</strong></label>
Expand Down

0 comments on commit cb2ce01

Please sign in to comment.