Skip to content

Commit cb2ce01

Browse files
committed
Stub openid support
Handle OpenID redirection Implement the verfication routes ... still all to be architectured
1 parent fa3abc2 commit cb2ce01

File tree

9 files changed

+174
-6
lines changed

9 files changed

+174
-6
lines changed

cmd/web.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ func runWeb(ctx *cli.Context) error {
186186
m.Group("/user", func() {
187187
m.Get("/login", user.SignIn)
188188
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
189+
m.Get("/login/openid/verify", user.OpenIDVerify)
189190
m.Get("/sign_up", user.SignUp)
190191
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
191192
m.Get("/reset_password", user.ResetPasswd)

models/error.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ func (err ErrUserNotExist) Error() string {
7878
return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID)
7979
}
8080

81+
// ErrDelegatedAuth is not a real error but notifies code that
82+
// authentication is delegated to a second request.
83+
type ErrDelegatedAuth struct {
84+
OP string // OpenID Provider
85+
}
86+
87+
// IsErrDelegatedAuth checks if an error is a ErrDelegatedAuth.
88+
func IsErrDelegatedAuth(err error) bool {
89+
_, ok := err.(ErrDelegatedAuth)
90+
return ok
91+
}
92+
93+
func (err ErrDelegatedAuth) Error() string {
94+
return err.OP
95+
}
96+
8197
// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error.
8298
type ErrEmailAlreadyUsed struct {
8399
Email string

models/login_source.go

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ import (
1818
"github.com/go-macaron/binding"
1919
"github.com/go-xorm/core"
2020
"github.com/go-xorm/xorm"
21+
"github.com/yohcop/openid-go"
22+
//"github.com/akavel/go-openid"
2123

2224
"code.gitea.io/gitea/modules/auth/ldap"
2325
"code.gitea.io/gitea/modules/auth/pam"
2426
"code.gitea.io/gitea/modules/log"
27+
"code.gitea.io/gitea/modules/setting"
2528
)
2629

2730
// LoginType represents an login type.
@@ -35,14 +38,16 @@ const (
3538
LoginSMTP // 3
3639
LoginPAM // 4
3740
LoginDLDAP // 5
41+
LoginOpenID // 6
3842
)
3943

4044
// LoginNames contains the name of LoginType values.
4145
var LoginNames = map[LoginType]string{
42-
LoginLDAP: "LDAP (via BindDN)",
43-
LoginDLDAP: "LDAP (simple auth)", // Via direct bind
44-
LoginSMTP: "SMTP",
45-
LoginPAM: "PAM",
46+
LoginLDAP: "LDAP (via BindDN)",
47+
LoginDLDAP: "LDAP (simple auth)", // Via direct bind
48+
LoginSMTP: "SMTP",
49+
LoginPAM: "PAM",
50+
LoginOpenID: "OpenID",
4651
}
4752

4853
// SecurityProtocolNames contains the name of SecurityProtocol values.
@@ -57,6 +62,7 @@ var (
5762
_ core.Conversion = &LDAPConfig{}
5863
_ core.Conversion = &SMTPConfig{}
5964
_ core.Conversion = &PAMConfig{}
65+
_ core.Conversion = &OpenIDConfig{}
6066
)
6167

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

89+
// OpenIDConfig holds an OpenID login source configuration.
90+
type OpenIDConfig struct {
91+
//*openid.Source
92+
}
93+
94+
// FromDB fills up an OpenIDConfig from serialized format.
95+
func (cfg *OpenIDConfig) FromDB(bs []byte) error {
96+
return json.Unmarshal(bs, &cfg)
97+
}
98+
99+
// ToDB exports an OpenIDConfig to a serialized format.
100+
func (cfg *OpenIDConfig) ToDB() ([]byte, error) {
101+
return json.Marshal(cfg)
102+
}
103+
83104
// SMTPConfig holds configuration for the SMTP login source.
84105
type SMTPConfig struct {
85106
Auth string
@@ -162,6 +183,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
162183
source.Cfg = new(SMTPConfig)
163184
case LoginPAM:
164185
source.Cfg = new(PAMConfig)
186+
case LoginOpenID:
187+
source.Cfg = new(OpenIDConfig)
165188
default:
166189
panic("unrecognized login source type: " + com.ToStr(*val))
167190
}
@@ -526,6 +549,54 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
526549
return user, CreateUser(user)
527550
}
528551

552+
// ________ .___________
553+
// \_____ \ ______ ____ ____ | \______ \
554+
// / | \\____ \_/ __ \ / \| || | \
555+
// / | \ |_> > ___/| | \ || ` \
556+
// \_______ / __/ \___ >___| /___/_______ /
557+
// \/|__| \/ \/ \/
558+
559+
// LoginViaOpenID authorizes against "id" (openid URL)
560+
// and create a local user if success when enabled.
561+
func LoginViaOpenID(user *User, id string, source *LoginSource, autoRegister bool) (*User, error) {
562+
563+
url, err := openid.RedirectURL(id, setting.AppURL+"user/login/openid/verify", setting.AppURL)
564+
if err != nil {
565+
return nil, err
566+
}
567+
return nil, ErrDelegatedAuth{OP: url}
568+
}
569+
570+
var nonceStore = openid.NewSimpleNonceStore()
571+
var discoveryCache = openid.NewSimpleDiscoveryCache()
572+
573+
// LoginViaOpenIDVerification verifies a given OpenID url.
574+
func LoginViaOpenIDVerification(url string, autoRegister bool) (*User, error) {
575+
576+
var id, err = openid.Verify(url, discoveryCache, nonceStore)
577+
if err != nil {
578+
log.Fatal(1, "Error verifying: %v", err)
579+
}
580+
log.Trace("Verified ID: " + id)
581+
582+
/*
583+
login := id
584+
585+
user = &User{
586+
LowerName: strings.ToLower(login),
587+
Name: login,
588+
Email: login,
589+
Passwd: nil,
590+
LoginType: LoginOpenID,
591+
LoginSource: sourceID,
592+
LoginName: login,
593+
IsActive: true,
594+
}
595+
return user, CreateUser(user)
596+
*/
597+
return nil, nil
598+
}
599+
529600
// ExternalUserLogin attempts a login using external source types.
530601
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
531602
if !source.IsActived {
@@ -539,6 +610,8 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
539610
return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
540611
case LoginPAM:
541612
return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
613+
case LoginOpenID:
614+
return LoginViaOpenID(user, login, source, autoRegister)
542615
}
543616

544617
return nil, ErrUnsupportedLoginType
@@ -549,6 +622,8 @@ func UserSignIn(username, password string) (*User, error) {
549622
var user *User
550623
if strings.Contains(username, "@") {
551624
user = &User{Email: strings.ToLower(strings.TrimSpace(username))}
625+
} else if strings.Contains(username, "://") {
626+
user = &User{OpenID: strings.ToLower(username)}
552627
} else {
553628
user = &User{LowerName: strings.ToLower(strings.TrimSpace(username))}
554629
}
@@ -580,7 +655,7 @@ func UserSignIn(username, password string) (*User, error) {
580655
}
581656
}
582657

583-
sources := make([]*LoginSource, 0, 3)
658+
sources := make([]*LoginSource, 0, 4)
584659
if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
585660
return nil, err
586661
}
@@ -590,6 +665,9 @@ func UserSignIn(username, password string) (*User, error) {
590665
if err == nil {
591666
return authUser, nil
592667
}
668+
if IsErrDelegatedAuth(err) {
669+
return nil, err
670+
}
593671

594672
log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
595673
}

models/user.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type User struct {
8686
Repos []*Repository `xorm:"-"`
8787
Location string
8888
Website string
89+
OpenID string
8990
Rands string `xorm:"VARCHAR(10)"`
9091
Salt string `xorm:"VARCHAR(10)"`
9192

modules/auth/auth_form.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
// AuthenticationForm form for authentication
1313
type AuthenticationForm struct {
1414
ID int64
15-
Type int `binding:"Range(2,5)"`
15+
Type int `binding:"Range(2,6)"`
1616
Name string `binding:"Required;MaxSize(30)"`
1717
Host string
1818
Port int

modules/auth/openid/openid.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2016 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package openid provide functions & structure to authenticate users
6+
// via OpenID
7+
package openid
8+
9+
import (
10+
"github.com/yohcop/openid-go"
11+
)
12+
13+
// For the demo, we use in-memory infinite storage nonce and discovery
14+
// cache. In your app, do not use this as it will eat up memory and
15+
// never
16+
// free it. Use your own implementation, on a better database system.
17+
// If you have multiple servers for example, you may need to share at
18+
// least
19+
// the nonceStore between them.
20+
var nonceStore = openid.NewSimpleNonceStore()
21+
var discoveryCache = openid.NewSimpleDiscoveryCache()
22+
23+
// LoginViaOpenIDVerification verifies an OpenID URL claim
24+
func LoginViaOpenIDVerification(url string, autoRegister bool) (*User, error) {
25+
26+
var id, err = openid.Verify(url, discoveryCache, nonceStore)
27+
if err != nil {
28+
log.Fatal(1, "Error verifying: %v", err)
29+
}
30+
log.Trace("Verified ID: " + id)
31+
32+
/*
33+
login := id
34+
35+
user = &User{
36+
LowerName: strings.ToLower(login),
37+
Name: login,
38+
Email: login,
39+
Passwd: nil,
40+
LoginType: LoginOpenID,
41+
LoginSource: sourceID,
42+
LoginName: login,
43+
IsActive: true,
44+
}
45+
return user, CreateUser(user)
46+
*/
47+
return nil, nil
48+
}

routers/admin/auths.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var (
5353
{models.LoginNames[models.LoginDLDAP], models.LoginDLDAP},
5454
{models.LoginNames[models.LoginSMTP], models.LoginSMTP},
5555
{models.LoginNames[models.LoginPAM], models.LoginPAM},
56+
{models.LoginNames[models.LoginOpenID], models.LoginOpenID},
5657
}
5758
securityProtocols = []dropdownItem{
5859
{models.SecurityProtocolNames[ldap.SecurityProtocolUnencrypted], ldap.SecurityProtocolUnencrypted},
@@ -138,6 +139,10 @@ func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
138139
config = &models.PAMConfig{
139140
ServiceName: form.PAMServiceName,
140141
}
142+
case models.LoginOpenID:
143+
config = &models.OpenIDConfig{
144+
//ServiceName: form.PAMServiceName,
145+
}
141146
default:
142147
ctx.Error(400)
143148
return

routers/user/auth.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ func SignIn(ctx *context.Context) {
100100
ctx.HTML(200, tplSignIn)
101101
}
102102

103+
// OpenIDVerify handles response from OpenID provider
104+
func OpenIDVerify(ctx *context.Context) {
105+
ctx.Data["Title"] = ctx.Tr("sign_in")
106+
107+
log.Trace("Incoming call to: " + ctx.Req.Request.URL.String())
108+
109+
fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:]
110+
log.Trace(fullURL)
111+
models.LoginViaOpenIDVerification(fullURL, true)
112+
113+
ctx.HTML(200, tplSignIn)
114+
}
115+
103116
// SignInPost response for sign in request
104117
func SignInPost(ctx *context.Context, form auth.SignInForm) {
105118
ctx.Data["Title"] = ctx.Tr("sign_in")
@@ -113,6 +126,8 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
113126
if err != nil {
114127
if models.IsErrUserNotExist(err) {
115128
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
129+
} else if models.IsErrDelegatedAuth(err) {
130+
ctx.Redirect(err.Error())
116131
} else {
117132
ctx.Handle(500, "UserSignIn", err)
118133
}

templates/admin/auth/new.tmpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@
133133
<input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}" />
134134
</div>
135135

136+
<!-- OpenID -->
137+
<div class="openid required field {{if not (eq .type 6)}}hide{{end}}">
138+
</div>
139+
136140
<div class="ldap field">
137141
<div class="ui checkbox">
138142
<label><strong>{{.i18n.Tr "admin.auths.attributes_in_bind"}}</strong></label>

0 commit comments

Comments
 (0)