Skip to content

Commit

Permalink
logged-in page: add agent info to session list
Browse files Browse the repository at this point in the history
  • Loading branch information
equinox0815 committed Nov 19, 2023
1 parent 09fe162 commit cb41766
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 22 deletions.
11 changes: 10 additions & 1 deletion cmd/whawty-nginx-sso/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (

"github.com/flosch/pongo2/v6"
"github.com/gin-gonic/gin"
"github.com/mileusna/useragent"
"github.com/whawty/nginx-sso/auth"
"github.com/whawty/nginx-sso/cookie"
"github.com/whawty/nginx-sso/ui"
Expand Down Expand Up @@ -142,7 +143,8 @@ func (h *HandlerContext) handleLoginPost(c *gin.Context) {
return
}

value, opts, err := h.cookies.New(username)
ua := useragent.Parse(c.GetHeader("User-Agent"))
value, opts, err := h.cookies.New(username, cookie.AgentInfo{Name: ua.Name, OS: ua.OS})
if err != nil {
c.HTML(http.StatusBadRequest, "login.htmpl", pongo2.Context{
"login": login,
Expand All @@ -160,6 +162,13 @@ func (h *HandlerContext) handleLoginPost(c *gin.Context) {
}

func (h *HandlerContext) handleLogout(c *gin.Context) {
id, _ := c.GetQuery("id")
if id != "" {
// TODO: implement this!
c.JSON(http.StatusNotImplemented, WebError{"logout of other sessions is not yet implemented"})
return
}

session, err := h.verifyCookie(c)
if err == nil {
if err = h.cookies.Revoke(*session); err != nil {
Expand Down
19 changes: 12 additions & 7 deletions cookie/backend_in-memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,27 @@ import (
type InMemoryBackendConfig struct {
}

type InMemorySessionMap map[ulid.ULID]SessionBase
type InMemorySession struct {
SessionBase
Agent AgentInfo `json:"agent"`
}

type InMemorySessionMap map[ulid.ULID]InMemorySession

type InMemoryBackend struct {
mutex sync.RWMutex
sessions map[string]InMemorySessionMap
revoked InMemorySessionMap
revoked map[ulid.ULID]SessionBase
}

func NewInMemoryBackend(conf *InMemoryBackendConfig) (*InMemoryBackend, error) {
m := &InMemoryBackend{}
m.sessions = make(map[string]InMemorySessionMap)
m.revoked = make(InMemorySessionMap)
m.revoked = make(map[ulid.ULID]SessionBase)
return m, nil
}

func (b *InMemoryBackend) Save(session Session) error {
func (b *InMemoryBackend) Save(session SessionFull) error {
b.mutex.Lock()
defer b.mutex.Unlock()

Expand All @@ -67,11 +72,11 @@ func (b *InMemoryBackend) Save(session Session) error {
if _, exists = sessions[session.ID]; exists {
return fmt.Errorf("session '%v' already exists!", session.ID)
}
sessions[session.ID] = session.SessionBase
sessions[session.ID] = InMemorySession{SessionBase: session.SessionBase, Agent: session.Agent}
return nil
}

func (b *InMemoryBackend) ListUser(username string) (list SessionList, err error) {
func (b *InMemoryBackend) ListUser(username string) (list SessionFullList, err error) {
b.mutex.RLock()
defer b.mutex.RUnlock()

Expand All @@ -81,7 +86,7 @@ func (b *InMemoryBackend) ListUser(username string) (list SessionList, err error
}
for id, session := range sessions {
if _, revoked := b.revoked[id]; !revoked {
list = append(list, Session{ID: id, SessionBase: session})
list = append(list, SessionFull{Session: Session{ID: id, SessionBase: session.SessionBase}, Agent: session.Agent})
}
}
return
Expand Down
38 changes: 33 additions & 5 deletions cookie/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,42 @@ func (l SessionList) MarshalJSON() ([]byte, error) {
return json.Marshal(tmp)
}

type AgentInfo struct {
Name string `json:"name"`
OS string `json:"os"`
}

type SessionFull struct {
Session
Agent AgentInfo `json:"agent"`
}

func (s *SessionFull) CreatedAt() time.Time {
return s.Session.CreatedAt()
}

func (s *SessionFull) ExpiresAt() time.Time {
return s.Session.ExpiresAt()
}

type SessionFullList []SessionFull

func (l SessionFullList) MarshalJSON() ([]byte, error) {
if len(l) == 0 {
return []byte("[]"), nil
}
var tmp []SessionFull = l
return json.Marshal(tmp)
}

type SignedRevocationList struct {
Revoked json.RawMessage `json:"revoked"`
Signature []byte `json:"signature"`
}

type StoreBackend interface {
Save(session Session) error
ListUser(username string) (SessionList, error)
Save(session SessionFull) error
ListUser(username string) (SessionFullList, error)
Revoke(session Session) error
IsRevoked(session Session) (bool, error)
ListRevoked() (SessionList, error)
Expand Down Expand Up @@ -327,7 +355,7 @@ func (st *Store) Options() (opts Options) {
return
}

func (st *Store) New(username string) (value string, opts Options, err error) {
func (st *Store) New(username string, ai AgentInfo) (value string, opts Options, err error) {
if st.signer == nil {
err = fmt.Errorf("no signing key loaded")
return
Expand All @@ -344,7 +372,7 @@ func (st *Store) New(username string) (value string, opts Options, err error) {
return
}

if err = st.backend.Save(Session{ID: id, SessionBase: s}); err != nil {
if err = st.backend.Save(SessionFull{Session: Session{ID: id, SessionBase: s}, Agent: ai}); err != nil {
return
}
st.dbgLog.Printf("successfully generated new session('%v'): %+v", id, s)
Expand Down Expand Up @@ -393,7 +421,7 @@ func (st *Store) Verify(value string) (s Session, err error) {
return
}

func (st *Store) ListUser(username string) (SessionList, error) {
func (st *Store) ListUser(username string) (SessionFullList, error) {
return st.backend.ListUser(username)
}

Expand Down
8 changes: 5 additions & 3 deletions cookie/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ func TestNew(t *testing.T) {
}

testUser := "test-user"
_, _, err = st.New(testUser)
testAgent := AgentInfo{Name: "test-agent", OS: "test-os"}
_, _, err = st.New(testUser, testAgent)
if err == nil {
t.Fatal("calling New() on verify-only store must return an error")
}
Expand All @@ -149,7 +150,7 @@ func TestNew(t *testing.T) {
if err != nil {
t.Fatal("unexpected error:", err)
}
value, opts, err := st.New(testUser)
value, opts, err := st.New(testUser, testAgent)
if err != nil {
t.Fatal("unexpected error:", err)
}
Expand Down Expand Up @@ -278,7 +279,8 @@ func TestNewThenVerifyMultipleKeys(t *testing.T) {
}

testUser := "test-user"
value, _, err := st.New(testUser)
testAgent := AgentInfo{Name: "test-agent", OS: "test-os"}
value, _, err := st.New(testUser, testAgent)
if err != nil {
t.Fatal("unexpected error:", err)
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require (
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mileusna/useragent v1.3.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mileusna/useragent v1.3.4 h1:MiuRRuvGjEie1+yZHO88UBYg8YBC/ddF6T7F56i3PCk=
github.com/mileusna/useragent v1.3.4/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down
6 changes: 6 additions & 0 deletions ui/assets/fontawesome/css/brands.min.css

Large diffs are not rendered by default.

Binary file added ui/assets/fontawesome/webfonts/fa-brands-400.ttf
Binary file not shown.
Binary file not shown.
23 changes: 17 additions & 6 deletions ui/assets/logged-in.htmpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<link href="{{ login.BasePath }}/ui/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="{{ login.BasePath }}/ui/fontawesome/css/fontawesome.min.css" rel="stylesheet">
<link href="{{ login.BasePath }}/ui/fontawesome/css/solid.min.css" rel="stylesheet">
<link href="{{ login.BasePath }}/ui/fontawesome/css/brands.min.css" rel="stylesheet">
<link href="{{ login.BasePath }}/ui/css/main.css" rel="stylesheet">
</head>
<body>
Expand Down Expand Up @@ -69,13 +70,23 @@
<div class="col-md-2"></div>
</div>
{% endif %}
{% for s in sessions %}
{% if s.ID != session.ID %}
{% for other in sessions %}
{% if other.ID != session.ID %}
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-4">{{ s.ID }}</div>
<div class="col-md-3">{{ session.CreatedAt() | time:"Mon 1.2.2006 15:04:05" }}</div>
<div class="col-md-3">{{ session.ExpiresAt() | time:"Mon 1.2.2006 15:04:05" }}</div>
<div class="col-md-2"></div>
<div class="col-md-3">
{# TODO: lookup function for fontawesome name? #}
<i class="fa-brands fa-{{ other.Agent.Name | lower }}" aria-hidden="true"></i> {{ other.Agent.Name | escape }} /
<i class="fa-brands fa-{{ other.Agent.OS | lower }}" aria-hidden="true"></i> {{ other.Agent.OS | escape }}
</div>
<div class="col-md-2"> --FIXME-- {# other.CreatedAt() | time:"Mon 1.2.2006 15:04:05" #}</div>
<div class="col-md-2"> --FIXME-- {# other.ExpiresAt() | time:"Mon 1.2.2006 15:04:05" #}</div>
<div class="col-md-2">
<form method="get" action="{{ login.BasePath }}/logout">
<input type=hidden name=id value="{{ other.ID }}">
<button type="submit" class="btn btn-danger btn-sm"><i class="fa-solid fa-right-from-bracket" aria-hidden="true"></i>&nbsp;&nbsp;Logout</button>
</form>
</div>
<div class="col-md-1"></div>
</div>
{% endif %}
Expand Down

0 comments on commit cb41766

Please sign in to comment.