mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Login via OpenID-2.0 (#618)
This commit is contained in:
		
				
					committed by
					
						
						Kim "BKC" Carlbäcker
					
				
			
			
				
	
			
			
			
						parent
						
							0693fbfc00
						
					
				
				
					commit
					71d16f69ff
				
			
							
								
								
									
										21
									
								
								cmd/web.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								cmd/web.go
									
									
									
									
									
								
							@@ -200,6 +200,19 @@ func runWeb(ctx *cli.Context) error {
 | 
				
			|||||||
	m.Group("/user", func() {
 | 
						m.Group("/user", func() {
 | 
				
			||||||
		m.Get("/login", user.SignIn)
 | 
							m.Get("/login", user.SignIn)
 | 
				
			||||||
		m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
 | 
							m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
 | 
				
			||||||
 | 
							if setting.EnableOpenIDSignIn {
 | 
				
			||||||
 | 
								m.Combo("/login/openid").
 | 
				
			||||||
 | 
									Get(user.SignInOpenID).
 | 
				
			||||||
 | 
									Post(bindIgnErr(auth.SignInOpenIDForm{}), user.SignInOpenIDPost)
 | 
				
			||||||
 | 
								m.Group("/openid", func() {
 | 
				
			||||||
 | 
									m.Combo("/connect").
 | 
				
			||||||
 | 
										Get(user.ConnectOpenID).
 | 
				
			||||||
 | 
										Post(bindIgnErr(auth.ConnectOpenIDForm{}), user.ConnectOpenIDPost)
 | 
				
			||||||
 | 
									m.Combo("/register").
 | 
				
			||||||
 | 
										Get(user.RegisterOpenID).
 | 
				
			||||||
 | 
										Post(bindIgnErr(auth.SignUpOpenIDForm{}), user.RegisterOpenIDPost)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		m.Get("/sign_up", user.SignUp)
 | 
							m.Get("/sign_up", user.SignUp)
 | 
				
			||||||
		m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
 | 
							m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
 | 
				
			||||||
		m.Get("/reset_password", user.ResetPasswd)
 | 
							m.Get("/reset_password", user.ResetPasswd)
 | 
				
			||||||
@@ -230,6 +243,14 @@ func runWeb(ctx *cli.Context) error {
 | 
				
			|||||||
		m.Post("/email/delete", user.DeleteEmail)
 | 
							m.Post("/email/delete", user.DeleteEmail)
 | 
				
			||||||
		m.Get("/password", user.SettingsPassword)
 | 
							m.Get("/password", user.SettingsPassword)
 | 
				
			||||||
		m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
 | 
							m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
 | 
				
			||||||
 | 
							if setting.EnableOpenIDSignIn {
 | 
				
			||||||
 | 
								m.Group("/openid", func() {
 | 
				
			||||||
 | 
									m.Combo("").Get(user.SettingsOpenID).
 | 
				
			||||||
 | 
										Post(bindIgnErr(auth.AddOpenIDForm{}), user.SettingsOpenIDPost)
 | 
				
			||||||
 | 
									m.Post("/delete", user.DeleteOpenID)
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Combo("/ssh").Get(user.SettingsSSHKeys).
 | 
							m.Combo("/ssh").Get(user.SettingsSSHKeys).
 | 
				
			||||||
			Post(bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
 | 
								Post(bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
 | 
				
			||||||
		m.Post("/ssh/delete", user.DeleteSSHKey)
 | 
							m.Post("/ssh/delete", user.DeleteSSHKey)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								conf/app.ini
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								conf/app.ini
									
									
									
									
										vendored
									
									
								
							@@ -182,6 +182,38 @@ MIN_PASSWORD_LENGTH = 6
 | 
				
			|||||||
; True when users are allowed to import local server paths
 | 
					; True when users are allowed to import local server paths
 | 
				
			||||||
IMPORT_LOCAL_PATHS = false
 | 
					IMPORT_LOCAL_PATHS = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[openid]
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					; OpenID is an open standard and decentralized authentication protocol.
 | 
				
			||||||
 | 
					; Your identity is the address of a webpage you provide, which describes
 | 
				
			||||||
 | 
					; how to prove you are in control of that page.
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					; For more info: https://en.wikipedia.org/wiki/OpenID
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					; Current implementation supports OpenID-2.0
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					; Tested to work providers at the time of writing:
 | 
				
			||||||
 | 
					;  - Any GNUSocial node (your.hostname.tld/username)
 | 
				
			||||||
 | 
					;  - Any SimpleID provider (http://simpleid.koinic.net)
 | 
				
			||||||
 | 
					;  - http://openid.org.cn/
 | 
				
			||||||
 | 
					;  - openid.stackexchange.com
 | 
				
			||||||
 | 
					;  - login.launchpad.net
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
 | 
					; Whether to allow signin in via OpenID
 | 
				
			||||||
 | 
					ENABLE_OPENID_SIGNIN = true
 | 
				
			||||||
 | 
					; Whether to allow registering via OpenID
 | 
				
			||||||
 | 
					ENABLE_OPENID_SIGNUP = true
 | 
				
			||||||
 | 
					; Allowed URI patterns (POSIX regexp).
 | 
				
			||||||
 | 
					; Space separated.
 | 
				
			||||||
 | 
					; Only these would be allowed if non-blank.
 | 
				
			||||||
 | 
					; Example value: trusted.domain.org trusted.domain.net
 | 
				
			||||||
 | 
					WHITELISTED_URIS =
 | 
				
			||||||
 | 
					; Forbidden URI patterns (POSIX regexp).
 | 
				
			||||||
 | 
					; Space sepaated.
 | 
				
			||||||
 | 
					; Only used if WHITELISTED_URIS is blank.
 | 
				
			||||||
 | 
					; Example value: loadaverage.org/badguy stackexchange.com/.*spammer
 | 
				
			||||||
 | 
					BLACKLISTED_URIS =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[service]
 | 
					[service]
 | 
				
			||||||
ACTIVE_CODE_LIVE_MINUTES = 180
 | 
					ACTIVE_CODE_LIVE_MINUTES = 180
 | 
				
			||||||
RESET_PASSWD_CODE_LIVE_MINUTES = 180
 | 
					RESET_PASSWD_CODE_LIVE_MINUTES = 180
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,6 +93,21 @@ func (err ErrEmailAlreadyUsed) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
 | 
						return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrOpenIDAlreadyUsed represents a "OpenIDAlreadyUsed" kind of error.
 | 
				
			||||||
 | 
					type ErrOpenIDAlreadyUsed struct {
 | 
				
			||||||
 | 
						OpenID string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrOpenIDAlreadyUsed checks if an error is a ErrOpenIDAlreadyUsed.
 | 
				
			||||||
 | 
					func IsErrOpenIDAlreadyUsed(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrOpenIDAlreadyUsed)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrOpenIDAlreadyUsed) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("OpenID has been used [oid: %s]", err.OpenID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
 | 
					// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
 | 
				
			||||||
type ErrUserOwnRepos struct {
 | 
					type ErrUserOwnRepos struct {
 | 
				
			||||||
	UID int64
 | 
						UID int64
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,6 +94,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("rewrite authorized_keys file via new format", useNewPublickeyFormat),
 | 
						NewMigration("rewrite authorized_keys file via new format", useNewPublickeyFormat),
 | 
				
			||||||
	// v22 -> v23
 | 
						// v22 -> v23
 | 
				
			||||||
	NewMigration("generate and migrate wiki Git hooks", generateAndMigrateWikiGitHooks),
 | 
						NewMigration("generate and migrate wiki Git hooks", generateAndMigrateWikiGitHooks),
 | 
				
			||||||
 | 
						// v23 -> v24
 | 
				
			||||||
 | 
						NewMigration("add user openid table", addUserOpenID),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Migrate database to current version
 | 
					// Migrate database to current version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										26
									
								
								models/migrations/v23.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								models/migrations/v23.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 Gitea. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-xorm/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UserOpenID is the list of all OpenID identities of a user.
 | 
				
			||||||
 | 
					type UserOpenID struct {
 | 
				
			||||||
 | 
						ID          int64  `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
						UID         int64  `xorm:"INDEX NOT NULL"`
 | 
				
			||||||
 | 
						URI         string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addUserOpenID(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						if err := x.Sync2(new(UserOpenID)); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Sync2: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -116,6 +116,7 @@ func init() {
 | 
				
			|||||||
		new(RepoRedirect),
 | 
							new(RepoRedirect),
 | 
				
			||||||
		new(ExternalLoginUser),
 | 
							new(ExternalLoginUser),
 | 
				
			||||||
		new(ProtectedBranch),
 | 
							new(ProtectedBranch),
 | 
				
			||||||
 | 
							new(UserOpenID),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gonicNames := []string{"SSL", "UID"}
 | 
						gonicNames := []string{"SSL", "UID"}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -964,6 +964,7 @@ func deleteUser(e *xorm.Session, u *User) error {
 | 
				
			|||||||
		&Action{UserID: u.ID},
 | 
							&Action{UserID: u.ID},
 | 
				
			||||||
		&IssueUser{UID: u.ID},
 | 
							&IssueUser{UID: u.ID},
 | 
				
			||||||
		&EmailAddress{UID: u.ID},
 | 
							&EmailAddress{UID: u.ID},
 | 
				
			||||||
 | 
							&UserOpenID{UID: u.ID},
 | 
				
			||||||
	); err != nil {
 | 
						); err != nil {
 | 
				
			||||||
		return fmt.Errorf("deleteBeans: %v", err)
 | 
							return fmt.Errorf("deleteBeans: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										117
									
								
								models/user_openid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								models/user_openid.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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 models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/auth/openid"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// ErrOpenIDNotExist openid is not known
 | 
				
			||||||
 | 
						ErrOpenIDNotExist = errors.New("OpenID is unknown")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UserOpenID is the list of all OpenID identities of a user.
 | 
				
			||||||
 | 
					type UserOpenID struct {
 | 
				
			||||||
 | 
						ID          int64  `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
						UID         int64  `xorm:"INDEX NOT NULL"`
 | 
				
			||||||
 | 
						URI         string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetUserOpenIDs returns all openid addresses that belongs to given user.
 | 
				
			||||||
 | 
					func GetUserOpenIDs(uid int64) ([]*UserOpenID, error) {
 | 
				
			||||||
 | 
						openids := make([]*UserOpenID, 0, 5)
 | 
				
			||||||
 | 
						if err := x.
 | 
				
			||||||
 | 
							Where("uid=?", uid).
 | 
				
			||||||
 | 
							Find(&openids); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return openids, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isOpenIDUsed(e Engine, uri string) (bool, error) {
 | 
				
			||||||
 | 
						if len(uri) == 0 {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return e.Get(&UserOpenID{URI: uri})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsOpenIDUsed returns true if the openid has been used.
 | 
				
			||||||
 | 
					func IsOpenIDUsed(openid string) (bool, error) {
 | 
				
			||||||
 | 
						return isOpenIDUsed(x, openid)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOTE: make sure openid.URI is normalized already
 | 
				
			||||||
 | 
					func addUserOpenID(e Engine, openid *UserOpenID) error {
 | 
				
			||||||
 | 
						used, err := isOpenIDUsed(e, openid.URI)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else if used {
 | 
				
			||||||
 | 
							return ErrOpenIDAlreadyUsed{openid.URI}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = e.Insert(openid)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddUserOpenID adds an pre-verified/normalized OpenID URI to given user.
 | 
				
			||||||
 | 
					func AddUserOpenID(openid *UserOpenID) error {
 | 
				
			||||||
 | 
						return addUserOpenID(x, openid)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteUserOpenID deletes an openid address of given user.
 | 
				
			||||||
 | 
					func DeleteUserOpenID(openid *UserOpenID) (err error) {
 | 
				
			||||||
 | 
						var deleted int64
 | 
				
			||||||
 | 
						// ask to check UID
 | 
				
			||||||
 | 
						var address = UserOpenID{
 | 
				
			||||||
 | 
							UID: openid.UID,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if openid.ID > 0 {
 | 
				
			||||||
 | 
							deleted, err = x.Id(openid.ID).Delete(&address)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							deleted, err = x.
 | 
				
			||||||
 | 
								Where("openid=?", openid.URI).
 | 
				
			||||||
 | 
								Delete(&address)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else if deleted != 1 {
 | 
				
			||||||
 | 
							return ErrOpenIDNotExist
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetUserByOpenID returns the user object by given OpenID if exists.
 | 
				
			||||||
 | 
					func GetUserByOpenID(uri string) (*User, error) {
 | 
				
			||||||
 | 
						if len(uri) == 0 {
 | 
				
			||||||
 | 
							return nil, ErrUserNotExist{0, uri, 0}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uri, err := openid.Normalize(uri)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Normalized OpenID URI: " + uri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Otherwise, check in openid table
 | 
				
			||||||
 | 
						oid := &UserOpenID{URI: uri}
 | 
				
			||||||
 | 
						has, err := x.Get(oid)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if has {
 | 
				
			||||||
 | 
							return GetUserByID(oid.UID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil, ErrUserNotExist{0, uri, 0}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										59
									
								
								modules/auth/openid/discovery_cache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								modules/auth/openid/discovery_cache.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/yohcop/openid-go"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type timedDiscoveredInfo struct {
 | 
				
			||||||
 | 
						info openid.DiscoveredInfo
 | 
				
			||||||
 | 
						time time.Time
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type timedDiscoveryCache struct {
 | 
				
			||||||
 | 
						cache map[string]timedDiscoveredInfo
 | 
				
			||||||
 | 
						ttl time.Duration
 | 
				
			||||||
 | 
						mutex *sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newTimedDiscoveryCache(ttl time.Duration) *timedDiscoveryCache {
 | 
				
			||||||
 | 
						return &timedDiscoveryCache{cache: map[string]timedDiscoveredInfo{}, ttl: ttl, mutex: &sync.Mutex{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *timedDiscoveryCache) Put(id string, info openid.DiscoveredInfo) {
 | 
				
			||||||
 | 
						s.mutex.Lock()
 | 
				
			||||||
 | 
						defer s.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.cache[id] = timedDiscoveredInfo{info: info, time: time.Now()}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Delete timed-out cache entries
 | 
				
			||||||
 | 
					func (s *timedDiscoveryCache) cleanTimedOut() {
 | 
				
			||||||
 | 
						now := time.Now()
 | 
				
			||||||
 | 
						for k, e := range s.cache {
 | 
				
			||||||
 | 
							diff := now.Sub(e.time)
 | 
				
			||||||
 | 
							if diff > s.ttl {
 | 
				
			||||||
 | 
								delete(s.cache, k)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *timedDiscoveryCache) Get(id string) openid.DiscoveredInfo {
 | 
				
			||||||
 | 
						s.mutex.Lock()
 | 
				
			||||||
 | 
						defer s.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete old cached while we are at it.
 | 
				
			||||||
 | 
						s.cleanTimedOut()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if info, has := s.cache[id]; has {
 | 
				
			||||||
 | 
							return info.info
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										47
									
								
								modules/auth/openid/discovery_cache_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								modules/auth/openid/discovery_cache_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type testDiscoveredInfo struct {}
 | 
				
			||||||
 | 
					func (s *testDiscoveredInfo) ClaimedID() string {
 | 
				
			||||||
 | 
						return "claimedID"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (s *testDiscoveredInfo) OpEndpoint() string {
 | 
				
			||||||
 | 
						return "opEndpoint"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (s *testDiscoveredInfo) OpLocalID() string {
 | 
				
			||||||
 | 
						return "opLocalID"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestTimedDiscoveryCache(t *testing.T) {
 | 
				
			||||||
 | 
						dc := newTimedDiscoveryCache(1*time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Put some initial values
 | 
				
			||||||
 | 
						dc.Put("foo", &testDiscoveredInfo{}) //openid.opEndpoint: "a", openid.opLocalID: "b", openid.claimedID: "c"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make sure we can retrieve them
 | 
				
			||||||
 | 
						if di := dc.Get("foo"); di == nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected a result, got nil")
 | 
				
			||||||
 | 
						} else if di.OpEndpoint() != "opEndpoint" || di.OpLocalID() != "opLocalID" || di.ClaimedID() != "claimedID" {
 | 
				
			||||||
 | 
							t.Errorf("Expected opEndpoint opLocalID claimedID, got %v %v %v", di.OpEndpoint(), di.OpLocalID(), di.ClaimedID())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Attempt to get a non-existent value
 | 
				
			||||||
 | 
						if di := dc.Get("bar"); di != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected nil, got %v", di)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sleep one second and try retrive again
 | 
				
			||||||
 | 
						time.Sleep(1 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if di := dc.Get("foo"); di != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected a nil, got a result")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								modules/auth/openid/openid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								modules/auth/openid/openid.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/yohcop/openid-go"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 = newTimedDiscoveryCache(24*time.Hour)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Verify handles response from OpenID provider
 | 
				
			||||||
 | 
					func Verify(fullURL string) (id string, err error) {
 | 
				
			||||||
 | 
						return openid.Verify(fullURL, discoveryCache, nonceStore)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Normalize normalizes an OpenID URI
 | 
				
			||||||
 | 
					func Normalize(url string) (id string, err error) {
 | 
				
			||||||
 | 
						return openid.Normalize(url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RedirectURL redirects browser
 | 
				
			||||||
 | 
					func RedirectURL(id, callbackURL, realm string) (string, error) {
 | 
				
			||||||
 | 
						return openid.RedirectURL(id, callbackURL, realm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -78,7 +78,7 @@ func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) bindi
 | 
				
			|||||||
	return validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SignInForm form for signing in
 | 
					// SignInForm form for signing in with user/password
 | 
				
			||||||
type SignInForm struct {
 | 
					type SignInForm struct {
 | 
				
			||||||
	UserName string `binding:"Required;MaxSize(254)"`
 | 
						UserName string `binding:"Required;MaxSize(254)"`
 | 
				
			||||||
	Password string `binding:"Required;MaxSize(255)"`
 | 
						Password string `binding:"Required;MaxSize(255)"`
 | 
				
			||||||
@@ -153,6 +153,16 @@ func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors)
 | 
				
			|||||||
	return validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddOpenIDForm is for changing openid uri
 | 
				
			||||||
 | 
					type AddOpenIDForm struct {
 | 
				
			||||||
 | 
						Openid      string `binding:"Required;MaxSize(256)"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate validates the fields
 | 
				
			||||||
 | 
					func (f *AddOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddSSHKeyForm form for adding SSH key
 | 
					// AddSSHKeyForm form for adding SSH key
 | 
				
			||||||
type AddSSHKeyForm struct {
 | 
					type AddSSHKeyForm struct {
 | 
				
			||||||
	Title   string `binding:"Required;MaxSize(50)"`
 | 
						Title   string `binding:"Required;MaxSize(50)"`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										45
									
								
								modules/auth/user_form_auth_openid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								modules/auth/user_form_auth_openid.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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 auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/go-macaron/binding"
 | 
				
			||||||
 | 
						"gopkg.in/macaron.v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SignInOpenIDForm form for signing in with OpenID
 | 
				
			||||||
 | 
					type SignInOpenIDForm struct {
 | 
				
			||||||
 | 
						Openid string `binding:"Required;MaxSize(256)"`
 | 
				
			||||||
 | 
						Remember bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate valideates the fields
 | 
				
			||||||
 | 
					func (f *SignInOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SignUpOpenIDForm form for signin up with OpenID
 | 
				
			||||||
 | 
					type SignUpOpenIDForm struct {
 | 
				
			||||||
 | 
						UserName string `binding:"Required;AlphaDashDot;MaxSize(35)"`
 | 
				
			||||||
 | 
						Email    string `binding:"Required;Email;MaxSize(254)"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate valideates the fields
 | 
				
			||||||
 | 
					func (f *SignUpOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConnectOpenIDForm form for connecting an existing account to an OpenID URI
 | 
				
			||||||
 | 
					type ConnectOpenIDForm struct {
 | 
				
			||||||
 | 
						UserName string `binding:"Required;MaxSize(254)"`
 | 
				
			||||||
 | 
						Password string `binding:"Required;MaxSize(255)"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate valideates the fields
 | 
				
			||||||
 | 
					func (f *ConnectOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -197,6 +197,7 @@ func Contexter() macaron.Handler {
 | 
				
			|||||||
		ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
 | 
							ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
 | 
				
			||||||
		ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
 | 
							ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
 | 
				
			||||||
		ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
 | 
							ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
 | 
				
			||||||
 | 
							ctx.Data["EnableOpenIDSignIn"] = setting.EnableOpenIDSignIn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		c.Map(ctx)
 | 
							c.Map(ctx)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ import (
 | 
				
			|||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@@ -120,6 +121,12 @@ var (
 | 
				
			|||||||
	MinPasswordLength    int
 | 
						MinPasswordLength    int
 | 
				
			||||||
	ImportLocalPaths     bool
 | 
						ImportLocalPaths     bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// OpenID settings
 | 
				
			||||||
 | 
						EnableOpenIDSignIn bool
 | 
				
			||||||
 | 
						EnableOpenIDSignUp bool
 | 
				
			||||||
 | 
						OpenIDWhitelist    []*regexp.Regexp
 | 
				
			||||||
 | 
						OpenIDBlacklist    []*regexp.Regexp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Database settings
 | 
						// Database settings
 | 
				
			||||||
	UseSQLite3    bool
 | 
						UseSQLite3    bool
 | 
				
			||||||
	UseMySQL      bool
 | 
						UseMySQL      bool
 | 
				
			||||||
@@ -755,6 +762,24 @@ please consider changing to GITEA_CUSTOM`)
 | 
				
			|||||||
	MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
 | 
						MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
 | 
				
			||||||
	ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
 | 
						ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sec = Cfg.Section("openid")
 | 
				
			||||||
 | 
						EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(true)
 | 
				
			||||||
 | 
						EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(true)
 | 
				
			||||||
 | 
						pats := sec.Key("WHITELISTED_URIS").Strings(" ")
 | 
				
			||||||
 | 
						if ( len(pats) != 0 ) {
 | 
				
			||||||
 | 
							OpenIDWhitelist = make([]*regexp.Regexp, len(pats))
 | 
				
			||||||
 | 
							for i, p := range pats {
 | 
				
			||||||
 | 
								OpenIDWhitelist[i] = regexp.MustCompilePOSIX(p)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pats = sec.Key("BLACKLISTED_URIS").Strings(" ")
 | 
				
			||||||
 | 
						if ( len(pats) != 0 ) {
 | 
				
			||||||
 | 
							OpenIDBlacklist = make([]*regexp.Regexp, len(pats))
 | 
				
			||||||
 | 
							for i, p := range pats {
 | 
				
			||||||
 | 
								OpenIDBlacklist[i] = regexp.MustCompilePOSIX(p)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sec = Cfg.Section("attachment")
 | 
						sec = Cfg.Section("attachment")
 | 
				
			||||||
	AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
 | 
						AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
 | 
				
			||||||
	if !filepath.IsAbs(AttachmentPath) {
 | 
						if !filepath.IsAbs(AttachmentPath) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -188,6 +188,14 @@ use_scratch_code = Use a scratch code
 | 
				
			|||||||
twofa_scratch_used = You have used your scratch code. You have been redirected to the two-factor settings page so you may remove your device enrollment or generate a new scratch code.
 | 
					twofa_scratch_used = You have used your scratch code. You have been redirected to the two-factor settings page so you may remove your device enrollment or generate a new scratch code.
 | 
				
			||||||
twofa_passcode_incorrect = Your passcode is not correct. If you misplaced your device, use your scratch code to login.
 | 
					twofa_passcode_incorrect = Your passcode is not correct. If you misplaced your device, use your scratch code to login.
 | 
				
			||||||
twofa_scratch_token_incorrect = Your scratch code is not correct.
 | 
					twofa_scratch_token_incorrect = Your scratch code is not correct.
 | 
				
			||||||
 | 
					login_userpass = User / Password
 | 
				
			||||||
 | 
					login_openid = OpenID
 | 
				
			||||||
 | 
					openid_connect_submit = Connect
 | 
				
			||||||
 | 
					openid_connect_title = Connect to an existing account
 | 
				
			||||||
 | 
					openid_connect_desc = The entered OpenID URIs is not know by the system, here you can associate it to an existing account.
 | 
				
			||||||
 | 
					openid_register_title = Create new account
 | 
				
			||||||
 | 
					openid_register_desc = The entered OpenID URIs is not know by the system, here you can associate it to a new account.
 | 
				
			||||||
 | 
					openid_signin_desc = Example URIs: https://anne.me, bob.openid.org.cn, gnusocial.net/carry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[mail]
 | 
					[mail]
 | 
				
			||||||
activate_account = Please activate your account
 | 
					activate_account = Please activate your account
 | 
				
			||||||
@@ -239,6 +247,7 @@ repo_name_been_taken = Repository name has already been used.
 | 
				
			|||||||
org_name_been_taken = Organization name has already been taken.
 | 
					org_name_been_taken = Organization name has already been taken.
 | 
				
			||||||
team_name_been_taken = Team name has already been taken.
 | 
					team_name_been_taken = Team name has already been taken.
 | 
				
			||||||
email_been_used = Email address has already been used.
 | 
					email_been_used = Email address has already been used.
 | 
				
			||||||
 | 
					openid_been_used = OpenID address '%s' has already been used.
 | 
				
			||||||
username_password_incorrect = Username or password is not correct.
 | 
					username_password_incorrect = Username or password is not correct.
 | 
				
			||||||
enterred_invalid_repo_name = Please make sure that the repository name you entered is correct.
 | 
					enterred_invalid_repo_name = Please make sure that the repository name you entered is correct.
 | 
				
			||||||
enterred_invalid_owner_name = Please make sure that the owner name you entered is correct.
 | 
					enterred_invalid_owner_name = Please make sure that the owner name you entered is correct.
 | 
				
			||||||
@@ -315,6 +324,7 @@ password_change_disabled = Non-local users are not allowed to change their passw
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
emails = Email Addresses
 | 
					emails = Email Addresses
 | 
				
			||||||
manage_emails = Manage email addresses
 | 
					manage_emails = Manage email addresses
 | 
				
			||||||
 | 
					manage_openid = Manage OpenID addresses
 | 
				
			||||||
email_desc = Your primary email address will be used for notifications and other operations.
 | 
					email_desc = Your primary email address will be used for notifications and other operations.
 | 
				
			||||||
primary = Primary
 | 
					primary = Primary
 | 
				
			||||||
primary_email = Set as primary
 | 
					primary_email = Set as primary
 | 
				
			||||||
@@ -322,12 +332,19 @@ delete_email = Delete
 | 
				
			|||||||
email_deletion = Email Deletion
 | 
					email_deletion = Email Deletion
 | 
				
			||||||
email_deletion_desc = Deleting this email address will remove all related information from your account. Do you want to continue?
 | 
					email_deletion_desc = Deleting this email address will remove all related information from your account. Do you want to continue?
 | 
				
			||||||
email_deletion_success = Email has been deleted successfully!
 | 
					email_deletion_success = Email has been deleted successfully!
 | 
				
			||||||
 | 
					openid_deletion = OpenID Deletion
 | 
				
			||||||
 | 
					openid_deletion_desc = Deleting this OpenID address will prevent you from signing in using it, are you sure you want to continue ?
 | 
				
			||||||
 | 
					openid_deletion_success = OpenID has been deleted successfully!
 | 
				
			||||||
add_new_email = Add new email address
 | 
					add_new_email = Add new email address
 | 
				
			||||||
 | 
					add_new_openid = Add new OpenID URI
 | 
				
			||||||
add_email = Add email
 | 
					add_email = Add email
 | 
				
			||||||
 | 
					add_openid = Add OpenID URI
 | 
				
			||||||
add_email_confirmation_sent = A new confirmation email has been sent to '%s', please check your inbox within the next %d hours to complete the confirmation process.
 | 
					add_email_confirmation_sent = A new confirmation email has been sent to '%s', please check your inbox within the next %d hours to complete the confirmation process.
 | 
				
			||||||
add_email_success = Your new email address was successfully added.
 | 
					add_email_success = Your new email address was successfully added.
 | 
				
			||||||
 | 
					add_openid_success = Your new OpenID address was successfully added.
 | 
				
			||||||
keep_email_private = Keep Email Address Private
 | 
					keep_email_private = Keep Email Address Private
 | 
				
			||||||
keep_email_private_popup = Your email address will be hidden from other users if this option is set.
 | 
					keep_email_private_popup = Your email address will be hidden from other users if this option is set.
 | 
				
			||||||
 | 
					openid_desc = Your OpenID addresses will let you delegate authentication to your provider of choice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
manage_ssh_keys = Manage SSH Keys
 | 
					manage_ssh_keys = Manage SSH Keys
 | 
				
			||||||
add_key = Add Key
 | 
					add_key = Add Key
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								public/img/openid-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/img/openid-16x16.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 230 B  | 
@@ -107,7 +107,6 @@ func checkAutoLogin(ctx *context.Context) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SignIn render sign in page
 | 
					// SignIn render sign in page
 | 
				
			||||||
func SignIn(ctx *context.Context) {
 | 
					func SignIn(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("sign_in")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check auto-login.
 | 
						// Check auto-login.
 | 
				
			||||||
	if checkAutoLogin(ctx) {
 | 
						if checkAutoLogin(ctx) {
 | 
				
			||||||
@@ -120,6 +119,9 @@ func SignIn(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["OAuth2Providers"] = oauth2Providers
 | 
						ctx.Data["OAuth2Providers"] = oauth2Providers
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("sign_in")
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsLogin"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.HTML(200, tplSignIn)
 | 
						ctx.HTML(200, tplSignIn)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -127,6 +129,8 @@ func SignIn(ctx *context.Context) {
 | 
				
			|||||||
// SignInPost response for sign in request
 | 
					// SignInPost response for sign in request
 | 
				
			||||||
func SignInPost(ctx *context.Context, form auth.SignInForm) {
 | 
					func SignInPost(ctx *context.Context, form auth.SignInForm) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("sign_in")
 | 
						ctx.Data["Title"] = ctx.Tr("sign_in")
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsLogin"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oauth2Providers, err := models.GetActiveOAuth2Providers()
 | 
						oauth2Providers, err := models.GetActiveOAuth2Providers()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -316,6 +320,10 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 | 
				
			|||||||
			setting.CookieRememberName, u.Name, days, setting.AppSubURL)
 | 
								setting.CookieRememberName, u.Name, days, setting.AppSubURL)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Session.Delete("openid_verified_uri")
 | 
				
			||||||
 | 
						ctx.Session.Delete("openid_signin_remember")
 | 
				
			||||||
 | 
						ctx.Session.Delete("openid_determined_email")
 | 
				
			||||||
 | 
						ctx.Session.Delete("openid_determined_username")
 | 
				
			||||||
	ctx.Session.Delete("twofaUid")
 | 
						ctx.Session.Delete("twofaUid")
 | 
				
			||||||
	ctx.Session.Delete("twofaRemember")
 | 
						ctx.Session.Delete("twofaRemember")
 | 
				
			||||||
	ctx.Session.Set("uid", u.ID)
 | 
						ctx.Session.Set("uid", u.ID)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										426
									
								
								routers/user/auth_openid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								routers/user/auth_openid.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,426 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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 user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/auth"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/auth/openid"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						tplSignInOpenID base.TplName = "user/auth/signin_openid"
 | 
				
			||||||
 | 
						tplConnectOID   base.TplName = "user/auth/signup_openid_connect"
 | 
				
			||||||
 | 
						tplSignUpOID    base.TplName = "user/auth/signup_openid_register"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SignInOpenID render sign in page
 | 
				
			||||||
 | 
					func SignInOpenID(ctx *context.Context) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("sign_in")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Query("openid.return_to") != "" {
 | 
				
			||||||
 | 
							signInOpenIDVerify(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check auto-login.
 | 
				
			||||||
 | 
						isSucceed, err := AutoSignIn(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "AutoSignIn", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						redirectTo := ctx.Query("redirect_to")
 | 
				
			||||||
 | 
						if len(redirectTo) > 0 {
 | 
				
			||||||
 | 
							ctx.SetCookie("redirect_to", redirectTo, 0, setting.AppSubURL)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							redirectTo, _ = url.QueryUnescape(ctx.GetCookie("redirect_to"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if isSucceed {
 | 
				
			||||||
 | 
							if len(redirectTo) > 0 {
 | 
				
			||||||
 | 
								ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
				
			||||||
 | 
								ctx.Redirect(redirectTo)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Redirect(setting.AppSubURL + "/")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsLoginOpenID"] = true
 | 
				
			||||||
 | 
						ctx.HTML(200, tplSignInOpenID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check if the given OpenID URI is allowed by blacklist/whitelist
 | 
				
			||||||
 | 
					func allowedOpenIDURI(uri string) (err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// In case a Whitelist is present, URI must be in it
 | 
				
			||||||
 | 
						// in order to be accepted
 | 
				
			||||||
 | 
						if len(setting.OpenIDWhitelist) != 0 {
 | 
				
			||||||
 | 
							for _, pat := range setting.OpenIDWhitelist {
 | 
				
			||||||
 | 
								if pat.MatchString(uri) {
 | 
				
			||||||
 | 
									return nil // pass
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// must match one of this or be refused
 | 
				
			||||||
 | 
							return fmt.Errorf("URI not allowed by whitelist")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// A blacklist match expliclty forbids
 | 
				
			||||||
 | 
						for _, pat := range setting.OpenIDBlacklist {
 | 
				
			||||||
 | 
							if pat.MatchString(uri) {
 | 
				
			||||||
 | 
								return fmt.Errorf("URI forbidden by blacklist")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SignInOpenIDPost response for openid sign in request
 | 
				
			||||||
 | 
					func SignInOpenIDPost(ctx *context.Context, form auth.SignInOpenIDForm) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("sign_in")
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsLoginOpenID"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.HTML(200, tplSignInOpenID)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id, err := openid.Normalize(form.Openid)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignInOpenID, &form)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						form.Openid = id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("OpenID uri: " + id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = allowedOpenIDURI(id); if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignInOpenID, &form)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						redirectTo := setting.AppURL + "user/login/openid"
 | 
				
			||||||
 | 
						url, err := openid.RedirectURL(id, redirectTo, setting.AppURL)
 | 
				
			||||||
 | 
					        if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignInOpenID, &form)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Request optional nickname and email info
 | 
				
			||||||
 | 
						// NOTE: change to `openid.sreg.required` to require it
 | 
				
			||||||
 | 
						url += "&openid.ns.sreg=http%3A%2F%2Fopenid.net%2Fextensions%2Fsreg%2F1.1"
 | 
				
			||||||
 | 
						url += "&openid.sreg.optional=nickname%2Cemail"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Form-passed openid-remember: %s", form.Remember)
 | 
				
			||||||
 | 
						ctx.Session.Set("openid_signin_remember", form.Remember)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Redirect(url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// signInOpenIDVerify handles response from OpenID provider
 | 
				
			||||||
 | 
					func signInOpenIDVerify(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.Trace("Incoming call to: " + ctx.Req.Request.URL.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:]
 | 
				
			||||||
 | 
					        log.Trace("Full URL: " + fullURL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var id, err = openid.Verify(fullURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
 | 
				
			||||||
 | 
								Openid: id,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Verified ID: " + id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Now we should seek for the user and log him in, or prompt
 | 
				
			||||||
 | 
						 * to register if not found */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u, _ := models.GetUserByOpenID(id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if ! models.IsErrUserNotExist(err) {
 | 
				
			||||||
 | 
								ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
 | 
				
			||||||
 | 
									Openid: id,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if u != nil {
 | 
				
			||||||
 | 
							log.Trace("User exists, logging in")
 | 
				
			||||||
 | 
							remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
 | 
				
			||||||
 | 
							log.Trace("Session stored openid-remember: %s", remember)
 | 
				
			||||||
 | 
							handleSignIn(ctx, u, remember)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("User with openid " + id + " does not exist, should connect or register")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parsedURL, err := url.Parse(fullURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
 | 
				
			||||||
 | 
								Openid: id,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						values, err := url.ParseQuery(parsedURL.RawQuery)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
 | 
				
			||||||
 | 
								Openid: id,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						email := values.Get("openid.sreg.email")
 | 
				
			||||||
 | 
						nickname := values.Get("openid.sreg.nickname")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("User has email=" + email +  " and nickname=" + nickname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if email != "" {
 | 
				
			||||||
 | 
							u, _ = models.GetUserByEmail(email)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if ! models.IsErrUserNotExist(err) {
 | 
				
			||||||
 | 
									ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
 | 
				
			||||||
 | 
										Openid: id,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if u != nil {
 | 
				
			||||||
 | 
								log.Trace("Local user " + u.LowerName + " has OpenID provided email " + email)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if u == nil && nickname != "" {
 | 
				
			||||||
 | 
							u, _ = models.GetUserByName(nickname)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if ! models.IsErrUserNotExist(err) {
 | 
				
			||||||
 | 
									ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
 | 
				
			||||||
 | 
										Openid: id,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if u != nil {
 | 
				
			||||||
 | 
								log.Trace("Local user " + u.LowerName + " has OpenID provided nickname " + nickname)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Session.Set("openid_verified_uri", id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Session.Set("openid_determined_email", email)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if u != nil {
 | 
				
			||||||
 | 
							nickname = u.LowerName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Session.Set("openid_determined_username", nickname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if u != nil || ! setting.EnableOpenIDSignUp {
 | 
				
			||||||
 | 
							ctx.Redirect(setting.AppSubURL + "/user/openid/connect")
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							ctx.Redirect(setting.AppSubURL + "/user/openid/register")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConnectOpenID shows a form to connect an OpenID URI to an existing account
 | 
				
			||||||
 | 
					func ConnectOpenID(ctx *context.Context) {
 | 
				
			||||||
 | 
						oid, _ := ctx.Session.Get("openid_verified_uri").(string)
 | 
				
			||||||
 | 
						if oid == "" {
 | 
				
			||||||
 | 
							ctx.Redirect(setting.AppSubURL + "/user/login/openid")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["Title"] = "OpenID connect"
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsOpenIDConnect"] = true
 | 
				
			||||||
 | 
						ctx.Data["EnableOpenIDSignUp"] = setting.EnableOpenIDSignUp
 | 
				
			||||||
 | 
						ctx.Data["OpenID"] = oid
 | 
				
			||||||
 | 
						userName, _ := ctx.Session.Get("openid_determined_username").(string)
 | 
				
			||||||
 | 
						if userName != "" {
 | 
				
			||||||
 | 
							ctx.Data["user_name"] = userName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.HTML(200, tplConnectOID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account
 | 
				
			||||||
 | 
					func ConnectOpenIDPost(ctx *context.Context, form auth.ConnectOpenIDForm) {
 | 
				
			||||||
 | 
						oid, _ := ctx.Session.Get("openid_verified_uri").(string)
 | 
				
			||||||
 | 
						if oid == "" {
 | 
				
			||||||
 | 
							ctx.Redirect(setting.AppSubURL + "/user/login/openid")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["Title"] = "OpenID connect"
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsOpenIDConnect"] = true
 | 
				
			||||||
 | 
						ctx.Data["EnableOpenIDSignUp"] = setting.EnableOpenIDSignUp
 | 
				
			||||||
 | 
						ctx.Data["OpenID"] = oid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u, err := models.UserSignIn(form.UserName, form.Password)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if models.IsErrUserNotExist(err) {
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Handle(500, "ConnectOpenIDPost", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// add OpenID for the user
 | 
				
			||||||
 | 
						userOID := &models.UserOpenID{UID:u.ID, URI:oid}
 | 
				
			||||||
 | 
						if err = models.AddUserOpenID(userOID); err != nil {
 | 
				
			||||||
 | 
							if models.IsErrOpenIDAlreadyUsed(err) {
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplConnectOID, &form)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Handle(500, "AddUserOpenID", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Flash.Success(ctx.Tr("settings.add_openid_success"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
 | 
				
			||||||
 | 
						log.Trace("Session stored openid-remember: %s", remember)
 | 
				
			||||||
 | 
						handleSignIn(ctx, u, remember)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RegisterOpenID shows a form to create a new user authenticated via an OpenID URI
 | 
				
			||||||
 | 
					func RegisterOpenID(ctx *context.Context) {
 | 
				
			||||||
 | 
						if ! setting.EnableOpenIDSignUp {
 | 
				
			||||||
 | 
							ctx.Error(403)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						oid, _ := ctx.Session.Get("openid_verified_uri").(string)
 | 
				
			||||||
 | 
						if oid == "" {
 | 
				
			||||||
 | 
							ctx.Redirect(setting.AppSubURL + "/user/login/openid")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["Title"] = "OpenID signup"
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsOpenIDRegister"] = true
 | 
				
			||||||
 | 
						ctx.Data["EnableOpenIDSignUp"] = setting.EnableOpenIDSignUp
 | 
				
			||||||
 | 
						ctx.Data["OpenID"] = oid
 | 
				
			||||||
 | 
						userName, _ := ctx.Session.Get("openid_determined_username").(string)
 | 
				
			||||||
 | 
						if userName != "" {
 | 
				
			||||||
 | 
							ctx.Data["user_name"] = userName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						email, _ := ctx.Session.Get("openid_determined_email").(string)
 | 
				
			||||||
 | 
						if email != "" {
 | 
				
			||||||
 | 
							ctx.Data["email"] = email
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.HTML(200, tplSignUpOID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RegisterOpenIDPost handles submission of a form to create a new user authenticated via an OpenID URI
 | 
				
			||||||
 | 
					func RegisterOpenIDPost(ctx *context.Context, form auth.SignUpOpenIDForm) {
 | 
				
			||||||
 | 
						if ! setting.EnableOpenIDSignUp {
 | 
				
			||||||
 | 
							ctx.Error(403)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						oid, _ := ctx.Session.Get("openid_verified_uri").(string)
 | 
				
			||||||
 | 
						if oid == "" {
 | 
				
			||||||
 | 
							ctx.Redirect(setting.AppSubURL + "/user/login/openid")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["Title"] = "OpenID signup"
 | 
				
			||||||
 | 
						ctx.Data["PageIsSignIn"] = true
 | 
				
			||||||
 | 
						ctx.Data["PageIsOpenIDRegister"] = true
 | 
				
			||||||
 | 
						ctx.Data["EnableOpenIDSignUp"] = setting.EnableOpenIDSignUp
 | 
				
			||||||
 | 
						ctx.Data["OpenID"] = oid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
						// TODO: handle captcha ?
 | 
				
			||||||
 | 
						if setting.Service.EnableCaptcha && !cpt.VerifyReq(ctx.Req) {
 | 
				
			||||||
 | 
							ctx.Data["Err_Captcha"] = true
 | 
				
			||||||
 | 
							ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignUpOID, &form)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						len := setting.MinPasswordLength
 | 
				
			||||||
 | 
						if len < 256 { len = 256 }
 | 
				
			||||||
 | 
						password, err := base.GetRandomString(len)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSignUpOID, form)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: abstract a finalizeSignUp function ?
 | 
				
			||||||
 | 
						u := &models.User{
 | 
				
			||||||
 | 
							Name:     form.UserName,
 | 
				
			||||||
 | 
							Email:    form.Email,
 | 
				
			||||||
 | 
							Passwd:   password,
 | 
				
			||||||
 | 
							IsActive: !setting.Service.RegisterEmailConfirm,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := models.CreateUser(u); err != nil {
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case models.IsErrUserAlreadyExist(err):
 | 
				
			||||||
 | 
								ctx.Data["Err_UserName"] = true
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSignUpOID, &form)
 | 
				
			||||||
 | 
							case models.IsErrEmailAlreadyUsed(err):
 | 
				
			||||||
 | 
								ctx.Data["Err_Email"] = true
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSignUpOID, &form)
 | 
				
			||||||
 | 
							case models.IsErrNameReserved(err):
 | 
				
			||||||
 | 
								ctx.Data["Err_UserName"] = true
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(models.ErrNameReserved).Name), tplSignUpOID, &form)
 | 
				
			||||||
 | 
							case models.IsErrNamePatternNotAllowed(err):
 | 
				
			||||||
 | 
								ctx.Data["Err_UserName"] = true
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSignUpOID, &form)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								ctx.Handle(500, "CreateUser", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						log.Trace("Account created: %s", u.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// add OpenID for the user
 | 
				
			||||||
 | 
						userOID := &models.UserOpenID{UID:u.ID, URI:oid}
 | 
				
			||||||
 | 
						if err = models.AddUserOpenID(userOID); err != nil {
 | 
				
			||||||
 | 
							if models.IsErrOpenIDAlreadyUsed(err) {
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplSignUpOID, &form)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Handle(500, "AddUserOpenID", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Auto-set admin for the only user.
 | 
				
			||||||
 | 
						if models.CountUsers() == 1 {
 | 
				
			||||||
 | 
							u.IsAdmin = true
 | 
				
			||||||
 | 
							u.IsActive = true
 | 
				
			||||||
 | 
							if err := models.UpdateUser(u); err != nil {
 | 
				
			||||||
 | 
								ctx.Handle(500, "UpdateUser", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send confirmation email, no need for social account.
 | 
				
			||||||
 | 
						if setting.Service.RegisterEmailConfirm && u.ID > 1 {
 | 
				
			||||||
 | 
							models.SendActivateAccountMail(ctx.Context, u)
 | 
				
			||||||
 | 
							ctx.Data["IsSendRegisterMail"] = true
 | 
				
			||||||
 | 
							ctx.Data["Email"] = u.Email
 | 
				
			||||||
 | 
							ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60
 | 
				
			||||||
 | 
							ctx.HTML(200, TplActivate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
 | 
				
			||||||
 | 
								log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						remember, _ := ctx.Session.Get("openid_signin_remember").(bool)
 | 
				
			||||||
 | 
						log.Trace("Session stored openid-remember: %s", remember)
 | 
				
			||||||
 | 
						handleSignIn(ctx, u, remember)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										142
									
								
								routers/user/setting_openid.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								routers/user/setting_openid.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 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 user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/auth"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/auth/openid"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						tplSettingsOpenID       base.TplName = "user/settings/openid"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SettingsOpenID renders change user's openid page
 | 
				
			||||||
 | 
					func SettingsOpenID(ctx *context.Context) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
 | 
						ctx.Data["PageIsSettingsOpenID"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Query("openid.return_to") != "" {
 | 
				
			||||||
 | 
							settingsOpenIDVerify(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						openid, err := models.GetUserOpenIDs(ctx.User.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "GetUserOpenIDs", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["OpenIDs"] = openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.HTML(200, tplSettingsOpenID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SettingsOpenIDPost response for change user's openid
 | 
				
			||||||
 | 
					func SettingsOpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) {
 | 
				
			||||||
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
 | 
						ctx.Data["PageIsSettingsOpenID"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.HTML(200, tplSettingsOpenID)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// WARNING: specifying a wrong OpenID here could lock
 | 
				
			||||||
 | 
						// a user out of her account, would be better to
 | 
				
			||||||
 | 
						// verify/confirm the new OpenID before storing it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Also, consider allowing for multiple OpenID URIs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id, err := openid.Normalize(form.Openid)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSettingsOpenID, &form)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						form.Openid = id
 | 
				
			||||||
 | 
					        log.Trace("Normalized id: " + id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						oids, err := models.GetUserOpenIDs(ctx.User.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "GetUserOpenIDs", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["OpenIDs"] = oids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check that the OpenID is not already used
 | 
				
			||||||
 | 
						for _, obj := range oids {
 | 
				
			||||||
 | 
							if obj.URI == id {
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsOpenID, &form)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						redirectTo := setting.AppURL + "user/settings/openid"
 | 
				
			||||||
 | 
						url, err := openid.RedirectURL(id, redirectTo, setting.AppURL)
 | 
				
			||||||
 | 
					        if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSettingsOpenID, &form)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
						ctx.Redirect(url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func settingsOpenIDVerify(ctx *context.Context) {
 | 
				
			||||||
 | 
					        log.Trace("Incoming call to: " + ctx.Req.Request.URL.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:]
 | 
				
			||||||
 | 
					        log.Trace("Full URL: " + fullURL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						oids, err := models.GetUserOpenIDs(ctx.User.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "GetUserOpenIDs", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["OpenIDs"] = oids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id, err := openid.Verify(fullURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.RenderWithErr(err.Error(), tplSettingsOpenID, &auth.AddOpenIDForm{
 | 
				
			||||||
 | 
								Openid: id,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Verified ID: " + id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						oid := &models.UserOpenID{UID:ctx.User.ID, URI:id}
 | 
				
			||||||
 | 
						if err = models.AddUserOpenID(oid); err != nil {
 | 
				
			||||||
 | 
							if models.IsErrOpenIDAlreadyUsed(err) {
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsOpenID, &auth.AddOpenIDForm{ Openid: id })
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Handle(500, "AddUserOpenID", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						log.Trace("Associated OpenID %s to user %s", id, ctx.User.Name)
 | 
				
			||||||
 | 
						ctx.Flash.Success(ctx.Tr("settings.add_openid_success"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Redirect(setting.AppSubURL + "/user/settings/openid")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeleteOpenID response for delete user's openid
 | 
				
			||||||
 | 
					func DeleteOpenID(ctx *context.Context) {
 | 
				
			||||||
 | 
						if err := models.DeleteUserOpenID(&models.UserOpenID{ID: ctx.QueryInt64("id"), UID: ctx.User.ID}); err != nil {
 | 
				
			||||||
 | 
							ctx.Handle(500, "DeleteUserOpenID", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						log.Trace("OpenID address deleted: %s", ctx.User.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Flash.Success(ctx.Tr("settings.openid_deletion_success"))
 | 
				
			||||||
 | 
						ctx.JSON(200, map[string]interface{}{
 | 
				
			||||||
 | 
							"redirect": setting.AppSubURL + "/user/settings/openid",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								templates/user/auth/finalize_openid.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								templates/user/auth/finalize_openid.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="user signin">
 | 
				
			||||||
 | 
						<div class="ui container">
 | 
				
			||||||
 | 
							<div class="ui grid">
 | 
				
			||||||
 | 
								{{template "user/auth/finalize_openid_navbar" .}}
 | 
				
			||||||
 | 
								<div class="twelve wide column content">
 | 
				
			||||||
 | 
									{{template "base/alert" .}}
 | 
				
			||||||
 | 
									<h4 class="ui top attached header">
 | 
				
			||||||
 | 
										{{.i18n.Tr "auth.login_userpass"}}
 | 
				
			||||||
 | 
									</h4>
 | 
				
			||||||
 | 
									<div class="ui attached segment">
 | 
				
			||||||
 | 
										<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
 | 
										{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
										<div class="required inline field {{if .Err_UserName}}error{{end}}">
 | 
				
			||||||
 | 
											<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
 | 
				
			||||||
 | 
											<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="required inline field {{if .Err_Password}}error{{end}}">
 | 
				
			||||||
 | 
											<label for="password">{{.i18n.Tr "password"}}</label>
 | 
				
			||||||
 | 
											<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="inline field">
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
											<div class="ui checkbox">
 | 
				
			||||||
 | 
												<label>{{.i18n.Tr "auth.remember_me"}}</label>
 | 
				
			||||||
 | 
												<input name="remember" type="checkbox">
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<div class="inline field">
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
											<button class="ui green button">{{.i18n.Tr "sign_in"}}</button>
 | 
				
			||||||
 | 
											<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										{{if .ShowRegistrationButton}}
 | 
				
			||||||
 | 
											<div class="inline field">
 | 
				
			||||||
 | 
												<label></label>
 | 
				
			||||||
 | 
												<a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</form>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
@@ -1,3 +1,8 @@
 | 
				
			|||||||
{{template "base/head" .}}
 | 
					{{template "base/head" .}}
 | 
				
			||||||
{{template "user/auth/signin_inner" .}}
 | 
					<div class="user signin{{if .LinkAccountMode}} icon{{end}}">
 | 
				
			||||||
 | 
						{{template "user/auth/signin_navbar" .}}
 | 
				
			||||||
 | 
						<div class="ui container">
 | 
				
			||||||
 | 
							{{template "user/auth/signin_inner" .}}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
{{template "base/footer" .}}
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,12 @@
 | 
				
			|||||||
<div class="user signin{{if .LinkAccountMode}} icon{{end}}">
 | 
					 | 
				
			||||||
	<div class="ui middle very relaxed page grid">
 | 
					 | 
				
			||||||
		<div class="column">
 | 
					 | 
				
			||||||
			<form class="ui form" action="{{if not .LinkAccountMode}}{{.Link}}{{else}}{{.SignInLink}}{{end}}" method="post">
 | 
					 | 
				
			||||||
				{{.CsrfTokenHtml}}
 | 
					 | 
				
			||||||
				<h3 class="ui top attached header">
 | 
					 | 
				
			||||||
					{{.i18n.Tr "sign_in"}}
 | 
					 | 
				
			||||||
				</h3>
 | 
					 | 
				
			||||||
				<div class="ui attached segment">
 | 
					 | 
				
			||||||
		{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}}
 | 
							{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}}
 | 
				
			||||||
		{{template "base/alert" .}}
 | 
							{{template "base/alert" .}}
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
 | 
							<h4 class="ui top attached header">
 | 
				
			||||||
 | 
								{{.i18n.Tr "auth.login_userpass"}}
 | 
				
			||||||
 | 
							</h4>
 | 
				
			||||||
 | 
							<div class="ui attached segment">
 | 
				
			||||||
 | 
								<form class="ui form" action="{{if not .LinkAccountMode}}{{.Link}}{{else}}{{.SignInLink}}{{end}}" method="post">
 | 
				
			||||||
 | 
								{{.CsrfTokenHtml}}
 | 
				
			||||||
			<div class="required inline field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
 | 
								<div class="required inline field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
 | 
				
			||||||
				<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
 | 
									<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
 | 
				
			||||||
				<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
 | 
									<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
 | 
				
			||||||
@@ -50,8 +47,5 @@
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								templates/user/auth/signin_navbar.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								templates/user/auth/signin_navbar.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
 | 
				
			||||||
 | 
						<a class="{{if .PageIsLogin}}active{{end}} item" href="{{AppSubUrl}}/user/login">
 | 
				
			||||||
 | 
							{{.i18n.Tr "auth.login_userpass"}}
 | 
				
			||||||
 | 
						</a>
 | 
				
			||||||
 | 
						{{if .EnableOpenIDSignIn}}
 | 
				
			||||||
 | 
							<a class="{{if .PageIsLoginOpenID}}active{{end}} item" href="{{AppSubUrl}}/user/login/openid">
 | 
				
			||||||
 | 
								<img align="left" width="16" height="16" src="{{AppSubUrl}}/img/openid-16x16.png"/>
 | 
				
			||||||
 | 
								OpenID
 | 
				
			||||||
 | 
							</a>
 | 
				
			||||||
 | 
						{{end}}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
							
								
								
									
										37
									
								
								templates/user/auth/signin_openid.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								templates/user/auth/signin_openid.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="user signin openid">
 | 
				
			||||||
 | 
						{{template "user/auth/signin_navbar" .}}
 | 
				
			||||||
 | 
						<div class="ui container">
 | 
				
			||||||
 | 
							{{template "base/alert" .}}
 | 
				
			||||||
 | 
							<h4 class="ui top attached header">
 | 
				
			||||||
 | 
								OpenID
 | 
				
			||||||
 | 
							</h4>
 | 
				
			||||||
 | 
							<div class="ui attached segment">
 | 
				
			||||||
 | 
								<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
 | 
								{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
								<div class="inline field">
 | 
				
			||||||
 | 
									{{.i18n.Tr "auth.openid_signin_desc"}}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="required inline field {{if .Err_OpenID}}error{{end}}">
 | 
				
			||||||
 | 
									<label for="openid">
 | 
				
			||||||
 | 
									<img alt="OpenID URI" height="16" src="{{AppSubUrl}}/img/openid-16x16.png"/>
 | 
				
			||||||
 | 
									OpenID URI
 | 
				
			||||||
 | 
									</label>
 | 
				
			||||||
 | 
									<input id="openid" name="openid" value="{{.openid}}" autofocus required>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="inline field">
 | 
				
			||||||
 | 
									<label></label>
 | 
				
			||||||
 | 
									<div class="ui checkbox">
 | 
				
			||||||
 | 
										<label>{{.i18n.Tr "auth.remember_me"}}</label>
 | 
				
			||||||
 | 
										<input name="remember" type="checkbox">
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="inline field">
 | 
				
			||||||
 | 
									<label></label>
 | 
				
			||||||
 | 
									<button class="ui green button">{{.i18n.Tr "sign_in"}}</button>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								</form>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
							
								
								
									
										45
									
								
								templates/user/auth/signup_openid_connect.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								templates/user/auth/signup_openid_connect.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="user signup">
 | 
				
			||||||
 | 
						{{template "user/auth/signup_openid_navbar" .}}
 | 
				
			||||||
 | 
						<div class="ui container">
 | 
				
			||||||
 | 
									{{template "base/alert" .}}
 | 
				
			||||||
 | 
									<h4 class="ui top attached header">
 | 
				
			||||||
 | 
										{{.i18n.Tr "auth.openid_connect_title"}}
 | 
				
			||||||
 | 
									</h4>
 | 
				
			||||||
 | 
									<div class="ui attached segment">
 | 
				
			||||||
 | 
										<p>
 | 
				
			||||||
 | 
											{{.i18n.Tr "auth.openid_connect_desc"}}
 | 
				
			||||||
 | 
										</p>
 | 
				
			||||||
 | 
										<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
 | 
										{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
										<div class="required inline field {{if .Err_UserName}}error{{end}}">
 | 
				
			||||||
 | 
											<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
 | 
				
			||||||
 | 
											<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="required inline field {{if .Err_Password}}error{{end}}">
 | 
				
			||||||
 | 
											<label for="password">{{.i18n.Tr "password"}}</label>
 | 
				
			||||||
 | 
											<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="inline field">
 | 
				
			||||||
 | 
											OpenID: {{ .OpenID }}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										{{if .EnableCaptcha}}
 | 
				
			||||||
 | 
											<div class="inline field">
 | 
				
			||||||
 | 
												<label></label>
 | 
				
			||||||
 | 
												{{.Captcha.CreateHtml}}
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="required inline field {{if .Err_Captcha}}error{{end}}">
 | 
				
			||||||
 | 
												<label for="captcha">{{.i18n.Tr "captcha"}}</label>
 | 
				
			||||||
 | 
												<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
 | 
										<div class="inline field">
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
											<button class="ui green button">{{.i18n.Tr "auth.openid_connect_submit"}}</button>
 | 
				
			||||||
 | 
											<a href="{{AppSubUrl}}/user/forgot_password">{{.i18n.Tr "auth.forgot_password"}}</a>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										</form>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
							
								
								
									
										11
									
								
								templates/user/auth/signup_openid_navbar.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								templates/user/auth/signup_openid_navbar.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
 | 
				
			||||||
 | 
						<a class="{{if .PageIsOpenIDConnect}}active{{end}} item" href="{{AppSubUrl}}/user/openid/connect">
 | 
				
			||||||
 | 
							{{.i18n.Tr "auth.openid_connect_title"}}
 | 
				
			||||||
 | 
						</a>
 | 
				
			||||||
 | 
						{{if .EnableOpenIDSignUp}}
 | 
				
			||||||
 | 
							<a class="{{if .PageIsOpenIDRegister}}active{{end}} item" href="{{AppSubUrl}}/user/openid/register">
 | 
				
			||||||
 | 
								{{.i18n.Tr "auth.openid_register_title"}}
 | 
				
			||||||
 | 
							</a>
 | 
				
			||||||
 | 
						{{end}}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								templates/user/auth/signup_openid_register.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								templates/user/auth/signup_openid_register.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="user signup">
 | 
				
			||||||
 | 
						{{template "user/auth/signup_openid_navbar" .}}
 | 
				
			||||||
 | 
						<div class="ui container">
 | 
				
			||||||
 | 
									{{template "base/alert" .}}
 | 
				
			||||||
 | 
									<h4 class="ui top attached header">
 | 
				
			||||||
 | 
										{{.i18n.Tr "auth.openid_register_title"}}
 | 
				
			||||||
 | 
									</h4>
 | 
				
			||||||
 | 
									<div class="ui attached segment">
 | 
				
			||||||
 | 
										<p>
 | 
				
			||||||
 | 
											{{.i18n.Tr "auth.openid_register_desc"}}
 | 
				
			||||||
 | 
										</p>
 | 
				
			||||||
 | 
										<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
 | 
										{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
										<div class="required inline field {{if .Err_UserName}}error{{end}}">
 | 
				
			||||||
 | 
											<label for="user_name">{{.i18n.Tr "username"}}</label>
 | 
				
			||||||
 | 
											<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="required inline field {{if .Err_Email}}error{{end}}">
 | 
				
			||||||
 | 
											<label for="email">{{.i18n.Tr "email"}}</label>
 | 
				
			||||||
 | 
											<input id="email" name="email" type="email" value="{{.email}}" required>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="inline field">
 | 
				
			||||||
 | 
											OpenID: {{ .OpenID }}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="inline field">
 | 
				
			||||||
 | 
											<label></label>
 | 
				
			||||||
 | 
											<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										</form>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
@@ -11,6 +11,11 @@
 | 
				
			|||||||
	<a class="{{if .PageIsSettingsEmails}}active{{end}} item" href="{{AppSubUrl}}/user/settings/email">
 | 
						<a class="{{if .PageIsSettingsEmails}}active{{end}} item" href="{{AppSubUrl}}/user/settings/email">
 | 
				
			||||||
		{{.i18n.Tr "settings.emails"}}
 | 
							{{.i18n.Tr "settings.emails"}}
 | 
				
			||||||
	</a>
 | 
						</a>
 | 
				
			||||||
 | 
						{{if .EnableOpenIDSignIn}}
 | 
				
			||||||
 | 
							<a class="{{if .PageIsSettingsOpenID}}active{{end}} item" href="{{AppSubUrl}}/user/settings/openid">
 | 
				
			||||||
 | 
								OpenID
 | 
				
			||||||
 | 
							</a>
 | 
				
			||||||
 | 
						{{end}}
 | 
				
			||||||
	<a class="{{if .PageIsSettingsSSHKeys}}active{{end}} item" href="{{AppSubUrl}}/user/settings/ssh">
 | 
						<a class="{{if .PageIsSettingsSSHKeys}}active{{end}} item" href="{{AppSubUrl}}/user/settings/ssh">
 | 
				
			||||||
		{{.i18n.Tr "settings.ssh_keys"}}
 | 
							{{.i18n.Tr "settings.ssh_keys"}}
 | 
				
			||||||
	</a>
 | 
						</a>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								templates/user/settings/openid.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								templates/user/settings/openid.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					{{template "base/head" .}}
 | 
				
			||||||
 | 
					<div class="user settings openid">
 | 
				
			||||||
 | 
						<div class="ui container">
 | 
				
			||||||
 | 
							<div class="ui grid">
 | 
				
			||||||
 | 
								{{template "user/settings/navbar" .}}
 | 
				
			||||||
 | 
								<div class="twelve wide column content">
 | 
				
			||||||
 | 
									{{template "base/alert" .}}
 | 
				
			||||||
 | 
									<h4 class="ui top attached header">
 | 
				
			||||||
 | 
										{{.i18n.Tr "settings.manage_openid"}}
 | 
				
			||||||
 | 
									</h4>
 | 
				
			||||||
 | 
									<div class="ui attached segment">
 | 
				
			||||||
 | 
										<div class="ui openid list">
 | 
				
			||||||
 | 
											<div class="item">
 | 
				
			||||||
 | 
												{{.i18n.Tr "settings.openid_desc"}}
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											{{range .OpenIDs}}
 | 
				
			||||||
 | 
												<div class="item ui grid">
 | 
				
			||||||
 | 
													<div class="column">
 | 
				
			||||||
 | 
														<strong>{{.URI}}</strong>
 | 
				
			||||||
 | 
														<div class="ui right">
 | 
				
			||||||
 | 
															<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 | 
				
			||||||
 | 
																{{$.i18n.Tr "settings.delete_key"}}
 | 
				
			||||||
 | 
															</button>
 | 
				
			||||||
 | 
														</div>
 | 
				
			||||||
 | 
													</div>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											{{end}}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="ui attached bottom segment">
 | 
				
			||||||
 | 
										<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
 | 
											{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
											<div class="required field {{if .Err_OpenID}}error{{end}}">
 | 
				
			||||||
 | 
												<label for="openid">{{.i18n.Tr "settings.add_new_openid"}}</label>
 | 
				
			||||||
 | 
												<input id="openid" name="openid" type="openid" autofocus required>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<button class="ui green button">
 | 
				
			||||||
 | 
												{{.i18n.Tr "settings.add_openid"}}
 | 
				
			||||||
 | 
											</button>
 | 
				
			||||||
 | 
										</form>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="ui small basic delete modal">
 | 
				
			||||||
 | 
						<div class="ui icon header">
 | 
				
			||||||
 | 
							<i class="trash icon"></i>
 | 
				
			||||||
 | 
							{{.i18n.Tr "settings.openid_deletion"}}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="content">
 | 
				
			||||||
 | 
							<p>{{.i18n.Tr "settings.openid_deletion_desc"}}</p>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						{{template "base/delete_modal_actions" .}}
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
							
								
								
									
										13
									
								
								vendor/github.com/yohcop/openid-go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/yohcop/openid-go/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					Copyright 2015 Yohann Coppel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
							
								
								
									
										38
									
								
								vendor/github.com/yohcop/openid-go/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/yohcop/openid-go/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					# openid.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a consumer (Relying party) implementation of OpenId 2.0,
 | 
				
			||||||
 | 
					written in Go.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go get -u github.com/yohcop/openid-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[](https://travis-ci.org/yohcop/openid-go)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Github
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Be awesome! Feel free to clone and use according to the licence.
 | 
				
			||||||
 | 
					If you make a useful change that can benefit others, send a
 | 
				
			||||||
 | 
					pull request! This ensures that one version has all the good stuff
 | 
				
			||||||
 | 
					and doesn't fall behind.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Code example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See `_example/` for a simple webserver using the openID
 | 
				
			||||||
 | 
					implementation. Also, read the comment about the NonceStore towards
 | 
				
			||||||
 | 
					the top of that file. The example must be run for the openid-go
 | 
				
			||||||
 | 
					directory, like so:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    go run _example/server.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## App Engine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to use this on Google App Engine, you need to create an instance with a custom `*http.Client` provided by [urlfetch](https://cloud.google.com/appengine/docs/go/urlfetch/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					oid := openid.NewOpenID(urlfetch.Client(appengine.NewContext(r)))
 | 
				
			||||||
 | 
					oid.RedirectURL(...)
 | 
				
			||||||
 | 
					oid.Verify(...)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Distributed under the [Apache v2.0 license](http://www.apache.org/licenses/LICENSE-2.0.html).
 | 
				
			||||||
							
								
								
									
										57
									
								
								vendor/github.com/yohcop/openid-go/discover.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/yohcop/openid-go/discover.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 7.3.1.  Discovered Information
 | 
				
			||||||
 | 
					// Upon successful completion of discovery, the Relying Party will
 | 
				
			||||||
 | 
					// have one or more sets of the following information (see the
 | 
				
			||||||
 | 
					// Terminology section for definitions). If more than one set of the
 | 
				
			||||||
 | 
					// following information has been discovered, the precedence rules
 | 
				
			||||||
 | 
					// defined in [XRI_Resolution_2.0] are to be applied.
 | 
				
			||||||
 | 
					//   - OP Endpoint URL
 | 
				
			||||||
 | 
					//   - Protocol Version
 | 
				
			||||||
 | 
					// If the end user did not enter an OP Identifier, the following
 | 
				
			||||||
 | 
					// information will also be present:
 | 
				
			||||||
 | 
					//   - Claimed Identifier
 | 
				
			||||||
 | 
					//   - OP-Local Identifier
 | 
				
			||||||
 | 
					// If the end user entered an OP Identifier, there is no Claimed
 | 
				
			||||||
 | 
					// Identifier. For the purposes of making OpenID Authentication
 | 
				
			||||||
 | 
					// requests, the value
 | 
				
			||||||
 | 
					// "http://specs.openid.net/auth/2.0/identifier_select" MUST be
 | 
				
			||||||
 | 
					// used as both the Claimed Identifier and the OP-Local Identifier
 | 
				
			||||||
 | 
					// when an OP Identifier is entered.
 | 
				
			||||||
 | 
					func Discover(id string) (opEndpoint, opLocalID, claimedID string, err error) {
 | 
				
			||||||
 | 
						return defaultInstance.Discover(id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (oid *OpenID) Discover(id string) (opEndpoint, opLocalID, claimedID string, err error) {
 | 
				
			||||||
 | 
						// From OpenID specs, 7.2: Normalization
 | 
				
			||||||
 | 
						if id, err = Normalize(id); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// From OpenID specs, 7.3: Discovery.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the identifier is an XRI, [XRI_Resolution_2.0] will yield an
 | 
				
			||||||
 | 
						// XRDS document that contains the necessary information. It
 | 
				
			||||||
 | 
						// should also be noted that Relying Parties can take advantage of
 | 
				
			||||||
 | 
						// XRI Proxy Resolvers, such as the one provided by XDI.org at
 | 
				
			||||||
 | 
						// http://www.xri.net. This will remove the need for the RPs to
 | 
				
			||||||
 | 
						// perform XRI Resolution locally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// XRI not supported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If it is a URL, the Yadis protocol [Yadis] SHALL be first
 | 
				
			||||||
 | 
						// attempted. If it succeeds, the result is again an XRDS
 | 
				
			||||||
 | 
						// document.
 | 
				
			||||||
 | 
						if opEndpoint, opLocalID, err = yadisDiscovery(id, oid.urlGetter); err != nil {
 | 
				
			||||||
 | 
							// If the Yadis protocol fails and no valid XRDS document is
 | 
				
			||||||
 | 
							// retrieved, or no Service Elements are found in the XRDS
 | 
				
			||||||
 | 
							// document, the URL is retrieved and HTML-Based discovery SHALL be
 | 
				
			||||||
 | 
							// attempted.
 | 
				
			||||||
 | 
							opEndpoint, opLocalID, claimedID, err = htmlDiscovery(id, oid.urlGetter)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										69
									
								
								vendor/github.com/yohcop/openid-go/discovery_cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								vendor/github.com/yohcop/openid-go/discovery_cache.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DiscoveredInfo interface {
 | 
				
			||||||
 | 
						OpEndpoint() string
 | 
				
			||||||
 | 
						OpLocalID() string
 | 
				
			||||||
 | 
						ClaimedID() string
 | 
				
			||||||
 | 
						// ProtocolVersion: it's always openId 2.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DiscoveryCache interface {
 | 
				
			||||||
 | 
						Put(id string, info DiscoveredInfo)
 | 
				
			||||||
 | 
						// Return a discovered info, or nil.
 | 
				
			||||||
 | 
						Get(id string) DiscoveredInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SimpleDiscoveredInfo struct {
 | 
				
			||||||
 | 
						opEndpoint string
 | 
				
			||||||
 | 
						opLocalID  string
 | 
				
			||||||
 | 
						claimedID  string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SimpleDiscoveredInfo) OpEndpoint() string {
 | 
				
			||||||
 | 
						return s.opEndpoint
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SimpleDiscoveredInfo) OpLocalID() string {
 | 
				
			||||||
 | 
						return s.opLocalID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SimpleDiscoveredInfo) ClaimedID() string {
 | 
				
			||||||
 | 
						return s.claimedID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SimpleDiscoveryCache struct {
 | 
				
			||||||
 | 
						cache map[string]DiscoveredInfo
 | 
				
			||||||
 | 
						mutex *sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSimpleDiscoveryCache() *SimpleDiscoveryCache {
 | 
				
			||||||
 | 
						return &SimpleDiscoveryCache{cache: map[string]DiscoveredInfo{}, mutex: &sync.Mutex{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SimpleDiscoveryCache) Put(id string, info DiscoveredInfo) {
 | 
				
			||||||
 | 
						s.mutex.Lock()
 | 
				
			||||||
 | 
						defer s.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.cache[id] = info
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SimpleDiscoveryCache) Get(id string) DiscoveredInfo {
 | 
				
			||||||
 | 
						s.mutex.Lock()
 | 
				
			||||||
 | 
						defer s.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if info, has := s.cache[id]; has {
 | 
				
			||||||
 | 
							return info
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func compareDiscoveredInfo(a DiscoveredInfo, opEndpoint, opLocalID, claimedID string) bool {
 | 
				
			||||||
 | 
						return a != nil &&
 | 
				
			||||||
 | 
							a.OpEndpoint() == opEndpoint &&
 | 
				
			||||||
 | 
							a.OpLocalID() == opLocalID &&
 | 
				
			||||||
 | 
							a.ClaimedID() == claimedID
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								vendor/github.com/yohcop/openid-go/getter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/yohcop/openid-go/getter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Interface that simplifies testing.
 | 
				
			||||||
 | 
					type httpGetter interface {
 | 
				
			||||||
 | 
						Get(uri string, headers map[string]string) (resp *http.Response, err error)
 | 
				
			||||||
 | 
						Post(uri string, form url.Values) (resp *http.Response, err error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type defaultGetter struct {
 | 
				
			||||||
 | 
						client *http.Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (dg *defaultGetter) Get(uri string, headers map[string]string) (resp *http.Response, err error) {
 | 
				
			||||||
 | 
						request, err := http.NewRequest("GET", uri, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for h, v := range headers {
 | 
				
			||||||
 | 
							request.Header.Add(h, v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dg.client.Do(request)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (dg *defaultGetter) Post(uri string, form url.Values) (resp *http.Response, err error) {
 | 
				
			||||||
 | 
						return dg.client.PostForm(uri, form)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										77
									
								
								vendor/github.com/yohcop/openid-go/html_discovery.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/yohcop/openid-go/html_discovery.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/net/html"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func htmlDiscovery(id string, getter httpGetter) (opEndpoint, opLocalID, claimedID string, err error) {
 | 
				
			||||||
 | 
						resp, err := getter.Get(id, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						opEndpoint, opLocalID, err = findProviderFromHeadLink(resp.Body)
 | 
				
			||||||
 | 
						return opEndpoint, opLocalID, resp.Request.URL.String(), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func findProviderFromHeadLink(input io.Reader) (opEndpoint, opLocalID string, err error) {
 | 
				
			||||||
 | 
						tokenizer := html.NewTokenizer(input)
 | 
				
			||||||
 | 
						inHead := false
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							tt := tokenizer.Next()
 | 
				
			||||||
 | 
							switch tt {
 | 
				
			||||||
 | 
							case html.ErrorToken:
 | 
				
			||||||
 | 
								// Even if the document is malformed after we found a
 | 
				
			||||||
 | 
								// valid <link> tag, ignore and let's be happy with our
 | 
				
			||||||
 | 
								// openid2.provider and potentially openid2.local_id as well.
 | 
				
			||||||
 | 
								if len(opEndpoint) > 0 {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return "", "", tokenizer.Err()
 | 
				
			||||||
 | 
							case html.StartTagToken, html.EndTagToken, html.SelfClosingTagToken:
 | 
				
			||||||
 | 
								tk := tokenizer.Token()
 | 
				
			||||||
 | 
								if tk.Data == "head" {
 | 
				
			||||||
 | 
									if tt == html.StartTagToken {
 | 
				
			||||||
 | 
										inHead = true
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										if len(opEndpoint) > 0 {
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return "", "", errors.New(
 | 
				
			||||||
 | 
											"LINK with rel=openid2.provider not found")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if inHead && tk.Data == "link" {
 | 
				
			||||||
 | 
									provider := false
 | 
				
			||||||
 | 
									localID := false
 | 
				
			||||||
 | 
									href := ""
 | 
				
			||||||
 | 
									for _, attr := range tk.Attr {
 | 
				
			||||||
 | 
										if attr.Key == "rel" {
 | 
				
			||||||
 | 
											if attr.Val == "openid2.provider" {
 | 
				
			||||||
 | 
												provider = true
 | 
				
			||||||
 | 
											} else if attr.Val == "openid2.local_id" {
 | 
				
			||||||
 | 
												localID = true
 | 
				
			||||||
 | 
											} else if attr.Val == "openid.server" {
 | 
				
			||||||
 | 
												provider = true
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else if attr.Key == "href" {
 | 
				
			||||||
 | 
											href = attr.Val
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if provider && !localID && len(href) > 0 {
 | 
				
			||||||
 | 
										opEndpoint = href
 | 
				
			||||||
 | 
									} else if !provider && localID && len(href) > 0 {
 | 
				
			||||||
 | 
										opLocalID = href
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// At this point we should probably have returned either from
 | 
				
			||||||
 | 
						// a closing </head> or a tokenizer error (no </head> found).
 | 
				
			||||||
 | 
						// But just in case.
 | 
				
			||||||
 | 
						if len(opEndpoint) > 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", "", errors.New("LINK rel=openid2.provider not found")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										87
									
								
								vendor/github.com/yohcop/openid-go/nonce_store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/yohcop/openid-go/nonce_store.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var maxNonceAge = flag.Duration("openid-max-nonce-age",
 | 
				
			||||||
 | 
						60*time.Second,
 | 
				
			||||||
 | 
						"Maximum accepted age for openid nonces. The bigger, the more"+
 | 
				
			||||||
 | 
							"memory is needed to store used nonces.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NonceStore interface {
 | 
				
			||||||
 | 
						// Returns nil if accepted, an error otherwise.
 | 
				
			||||||
 | 
						Accept(endpoint, nonce string) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Nonce struct {
 | 
				
			||||||
 | 
						T time.Time
 | 
				
			||||||
 | 
						S string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SimpleNonceStore struct {
 | 
				
			||||||
 | 
						store map[string][]*Nonce
 | 
				
			||||||
 | 
						mutex *sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewSimpleNonceStore() *SimpleNonceStore {
 | 
				
			||||||
 | 
						return &SimpleNonceStore{store: map[string][]*Nonce{}, mutex: &sync.Mutex{}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *SimpleNonceStore) Accept(endpoint, nonce string) error {
 | 
				
			||||||
 | 
						// Value: A string 255 characters or less in length, that MUST be
 | 
				
			||||||
 | 
						// unique to this particular successful authentication response.
 | 
				
			||||||
 | 
						if len(nonce) < 20 || len(nonce) > 256 {
 | 
				
			||||||
 | 
							return errors.New("Invalid nonce")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The nonce MUST start with the current time on the server, and MAY
 | 
				
			||||||
 | 
						// contain additional ASCII characters in the range 33-126 inclusive
 | 
				
			||||||
 | 
						// (printable non-whitespace characters), as necessary to make each
 | 
				
			||||||
 | 
						// response unique. The date and time MUST be formatted as specified in
 | 
				
			||||||
 | 
						// section 5.6 of [RFC3339], with the following restrictions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// All times must be in the UTC timezone, indicated with a "Z".  No
 | 
				
			||||||
 | 
						// fractional seconds are allowed For example:
 | 
				
			||||||
 | 
						// 2005-05-15T17:11:51ZUNIQUE
 | 
				
			||||||
 | 
						ts, err := time.Parse(time.RFC3339, nonce[0:20])
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						now := time.Now()
 | 
				
			||||||
 | 
						diff := now.Sub(ts)
 | 
				
			||||||
 | 
						if diff > *maxNonceAge {
 | 
				
			||||||
 | 
							return fmt.Errorf("Nonce too old: %ds", diff.Seconds())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s := nonce[20:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Meh.. now we have to use a mutex, to protect that map from
 | 
				
			||||||
 | 
						// concurrent access. Could put a go routine in charge of it
 | 
				
			||||||
 | 
						// though.
 | 
				
			||||||
 | 
						d.mutex.Lock()
 | 
				
			||||||
 | 
						defer d.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if nonces, hasOp := d.store[endpoint]; hasOp {
 | 
				
			||||||
 | 
							// Delete old nonces while we are at it.
 | 
				
			||||||
 | 
							newNonces := []*Nonce{{ts, s}}
 | 
				
			||||||
 | 
							for _, n := range nonces {
 | 
				
			||||||
 | 
								if n.T == ts && n.S == s {
 | 
				
			||||||
 | 
									// If return early, just ignore the filtered list
 | 
				
			||||||
 | 
									// we have been building so far...
 | 
				
			||||||
 | 
									return errors.New("Nonce already used")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if now.Sub(n.T) < *maxNonceAge {
 | 
				
			||||||
 | 
									newNonces = append(newNonces, n)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							d.store[endpoint] = newNonces
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							d.store[endpoint] = []*Nonce{{ts, s}}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								vendor/github.com/yohcop/openid-go/normalizer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/yohcop/openid-go/normalizer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Normalize(id string) (string, error) {
 | 
				
			||||||
 | 
						id = strings.TrimSpace(id)
 | 
				
			||||||
 | 
						if len(id) == 0 {
 | 
				
			||||||
 | 
							return "", errors.New("No id provided")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 7.2 from openID 2.0 spec.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//If the user's input starts with the "xri://" prefix, it MUST be
 | 
				
			||||||
 | 
						//stripped off, so that XRIs are used in the canonical form.
 | 
				
			||||||
 | 
						if strings.HasPrefix(id, "xri://") {
 | 
				
			||||||
 | 
							id = id[6:]
 | 
				
			||||||
 | 
							return id, errors.New("XRI identifiers not supported")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the first character of the resulting string is an XRI
 | 
				
			||||||
 | 
						// Global Context Symbol ("=", "@", "+", "$", "!") or "(", as
 | 
				
			||||||
 | 
						// defined in Section 2.2.1 of [XRI_Syntax_2.0], then the input
 | 
				
			||||||
 | 
						// SHOULD be treated as an XRI.
 | 
				
			||||||
 | 
						if b := id[0]; b == '=' || b == '@' || b == '+' || b == '$' || b == '!' {
 | 
				
			||||||
 | 
							return id, errors.New("XRI identifiers not supported")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Otherwise, the input SHOULD be treated as an http URL; if it
 | 
				
			||||||
 | 
						// does not include a "http" or "https" scheme, the Identifier
 | 
				
			||||||
 | 
						// MUST be prefixed with the string "http://". If the URL
 | 
				
			||||||
 | 
						// contains a fragment part, it MUST be stripped off together
 | 
				
			||||||
 | 
						// with the fragment delimiter character "#". See Section 11.5.2 for
 | 
				
			||||||
 | 
						// more information.
 | 
				
			||||||
 | 
						if !strings.HasPrefix(id, "http://") && !strings.HasPrefix(id,
 | 
				
			||||||
 | 
							"https://") {
 | 
				
			||||||
 | 
							id = "http://" + id
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if fragmentIndex := strings.Index(id, "#"); fragmentIndex != -1 {
 | 
				
			||||||
 | 
							id = id[0:fragmentIndex]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if u, err := url.ParseRequestURI(id); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if u.Host == "" {
 | 
				
			||||||
 | 
								return "", errors.New("Invalid address provided as id")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if u.Path == "" {
 | 
				
			||||||
 | 
								u.Path = "/"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							id = u.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// URL Identifiers MUST then be further normalized by both
 | 
				
			||||||
 | 
						// following redirects when retrieving their content and finally
 | 
				
			||||||
 | 
						// applying the rules in Section 6 of [RFC3986] to the final
 | 
				
			||||||
 | 
						// destination URL. This final URL MUST be noted by the Relying
 | 
				
			||||||
 | 
						// Party as the Claimed Identifier and be used when requesting
 | 
				
			||||||
 | 
						// authentication.
 | 
				
			||||||
 | 
						return id, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								vendor/github.com/yohcop/openid-go/openid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/yohcop/openid-go/openid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type OpenID struct {
 | 
				
			||||||
 | 
						urlGetter httpGetter
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewOpenID(client *http.Client) *OpenID {
 | 
				
			||||||
 | 
						return &OpenID{urlGetter: &defaultGetter{client: client}}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var defaultInstance = NewOpenID(http.DefaultClient)
 | 
				
			||||||
							
								
								
									
										55
									
								
								vendor/github.com/yohcop/openid-go/redirect.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/yohcop/openid-go/redirect.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RedirectURL(id, callbackURL, realm string) (string, error) {
 | 
				
			||||||
 | 
						return defaultInstance.RedirectURL(id, callbackURL, realm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (oid *OpenID) RedirectURL(id, callbackURL, realm string) (string, error) {
 | 
				
			||||||
 | 
						opEndpoint, opLocalID, claimedID, err := oid.Discover(id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return BuildRedirectURL(opEndpoint, opLocalID, claimedID, callbackURL, realm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BuildRedirectURL(opEndpoint, opLocalID, claimedID, returnTo, realm string) (string, error) {
 | 
				
			||||||
 | 
						values := make(url.Values)
 | 
				
			||||||
 | 
						values.Add("openid.ns", "http://specs.openid.net/auth/2.0")
 | 
				
			||||||
 | 
						values.Add("openid.mode", "checkid_setup")
 | 
				
			||||||
 | 
						values.Add("openid.return_to", returnTo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 9.1.  Request Parameters
 | 
				
			||||||
 | 
						// "openid.claimed_id" and "openid.identity" SHALL be either both present or both absent.
 | 
				
			||||||
 | 
						if len(claimedID) > 0 {
 | 
				
			||||||
 | 
							values.Add("openid.claimed_id", claimedID)
 | 
				
			||||||
 | 
							if len(opLocalID) > 0 {
 | 
				
			||||||
 | 
								values.Add("openid.identity", opLocalID)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// If a different OP-Local Identifier is not specified,
 | 
				
			||||||
 | 
								// the claimed identifier MUST be used as the value for openid.identity.
 | 
				
			||||||
 | 
								values.Add("openid.identity", claimedID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// 7.3.1.  Discovered Information
 | 
				
			||||||
 | 
							// If the end user entered an OP Identifier, there is no Claimed Identifier.
 | 
				
			||||||
 | 
							// For the purposes of making OpenID Authentication requests, the value
 | 
				
			||||||
 | 
							// "http://specs.openid.net/auth/2.0/identifier_select" MUST be used as both the
 | 
				
			||||||
 | 
							// Claimed Identifier and the OP-Local Identifier when an OP Identifier is entered.
 | 
				
			||||||
 | 
							values.Add("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select")
 | 
				
			||||||
 | 
							values.Add("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(realm) > 0 {
 | 
				
			||||||
 | 
							values.Add("openid.realm", realm)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if strings.Contains(opEndpoint, "?") {
 | 
				
			||||||
 | 
							return opEndpoint + "&" + values.Encode(), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return opEndpoint + "?" + values.Encode(), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										250
									
								
								vendor/github.com/yohcop/openid-go/verify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								vendor/github.com/yohcop/openid-go/verify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,250 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Verify(uri string, cache DiscoveryCache, nonceStore NonceStore) (id string, err error) {
 | 
				
			||||||
 | 
						return defaultInstance.Verify(uri, cache, nonceStore)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (oid *OpenID) Verify(uri string, cache DiscoveryCache, nonceStore NonceStore) (id string, err error) {
 | 
				
			||||||
 | 
						parsedURL, err := url.Parse(uri)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						values, err := url.ParseQuery(parsedURL.RawQuery)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 11.  Verifying Assertions
 | 
				
			||||||
 | 
						// When the Relying Party receives a positive assertion, it MUST
 | 
				
			||||||
 | 
						// verify the following before accepting the assertion:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// - The value of "openid.signed" contains all the required fields.
 | 
				
			||||||
 | 
						//   (Section 10.1)
 | 
				
			||||||
 | 
						if err = verifySignedFields(values); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// - The signature on the assertion is valid (Section 11.4)
 | 
				
			||||||
 | 
						if err = verifySignature(uri, values, oid.urlGetter); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// - The value of "openid.return_to" matches the URL of the current
 | 
				
			||||||
 | 
						//   request (Section 11.1)
 | 
				
			||||||
 | 
						if err = verifyReturnTo(parsedURL, values); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// - Discovered information matches the information in the assertion
 | 
				
			||||||
 | 
						//   (Section 11.2)
 | 
				
			||||||
 | 
						if err = oid.verifyDiscovered(parsedURL, values, cache); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// - An assertion has not yet been accepted from this OP with the
 | 
				
			||||||
 | 
						//   same value for "openid.response_nonce" (Section 11.3)
 | 
				
			||||||
 | 
						if err = verifyNonce(values, nonceStore); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If all four of these conditions are met, assertion is now
 | 
				
			||||||
 | 
						// verified. If the assertion contained a Claimed Identifier, the
 | 
				
			||||||
 | 
						// user is now authenticated with that identifier.
 | 
				
			||||||
 | 
						return values.Get("openid.claimed_id"), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 10.1. Positive Assertions
 | 
				
			||||||
 | 
					// openid.signed - Comma-separated list of signed fields.
 | 
				
			||||||
 | 
					// This entry consists of the fields without the "openid." prefix that the signature covers.
 | 
				
			||||||
 | 
					// This list MUST contain at least "op_endpoint", "return_to" "response_nonce" and "assoc_handle",
 | 
				
			||||||
 | 
					// and if present in the response, "claimed_id" and "identity".
 | 
				
			||||||
 | 
					func verifySignedFields(vals url.Values) error {
 | 
				
			||||||
 | 
						ok := map[string]bool{
 | 
				
			||||||
 | 
							"op_endpoint":    false,
 | 
				
			||||||
 | 
							"return_to":      false,
 | 
				
			||||||
 | 
							"response_nonce": false,
 | 
				
			||||||
 | 
							"assoc_handle":   false,
 | 
				
			||||||
 | 
							"claimed_id":     vals.Get("openid.claimed_id") == "",
 | 
				
			||||||
 | 
							"identity":       vals.Get("openid.identity") == "",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						signed := strings.Split(vals.Get("openid.signed"), ",")
 | 
				
			||||||
 | 
						for _, sf := range signed {
 | 
				
			||||||
 | 
							ok[sf] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, v := range ok {
 | 
				
			||||||
 | 
							if !v {
 | 
				
			||||||
 | 
								return fmt.Errorf("%v must be signed but isn't", k)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 11.1.  Verifying the Return URL
 | 
				
			||||||
 | 
					// To verify that the "openid.return_to" URL matches the URL that is processing this assertion:
 | 
				
			||||||
 | 
					// - The URL scheme, authority, and path MUST be the same between the two
 | 
				
			||||||
 | 
					//   URLs.
 | 
				
			||||||
 | 
					// - Any query parameters that are present in the "openid.return_to" URL
 | 
				
			||||||
 | 
					//   MUST also be present with the same values in the URL of the HTTP
 | 
				
			||||||
 | 
					//   request the RP received.
 | 
				
			||||||
 | 
					func verifyReturnTo(uri *url.URL, vals url.Values) error {
 | 
				
			||||||
 | 
						returnTo := vals.Get("openid.return_to")
 | 
				
			||||||
 | 
						rp, err := url.Parse(returnTo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if uri.Scheme != rp.Scheme ||
 | 
				
			||||||
 | 
							uri.Host != rp.Host ||
 | 
				
			||||||
 | 
							uri.Path != rp.Path {
 | 
				
			||||||
 | 
							return errors.New(
 | 
				
			||||||
 | 
								"Scheme, host or path don't match in return_to URL")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						qp, err := url.ParseQuery(rp.RawQuery)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return compareQueryParams(qp, vals)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Any parameter in q1 must also be present in q2, and values must match.
 | 
				
			||||||
 | 
					func compareQueryParams(q1, q2 url.Values) error {
 | 
				
			||||||
 | 
						for k := range q1 {
 | 
				
			||||||
 | 
							v1 := q1.Get(k)
 | 
				
			||||||
 | 
							v2 := q2.Get(k)
 | 
				
			||||||
 | 
							if v1 != v2 {
 | 
				
			||||||
 | 
								return fmt.Errorf(
 | 
				
			||||||
 | 
									"URLs query params don't match: Param %s different: %s vs %s",
 | 
				
			||||||
 | 
									k, v1, v2)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (oid *OpenID) verifyDiscovered(uri *url.URL, vals url.Values, cache DiscoveryCache) error {
 | 
				
			||||||
 | 
						version := vals.Get("openid.ns")
 | 
				
			||||||
 | 
						if version != "http://specs.openid.net/auth/2.0" {
 | 
				
			||||||
 | 
							return errors.New("Bad protocol version")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						endpoint := vals.Get("openid.op_endpoint")
 | 
				
			||||||
 | 
						if len(endpoint) == 0 {
 | 
				
			||||||
 | 
							return errors.New("missing openid.op_endpoint url param")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						localID := vals.Get("openid.identity")
 | 
				
			||||||
 | 
						if len(localID) == 0 {
 | 
				
			||||||
 | 
							return errors.New("no localId to verify")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						claimedID := vals.Get("openid.claimed_id")
 | 
				
			||||||
 | 
						if len(claimedID) == 0 {
 | 
				
			||||||
 | 
							// If no Claimed Identifier is present in the response, the
 | 
				
			||||||
 | 
							// assertion is not about an identifier and the RP MUST NOT use the
 | 
				
			||||||
 | 
							// User-supplied Identifier associated with the current OpenID
 | 
				
			||||||
 | 
							// authentication transaction to identify the user. Extension
 | 
				
			||||||
 | 
							// information in the assertion MAY still be used.
 | 
				
			||||||
 | 
							// --- This library does not support this case. So claimed
 | 
				
			||||||
 | 
							//     identifier must be present.
 | 
				
			||||||
 | 
							return errors.New("no claimed_id to verify")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 11.2.  Verifying Discovered Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the Claimed Identifier in the assertion is a URL and contains a
 | 
				
			||||||
 | 
						// fragment, the fragment part and the fragment delimiter character "#"
 | 
				
			||||||
 | 
						// MUST NOT be used for the purposes of verifying the discovered
 | 
				
			||||||
 | 
						// information.
 | 
				
			||||||
 | 
						claimedIDVerify := claimedID
 | 
				
			||||||
 | 
						if fragmentIndex := strings.Index(claimedID, "#"); fragmentIndex != -1 {
 | 
				
			||||||
 | 
							claimedIDVerify = claimedID[0:fragmentIndex]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the Claimed Identifier is included in the assertion, it
 | 
				
			||||||
 | 
						// MUST have been discovered by the Relying Party and the
 | 
				
			||||||
 | 
						// information in the assertion MUST be present in the
 | 
				
			||||||
 | 
						// discovered information. The Claimed Identifier MUST NOT be an
 | 
				
			||||||
 | 
						// OP Identifier.
 | 
				
			||||||
 | 
						if discovered := cache.Get(claimedIDVerify); discovered != nil &&
 | 
				
			||||||
 | 
							discovered.OpEndpoint() == endpoint &&
 | 
				
			||||||
 | 
							discovered.OpLocalID() == localID &&
 | 
				
			||||||
 | 
							discovered.ClaimedID() == claimedIDVerify {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the Claimed Identifier was not previously discovered by the
 | 
				
			||||||
 | 
						// Relying Party (the "openid.identity" in the request was
 | 
				
			||||||
 | 
						// "http://specs.openid.net/auth/2.0/identifier_select" or a different
 | 
				
			||||||
 | 
						// Identifier, or if the OP is sending an unsolicited positive
 | 
				
			||||||
 | 
						// assertion), the Relying Party MUST perform discovery on the Claimed
 | 
				
			||||||
 | 
						// Identifier in the response to make sure that the OP is authorized to
 | 
				
			||||||
 | 
						// make assertions about the Claimed Identifier.
 | 
				
			||||||
 | 
						if ep, _, _, err := oid.Discover(claimedID); err == nil {
 | 
				
			||||||
 | 
							if ep == endpoint {
 | 
				
			||||||
 | 
								// This claimed ID points to the same endpoint, therefore this
 | 
				
			||||||
 | 
								// endpoint is authorized to make assertions about that claimed ID.
 | 
				
			||||||
 | 
								// TODO: There may be multiple endpoints found during discovery.
 | 
				
			||||||
 | 
								// They should all be checked.
 | 
				
			||||||
 | 
								cache.Put(claimedIDVerify, &SimpleDiscoveredInfo{opEndpoint: endpoint, opLocalID: localID, claimedID: claimedIDVerify})
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return errors.New("Could not verify the claimed ID")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func verifyNonce(vals url.Values, store NonceStore) error {
 | 
				
			||||||
 | 
						nonce := vals.Get("openid.response_nonce")
 | 
				
			||||||
 | 
						endpoint := vals.Get("openid.op_endpoint")
 | 
				
			||||||
 | 
						return store.Accept(endpoint, nonce)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func verifySignature(uri string, vals url.Values, getter httpGetter) error {
 | 
				
			||||||
 | 
						// To have the signature verification performed by the OP, the
 | 
				
			||||||
 | 
						// Relying Party sends a direct request to the OP. To verify the
 | 
				
			||||||
 | 
						// signature, the OP uses a private association that was generated
 | 
				
			||||||
 | 
						// when it issued the positive assertion.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 11.4.2.1.  Request Parameters
 | 
				
			||||||
 | 
						params := make(url.Values)
 | 
				
			||||||
 | 
						// openid.mode: Value: "check_authentication"
 | 
				
			||||||
 | 
						params.Add("openid.mode", "check_authentication")
 | 
				
			||||||
 | 
						// Exact copies of all fields from the authentication response,
 | 
				
			||||||
 | 
						// except for "openid.mode".
 | 
				
			||||||
 | 
						for k, vs := range vals {
 | 
				
			||||||
 | 
							if k == "openid.mode" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, v := range vs {
 | 
				
			||||||
 | 
								params.Add(k, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp, err := getter.Post(vals.Get("openid.op_endpoint"), params)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
						content, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						response := string(content)
 | 
				
			||||||
 | 
						lines := strings.Split(response, "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isValid := false
 | 
				
			||||||
 | 
						nsValid := false
 | 
				
			||||||
 | 
						for _, l := range lines {
 | 
				
			||||||
 | 
							if l == "is_valid:true" {
 | 
				
			||||||
 | 
								isValid = true
 | 
				
			||||||
 | 
							} else if l == "ns:http://specs.openid.net/auth/2.0" {
 | 
				
			||||||
 | 
								nsValid = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isValid && nsValid {
 | 
				
			||||||
 | 
							// Yay !
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return errors.New("Could not verify assertion with provider")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								vendor/github.com/yohcop/openid-go/xrds.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/yohcop/openid-go/xrds.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/xml"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: As per 11.2 in openid 2 specs, a service may have multiple
 | 
				
			||||||
 | 
					//       URIs. We don't care for discovery really, but we do care for
 | 
				
			||||||
 | 
					//       verification though.
 | 
				
			||||||
 | 
					type XrdsIdentifier struct {
 | 
				
			||||||
 | 
						Type     []string `xml:"Type"`
 | 
				
			||||||
 | 
						URI      string   `xml:"URI"`
 | 
				
			||||||
 | 
						LocalID  string   `xml:"LocalID"`
 | 
				
			||||||
 | 
						Priority int      `xml:"priority,attr"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Xrd struct {
 | 
				
			||||||
 | 
						Service []*XrdsIdentifier `xml:"Service"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type XrdsDocument struct {
 | 
				
			||||||
 | 
						XMLName xml.Name `xml:"XRDS"`
 | 
				
			||||||
 | 
						Xrd     *Xrd     `xml:"XRD"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func parseXrds(input []byte) (opEndpoint, opLocalID string, err error) {
 | 
				
			||||||
 | 
						xrdsDoc := &XrdsDocument{}
 | 
				
			||||||
 | 
						err = xml.Unmarshal(input, xrdsDoc)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if xrdsDoc.Xrd == nil {
 | 
				
			||||||
 | 
							return "", "", errors.New("XRDS document missing XRD tag")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 7.3.2.2.  Extracting Authentication Data
 | 
				
			||||||
 | 
						// Once the Relying Party has obtained an XRDS document, it
 | 
				
			||||||
 | 
						// MUST first search the document (following the rules
 | 
				
			||||||
 | 
						// described in [XRI_Resolution_2.0]) for an OP Identifier
 | 
				
			||||||
 | 
						// Element. If none is found, the RP will search for a Claimed
 | 
				
			||||||
 | 
						// Identifier Element.
 | 
				
			||||||
 | 
						for _, service := range xrdsDoc.Xrd.Service {
 | 
				
			||||||
 | 
							// 7.3.2.1.1.  OP Identifier Element
 | 
				
			||||||
 | 
							// An OP Identifier Element is an <xrd:Service> element with the
 | 
				
			||||||
 | 
							// following information:
 | 
				
			||||||
 | 
							// An <xrd:Type> tag whose text content is
 | 
				
			||||||
 | 
							//     "http://specs.openid.net/auth/2.0/server".
 | 
				
			||||||
 | 
							// An <xrd:URI> tag whose text content is the OP Endpoint URL
 | 
				
			||||||
 | 
							if service.hasType("http://specs.openid.net/auth/2.0/server") {
 | 
				
			||||||
 | 
								opEndpoint = strings.TrimSpace(service.URI)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, service := range xrdsDoc.Xrd.Service {
 | 
				
			||||||
 | 
							// 7.3.2.1.2.  Claimed Identifier Element
 | 
				
			||||||
 | 
							// A Claimed Identifier Element is an <xrd:Service> element
 | 
				
			||||||
 | 
							// with the following information:
 | 
				
			||||||
 | 
							// An <xrd:Type> tag whose text content is
 | 
				
			||||||
 | 
							//     "http://specs.openid.net/auth/2.0/signon".
 | 
				
			||||||
 | 
							// An <xrd:URI> tag whose text content is the OP Endpoint
 | 
				
			||||||
 | 
							//     URL.
 | 
				
			||||||
 | 
							// An <xrd:LocalID> tag (optional) whose text content is the
 | 
				
			||||||
 | 
							//     OP-Local Identifier.
 | 
				
			||||||
 | 
							if service.hasType("http://specs.openid.net/auth/2.0/signon") {
 | 
				
			||||||
 | 
								opEndpoint = strings.TrimSpace(service.URI)
 | 
				
			||||||
 | 
								opLocalID = strings.TrimSpace(service.LocalID)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", "", errors.New("Could not find a compatible service")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (xrdsi *XrdsIdentifier) hasType(tpe string) bool {
 | 
				
			||||||
 | 
						for _, t := range xrdsi.Type {
 | 
				
			||||||
 | 
							if t == tpe {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										119
									
								
								vendor/github.com/yohcop/openid-go/yadis_discovery.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								vendor/github.com/yohcop/openid-go/yadis_discovery.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					package openid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/net/html"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var yadisHeaders = map[string]string{
 | 
				
			||||||
 | 
						"Accept": "application/xrds+xml"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func yadisDiscovery(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) {
 | 
				
			||||||
 | 
						// Section 6.2.4 of Yadis 1.0 specifications.
 | 
				
			||||||
 | 
						// The Yadis Protocol is initiated by the Relying Party Agent
 | 
				
			||||||
 | 
						// with an initial HTTP request using the Yadis URL.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This request MUST be either a GET or a HEAD request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// A GET or HEAD request MAY include an HTTP Accept
 | 
				
			||||||
 | 
						// request-header (HTTP 14.1) specifying MIME media type,
 | 
				
			||||||
 | 
						// application/xrds+xml.
 | 
				
			||||||
 | 
						resp, err := getter.Get(id, yadisHeaders)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Section 6.2.5 from Yadis 1.0 spec: Response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contentType := resp.Header.Get("Content-Type")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The response MUST be one of:
 | 
				
			||||||
 | 
						// (see 6.2.6 for precedence)
 | 
				
			||||||
 | 
						if l := resp.Header.Get("X-XRDS-Location"); l != "" {
 | 
				
			||||||
 | 
							// 2. HTTP response-headers that include an X-XRDS-Location
 | 
				
			||||||
 | 
							// response-header, together with a document
 | 
				
			||||||
 | 
							return getYadisResourceDescriptor(l, getter)
 | 
				
			||||||
 | 
						} else if strings.Contains(contentType, "text/html") {
 | 
				
			||||||
 | 
							// 1. An HTML document with a <head> element that includes a
 | 
				
			||||||
 | 
							// <meta> element with http-equiv attribute, X-XRDS-Location,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							metaContent, err := findMetaXrdsLocation(resp.Body)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return getYadisResourceDescriptor(metaContent, getter)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						} else if strings.Contains(contentType, "application/xrds+xml") {
 | 
				
			||||||
 | 
							// 4. A document of MIME media type, application/xrds+xml.
 | 
				
			||||||
 | 
							body, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return parseXrds(body)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// 3. HTTP response-headers only, which MAY include an
 | 
				
			||||||
 | 
						// X-XRDS-Location response-header, a content-type
 | 
				
			||||||
 | 
						// response-header specifying MIME media type,
 | 
				
			||||||
 | 
						// application/xrds+xml, or both.
 | 
				
			||||||
 | 
						//   (this is handled by one of the 2 previous if statements)
 | 
				
			||||||
 | 
						return "", "", errors.New("No expected header, or content type")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Similar as above, but we expect an absolute Yadis document URL.
 | 
				
			||||||
 | 
					func getYadisResourceDescriptor(id string, getter httpGetter) (opEndpoint string, opLocalID string, err error) {
 | 
				
			||||||
 | 
						resp, err := getter.Get(id, yadisHeaders)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
						// 4. A document of MIME media type, application/xrds+xml.
 | 
				
			||||||
 | 
						body, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return parseXrds(body)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", "", err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Search for
 | 
				
			||||||
 | 
					// <head>
 | 
				
			||||||
 | 
					//    <meta http-equiv="X-XRDS-Location" content="....">
 | 
				
			||||||
 | 
					func findMetaXrdsLocation(input io.Reader) (location string, err error) {
 | 
				
			||||||
 | 
						tokenizer := html.NewTokenizer(input)
 | 
				
			||||||
 | 
						inHead := false
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							tt := tokenizer.Next()
 | 
				
			||||||
 | 
							switch tt {
 | 
				
			||||||
 | 
							case html.ErrorToken:
 | 
				
			||||||
 | 
								return "", tokenizer.Err()
 | 
				
			||||||
 | 
							case html.StartTagToken, html.EndTagToken:
 | 
				
			||||||
 | 
								tk := tokenizer.Token()
 | 
				
			||||||
 | 
								if tk.Data == "head" {
 | 
				
			||||||
 | 
									if tt == html.StartTagToken {
 | 
				
			||||||
 | 
										inHead = true
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										return "", errors.New("Meta X-XRDS-Location not found")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if inHead && tk.Data == "meta" {
 | 
				
			||||||
 | 
									ok := false
 | 
				
			||||||
 | 
									content := ""
 | 
				
			||||||
 | 
									for _, attr := range tk.Attr {
 | 
				
			||||||
 | 
										if attr.Key == "http-equiv" &&
 | 
				
			||||||
 | 
											strings.ToLower(attr.Val) == "x-xrds-location" {
 | 
				
			||||||
 | 
											ok = true
 | 
				
			||||||
 | 
										} else if attr.Key == "content" {
 | 
				
			||||||
 | 
											content = attr.Val
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if ok && len(content) > 0 {
 | 
				
			||||||
 | 
										return content, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "", errors.New("Meta X-XRDS-Location not found")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							@@ -1163,6 +1163,12 @@
 | 
				
			|||||||
			"revision": "b8a2a83acfe6e6770b75de42d5ff4c67596675c0",
 | 
								"revision": "b8a2a83acfe6e6770b75de42d5ff4c67596675c0",
 | 
				
			||||||
			"revisionTime": "2017-01-13T19:21:00Z"
 | 
								"revisionTime": "2017-01-13T19:21:00Z"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
								"checksumSHA1": "pkrINpw0HkmO+18SdtSjje9MB9g=",
 | 
				
			||||||
 | 
								"path": "github.com/yohcop/openid-go",
 | 
				
			||||||
 | 
								"revision": "2c050d2dae5345c417db301f11fda6fbf5ad0f0a",
 | 
				
			||||||
 | 
								"revisionTime": "2016-09-14T08:04:27Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "dwOedwBJ1EIK9+S3t108Bx054Y8=",
 | 
								"checksumSHA1": "dwOedwBJ1EIK9+S3t108Bx054Y8=",
 | 
				
			||||||
			"path": "golang.org/x/crypto/curve25519",
 | 
								"path": "golang.org/x/crypto/curve25519",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user