chore: add mercury
This commit is contained in:
115
ident/ident.go
Normal file
115
ident/ident.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package ident
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/oklog/ulid/v2"
|
||||
"go.sour.is/passwd"
|
||||
)
|
||||
|
||||
// Ident interface for a logged in user
|
||||
type Ident interface {
|
||||
Identity() string
|
||||
Session() *SessionInfo
|
||||
}
|
||||
|
||||
type SessionInfo struct {
|
||||
SessionID ulid.ULID
|
||||
Active bool
|
||||
}
|
||||
|
||||
func (s *SessionInfo) Session() *SessionInfo { return s }
|
||||
|
||||
// Handler handler function to read ident from HTTP request
|
||||
type Handler interface {
|
||||
ReadIdent(r *http.Request) (Ident, error)
|
||||
}
|
||||
type HandleGet interface {
|
||||
GetIdent(context.Context /* identity */, string) (Ident, error)
|
||||
}
|
||||
type HandleRegister interface {
|
||||
RegisterIdent(ctx context.Context, identity, displayName string, passwd []byte) (Ident, error)
|
||||
}
|
||||
|
||||
type source struct {
|
||||
Handler
|
||||
priority int
|
||||
}
|
||||
|
||||
var contextKey = struct{ key string }{"ident"}
|
||||
|
||||
func FromContext(ctx context.Context) Ident {
|
||||
if id, ok := ctx.Value(contextKey).(Ident); ok {
|
||||
return id
|
||||
}
|
||||
return Anonymous
|
||||
}
|
||||
|
||||
type IDM struct {
|
||||
rand io.Reader
|
||||
sources []source
|
||||
pwd *passwd.Passwd
|
||||
}
|
||||
|
||||
func NewIDM(pwd *passwd.Passwd, rand io.Reader) *IDM {
|
||||
return &IDM{pwd: pwd, rand:rand}
|
||||
}
|
||||
|
||||
func (idm *IDM) Add(p int, h Handler) {
|
||||
idm.sources = append(idm.sources, source{priority: p, Handler: h})
|
||||
sort.Slice(idm.sources, func(i, j int) bool { return idm.sources[i].priority < idm.sources[j].priority })
|
||||
}
|
||||
|
||||
func (idm *IDM) Passwd(pass, hash []byte) ([]byte, error) {
|
||||
return idm.pwd.Passwd(pass, hash)
|
||||
}
|
||||
|
||||
// ReadIdent read ident from a list of ident handlers
|
||||
func (idm *IDM) ReadIdent(r *http.Request) (Ident, error) {
|
||||
for _, source := range idm.sources {
|
||||
u, err := source.ReadIdent(r)
|
||||
if err != nil {
|
||||
return Anonymous, err
|
||||
}
|
||||
|
||||
if u.Session().Active {
|
||||
return u, err
|
||||
}
|
||||
}
|
||||
|
||||
return Anonymous, nil
|
||||
}
|
||||
|
||||
func (idm *IDM) RegisterIdent(ctx context.Context, identity, displayName string, passwd []byte) (Ident, error) {
|
||||
for _, source := range idm.sources {
|
||||
if source, ok := source.Handler.(HandleRegister); ok {
|
||||
return source.RegisterIdent(ctx, identity, displayName, passwd)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no HandleRegister source registered")
|
||||
}
|
||||
|
||||
func (idm *IDM) GetIdent(ctx context.Context, identity string) (Ident, error) {
|
||||
for _, source := range idm.sources {
|
||||
if source, ok := source.Handler.(HandleGet); ok {
|
||||
return source.GetIdent(ctx, identity)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no HandleGet source registered")
|
||||
}
|
||||
|
||||
func (idm *IDM) NewSessionInfo() (session SessionInfo, err error) {
|
||||
session.SessionID, err = ulid.New(ulid.Now(), idm.rand)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
session.Active = true
|
||||
|
||||
return session, nil
|
||||
}
|
||||
75
ident/null-user.go
Normal file
75
ident/null-user.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package ident
|
||||
|
||||
import "net/http"
|
||||
|
||||
// nullUser implements a null ident
|
||||
type nullUser struct {
|
||||
identity string
|
||||
aspect string
|
||||
displayName string
|
||||
SessionInfo
|
||||
}
|
||||
|
||||
// Anonymous is a logged out user
|
||||
var Anonymous = NewNullUser("anon", "none", "Guest User", false)
|
||||
|
||||
// NewNullUser creates a null user ident
|
||||
func NewNullUser(ident, aspect, name string, active bool) *nullUser {
|
||||
return &nullUser{ident, aspect, name, SessionInfo{Active: active}}
|
||||
}
|
||||
|
||||
func (id nullUser) String() string {
|
||||
return "id: " + id.identity + " dn: " + id.displayName
|
||||
}
|
||||
|
||||
// GetIdentity returns identity
|
||||
func (m nullUser) Identity() string {
|
||||
return m.identity
|
||||
}
|
||||
|
||||
// GetAspect returns aspect
|
||||
func (m nullUser) Aspect() string {
|
||||
return m.aspect
|
||||
}
|
||||
|
||||
// HasRole returns true if matches role
|
||||
func (m nullUser) Role(r ...string) bool {
|
||||
return m.Active
|
||||
}
|
||||
|
||||
// HasGroup returns true if matches group
|
||||
func (m nullUser) Group(g ...string) bool {
|
||||
return m.Active
|
||||
}
|
||||
|
||||
// GetGroups returns empty list
|
||||
func (m nullUser) Groups() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// GetRoles returns empty list
|
||||
func (m nullUser) Roles() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// GetMeta returns empty list
|
||||
func (m nullUser) Meta() map[string]string {
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
// IsActive returns true if active
|
||||
func (m nullUser) IsActive() bool {
|
||||
return m.Active
|
||||
}
|
||||
|
||||
// GetDisplay returns display name
|
||||
func (m nullUser) Display() string {
|
||||
return m.displayName
|
||||
}
|
||||
|
||||
// MakeHandlerFunc returns handler func
|
||||
func (m nullUser) HandlerFunc() func(r *http.Request) Ident {
|
||||
return func(r *http.Request) Ident {
|
||||
return &m
|
||||
}
|
||||
}
|
||||
360
ident/routes.go
Normal file
360
ident/routes.go
Normal file
@@ -0,0 +1,360 @@
|
||||
package ident
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/oklog/ulid/v2"
|
||||
|
||||
"go.sour.is/passwd"
|
||||
"go.sour.is/pkg/lg"
|
||||
"go.sour.is/pkg/locker"
|
||||
)
|
||||
|
||||
var (
|
||||
loginForm = func(nick string, valid bool) string {
|
||||
indicator := ""
|
||||
if !valid {
|
||||
indicator = `class="invalid"`
|
||||
}
|
||||
if nick != "" {
|
||||
nick = `value="` + nick + `"`
|
||||
}
|
||||
return `
|
||||
<form id="login" hx-post="ident/login" hx-target="#login" hx-swap="outerHTML">
|
||||
<input required id="login-identity" name="identity" type="text" ` + nick + `placeholder="Identity..." />
|
||||
<input required id="login-passwd" name="passwd" type="password" ` + indicator + ` placeholder="Password..." />
|
||||
|
||||
<button type="submit">Login</button>
|
||||
<button hx-get="ident/register">Register</button>
|
||||
</form>`
|
||||
}
|
||||
logoutForm = func(display string) string {
|
||||
return `<button id="login" hx-post="ident/logout" hx-target="#login" hx-swap="outerHTML">` + display + ` (logout)</button>`
|
||||
}
|
||||
registerForm = `
|
||||
<form id="login" hx-post="ident/register" hx-target="#login" hx-swap="outerHTML">
|
||||
<input required id="register-display" name="displayName" type="text" placeholder="Display Name..." />
|
||||
<input required id="register-identity" name="identity" type="text" placeholder="Identity..." />
|
||||
<input required id="register-passwd" name="passwd" type="password" placeholder="Password..." />
|
||||
|
||||
<button type="submit">Register</button>
|
||||
<button hx-get="ident" hx-target="#login" hx-swap="outerHTML">Close</button>
|
||||
</form>`
|
||||
)
|
||||
|
||||
type sessions map[ulid.ULID]Ident
|
||||
|
||||
type root struct {
|
||||
idm *IDM
|
||||
sessions *locker.Locked[sessions]
|
||||
}
|
||||
|
||||
func NewHTTP(idm *IDM) *root {
|
||||
sessions := make(sessions)
|
||||
return &root{
|
||||
idm: idm,
|
||||
sessions: locker.New(sessions),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *root) RegisterHTTP(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/ident", s.get)
|
||||
mux.HandleFunc("/ident/register", s.register)
|
||||
mux.HandleFunc("/ident/login", s.login)
|
||||
mux.HandleFunc("/ident/logout", s.logout)
|
||||
}
|
||||
func (s *root) RegisterAPIv1(mux *http.ServeMux) {
|
||||
mux.HandleFunc("POST /ident", s.registerV1)
|
||||
mux.HandleFunc("POST /ident/session", s.loginV1)
|
||||
mux.HandleFunc("DELETE /ident/session", s.logoutV1)
|
||||
mux.HandleFunc("GET /ident", s.getV1)
|
||||
}
|
||||
func (s *root) RegisterMiddleware(hdlr http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
cookie, err := r.Cookie("sour.is-ident")
|
||||
span.RecordError(err)
|
||||
if err != nil {
|
||||
hdlr.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
sessionID, err := ulid.Parse(cookie.Value)
|
||||
span.RecordError(err)
|
||||
|
||||
var id Ident = Anonymous
|
||||
if err == nil {
|
||||
err = s.sessions.Use(ctx, func(ctx context.Context, sessions sessions) error {
|
||||
if session, ok := sessions[sessionID]; ok {
|
||||
id = session
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
span.RecordError(err)
|
||||
|
||||
r = r.WithContext(context.WithValue(r.Context(), contextKey, id))
|
||||
|
||||
hdlr.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
func (s *root) createSession(ctx context.Context, id Ident) error {
|
||||
return s.sessions.Use(ctx, func(ctx context.Context, sessions sessions) error {
|
||||
sessions[id.Session().SessionID] = id
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func (s *root) destroySession(ctx context.Context, id Ident) error {
|
||||
session := id.Session()
|
||||
session.Active = false
|
||||
|
||||
return s.sessions.Use(ctx, func(ctx context.Context, sessions sessions) error {
|
||||
delete(sessions, session.SessionID)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *root) getV1(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
var id Ident = FromContext(ctx)
|
||||
if id == nil {
|
||||
http.Error(w, "NO_AUTH", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, id)
|
||||
}
|
||||
func (s *root) loginV1(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
id, err := s.idm.ReadIdent(r)
|
||||
span.RecordError(err)
|
||||
if err != nil {
|
||||
http.Error(w, "ERR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if !id.Session().Active {
|
||||
http.Error(w, "NO_AUTH", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.createSession(ctx, id)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
http.Error(w, "ERR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "sour.is-ident",
|
||||
Value: id.Session().SessionID.String(),
|
||||
Expires: time.Time{},
|
||||
Path: "/",
|
||||
Secure: false,
|
||||
HttpOnly: true,
|
||||
})
|
||||
|
||||
fmt.Fprint(w, id)
|
||||
}
|
||||
func (s *root) logoutV1(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "ERR", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
err := s.destroySession(ctx, FromContext(ctx))
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
http.Error(w, "NO_AUTH", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{Name: "sour.is-ident", MaxAge: -1})
|
||||
|
||||
http.Error(w, "GONE", http.StatusGone)
|
||||
}
|
||||
func (s *root) registerV1(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "ERR", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
r.ParseForm()
|
||||
|
||||
identity := r.Form.Get("identity")
|
||||
display := r.Form.Get("displayName")
|
||||
passwd, err := s.idm.Passwd([]byte(r.Form.Get("passwd")), nil)
|
||||
if err != nil {
|
||||
http.Error(w, "ERR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = s.idm.RegisterIdent(ctx, identity, display, passwd)
|
||||
if err != nil {
|
||||
http.Error(w, "ERR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "OK "+identity, http.StatusCreated)
|
||||
}
|
||||
|
||||
func (s *root) get(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
var id Ident = FromContext(ctx)
|
||||
if id == nil {
|
||||
http.Error(w, loginForm("", true), http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if !id.Session().Active {
|
||||
http.Error(w, loginForm("", true), http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
display := id.Identity()
|
||||
if id, ok := id.(interface{ DisplayName() string }); ok {
|
||||
display = id.DisplayName()
|
||||
}
|
||||
fmt.Fprint(w, logoutForm(display))
|
||||
}
|
||||
func (s *root) login(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
if r.Method == http.MethodGet {
|
||||
fmt.Fprint(w, loginForm("", true))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := s.idm.ReadIdent(r)
|
||||
span.RecordError(err)
|
||||
if err != nil {
|
||||
if errors.Is(err, passwd.ErrNoMatch) {
|
||||
http.Error(w, loginForm("", false), http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "ERROR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !id.Session().Active {
|
||||
http.Error(w, loginForm("", false), http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.createSession(ctx, id)
|
||||
span.RecordError(err)
|
||||
if err != nil {
|
||||
http.Error(w, "ERROR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "sour.is-ident",
|
||||
Value: id.Session().SessionID.String(),
|
||||
Expires: time.Time{},
|
||||
Path: "/",
|
||||
Secure: false,
|
||||
HttpOnly: true,
|
||||
})
|
||||
|
||||
display := id.Identity()
|
||||
if id, ok := id.(interface{ DisplayName() string }); ok {
|
||||
display = id.DisplayName()
|
||||
}
|
||||
fmt.Fprint(w, logoutForm(display))
|
||||
}
|
||||
func (s *root) logout(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "ERR", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{Name: "sour.is-ident", MaxAge: -1})
|
||||
|
||||
err := s.destroySession(ctx, FromContext(ctx))
|
||||
span.RecordError(err)
|
||||
if err != nil {
|
||||
http.Error(w, loginForm("", true), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, loginForm("", true))
|
||||
}
|
||||
func (s *root) register(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
if r.Method == http.MethodGet {
|
||||
fmt.Fprint(w, registerForm)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "ERR", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
identity := r.Form.Get("identity")
|
||||
display := r.Form.Get("displayName")
|
||||
|
||||
passwd, err := s.idm.Passwd([]byte(r.Form.Get("passwd")), nil)
|
||||
if err != nil {
|
||||
http.Error(w, "ERR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := s.idm.RegisterIdent(ctx, identity, display, passwd)
|
||||
if err != nil {
|
||||
http.Error(w, "ERR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !id.Session().Active {
|
||||
http.Error(w, loginForm("", false), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.createSession(ctx, id)
|
||||
span.RecordError(err)
|
||||
if err != nil {
|
||||
http.Error(w, "ERROR", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "sour.is-ident",
|
||||
Value: id.Session().SessionID.String(),
|
||||
Expires: time.Time{},
|
||||
Path: "/",
|
||||
Secure: false,
|
||||
HttpOnly: true,
|
||||
})
|
||||
|
||||
display = id.Identity()
|
||||
if id, ok := id.(interface{ DisplayName() string }); ok {
|
||||
display = id.DisplayName()
|
||||
}
|
||||
|
||||
http.Error(w, logoutForm(display), http.StatusCreated)
|
||||
}
|
||||
169
ident/source/mercury.go
Normal file
169
ident/source/mercury.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package source
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.sour.is/pkg/lg"
|
||||
"go.sour.is/pkg/mercury"
|
||||
"go.sour.is/pkg/ident"
|
||||
)
|
||||
|
||||
type registry interface {
|
||||
GetIndex(ctx context.Context, match, search string) (c mercury.Config, err error)
|
||||
GetConfig(ctx context.Context, match, search, fields string) (mercury.Config, error)
|
||||
WriteConfig(ctx context.Context, spaces mercury.Config) error
|
||||
}
|
||||
|
||||
type mercuryIdent struct {
|
||||
identity string
|
||||
display string
|
||||
passwd []byte
|
||||
ident.SessionInfo
|
||||
}
|
||||
|
||||
func (id *mercuryIdent) Identity() string { return id.identity }
|
||||
func (id *mercuryIdent) DisplayName() string { return id.display }
|
||||
func (id *mercuryIdent) Space() string { return "mercury.@" + id.identity }
|
||||
|
||||
func (id *mercuryIdent) FromConfig(cfg mercury.Config) error {
|
||||
if id == nil {
|
||||
return fmt.Errorf("nil ident")
|
||||
}
|
||||
|
||||
for _, s := range cfg {
|
||||
if !strings.HasPrefix(s.Space, "mercury.") {
|
||||
continue
|
||||
}
|
||||
if id.identity == "" {
|
||||
_, id.identity, _ = strings.Cut(s.Space, ".@")
|
||||
id.identity, _, _ = strings.Cut(id.identity, ".")
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(s.Space, ".ident"):
|
||||
id.passwd = []byte(s.FirstValue("passwd").First())
|
||||
default:
|
||||
id.display = s.FirstValue("displayName").First()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id *mercuryIdent) ToConfig() mercury.Config {
|
||||
space := id.Space()
|
||||
return mercury.Config{
|
||||
&mercury.Space{
|
||||
Space: space,
|
||||
List: []mercury.Value{
|
||||
{
|
||||
Space: space,
|
||||
Seq: 1,
|
||||
Name: "displayName",
|
||||
Values: []string{id.display},
|
||||
},
|
||||
{
|
||||
Space: space,
|
||||
Seq: 2,
|
||||
Name: "lastLogin",
|
||||
Values: []string{time.UnixMilli(int64(id.Session().SessionID.Time())).Format(time.RFC3339)},
|
||||
},
|
||||
},
|
||||
},
|
||||
&mercury.Space{
|
||||
Space: space + ".ident",
|
||||
List: []mercury.Value{
|
||||
{
|
||||
Space: space + ".ident",
|
||||
Seq: 1,
|
||||
Name: "passwd",
|
||||
Values: []string{string(id.passwd)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (id *mercuryIdent) String() string {
|
||||
return "id: " + id.identity + " sp: " + id.Space() + " dn: " + id.display // + " ps: " + string(id.passwd)
|
||||
}
|
||||
|
||||
func (id *mercuryIdent) HasRole(r ...string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type mercurySource struct {
|
||||
r registry
|
||||
idm *ident.IDM
|
||||
}
|
||||
|
||||
func NewMercury(r registry, pwd *ident.IDM) *mercurySource {
|
||||
return &mercurySource{r, pwd}
|
||||
}
|
||||
|
||||
func (s *mercurySource) ReadIdent(r *http.Request) (ident.Ident, error) {
|
||||
ctx, span := lg.Span(r.Context())
|
||||
defer span.End()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
return nil, fmt.Errorf("method not allowed")
|
||||
}
|
||||
r.ParseForm()
|
||||
id := &mercuryIdent{
|
||||
identity: r.Form.Get("identity"),
|
||||
passwd: []byte(r.Form.Get("passwd")),
|
||||
}
|
||||
|
||||
space := id.Space()
|
||||
c, err := s.r.GetConfig(ctx, "trace:"+space+".ident", "", "")
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
return id, err
|
||||
}
|
||||
var current mercuryIdent
|
||||
current.FromConfig(c)
|
||||
if len(current.passwd) == 0 {
|
||||
return nil, fmt.Errorf("not registered")
|
||||
}
|
||||
_, err = s.idm.Passwd(id.passwd, current.passwd)
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
current.SessionInfo, err = s.idm.NewSessionInfo()
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
err = s.r.WriteConfig(ctx, current.ToConfig())
|
||||
if err != nil {
|
||||
return ¤t, err
|
||||
}
|
||||
|
||||
return ¤t, nil
|
||||
}
|
||||
func (s *mercurySource) RegisterIdent(ctx context.Context, identity, display string, passwd []byte) (ident.Ident, error) {
|
||||
ctx, span := lg.Span(ctx)
|
||||
defer span.End()
|
||||
|
||||
id := &mercuryIdent{identity: identity, display: display, passwd: passwd}
|
||||
space := id.Space()
|
||||
|
||||
_, err := s.r.GetIndex(ctx, space, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id.SessionInfo, err = s.idm.NewSessionInfo()
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
err = s.r.WriteConfig(ctx, id.ToConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
Reference in New Issue
Block a user