Login via OpenID-2.0 (#618)

This commit is contained in:
Sandro Santilli 2017-03-17 15:16:08 +01:00 committed by Kim "BKC" Carlbäcker
parent 0693fbfc00
commit 71d16f69ff
44 changed files with 2298 additions and 57 deletions

View 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
}

View 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")
}
}

View 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)
}

View file

@ -78,7 +78,7 @@ func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) bindi
return validate(errs, ctx.Data, f, ctx.Locale)
}
// SignInForm form for signing in
// SignInForm form for signing in with user/password
type SignInForm struct {
UserName string `binding:"Required;MaxSize(254)"`
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)
}
// 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
type AddSSHKeyForm struct {
Title string `binding:"Required;MaxSize(50)"`

View 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)
}