Skip to content

Commit e33d113

Browse files
committed
logged-in page: add agent info to session list (WIP)
1 parent 09fe162 commit e33d113

File tree

10 files changed

+86
-22
lines changed

10 files changed

+86
-22
lines changed

cmd/whawty-nginx-sso/web.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040

4141
"github.com/flosch/pongo2/v6"
4242
"github.com/gin-gonic/gin"
43+
"github.com/mileusna/useragent"
4344
"github.com/whawty/nginx-sso/auth"
4445
"github.com/whawty/nginx-sso/cookie"
4546
"github.com/whawty/nginx-sso/ui"
@@ -142,7 +143,8 @@ func (h *HandlerContext) handleLoginPost(c *gin.Context) {
142143
return
143144
}
144145

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

162164
func (h *HandlerContext) handleLogout(c *gin.Context) {
165+
id, _ := c.GetQuery("id")
166+
if id != "" {
167+
// TODO: implement this!
168+
c.JSON(http.StatusNotImplemented, WebError{"logout of other sessions is not yet implemented"})
169+
return
170+
}
171+
163172
session, err := h.verifyCookie(c)
164173
if err == nil {
165174
if err = h.cookies.Revoke(*session); err != nil {

cookie/backend_in-memory.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,27 @@ import (
4040
type InMemoryBackendConfig struct {
4141
}
4242

43-
type InMemorySessionMap map[ulid.ULID]SessionBase
43+
type InMemorySession struct {
44+
SessionBase
45+
Agent AgentInfo `json:"agent"`
46+
}
47+
48+
type InMemorySessionMap map[ulid.ULID]InMemorySession
4449

4550
type InMemoryBackend struct {
4651
mutex sync.RWMutex
4752
sessions map[string]InMemorySessionMap
48-
revoked InMemorySessionMap
53+
revoked map[ulid.ULID]SessionBase
4954
}
5055

5156
func NewInMemoryBackend(conf *InMemoryBackendConfig) (*InMemoryBackend, error) {
5257
m := &InMemoryBackend{}
5358
m.sessions = make(map[string]InMemorySessionMap)
54-
m.revoked = make(InMemorySessionMap)
59+
m.revoked = make(map[ulid.ULID]SessionBase)
5560
return m, nil
5661
}
5762

58-
func (b *InMemoryBackend) Save(session Session) error {
63+
func (b *InMemoryBackend) Save(session SessionFull) error {
5964
b.mutex.Lock()
6065
defer b.mutex.Unlock()
6166

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

74-
func (b *InMemoryBackend) ListUser(username string) (list SessionList, err error) {
79+
func (b *InMemoryBackend) ListUser(username string) (list SessionFullList, err error) {
7580
b.mutex.RLock()
7681
defer b.mutex.RUnlock()
7782

@@ -81,7 +86,7 @@ func (b *InMemoryBackend) ListUser(username string) (list SessionList, err error
8186
}
8287
for id, session := range sessions {
8388
if _, revoked := b.revoked[id]; !revoked {
84-
list = append(list, Session{ID: id, SessionBase: session})
89+
list = append(list, SessionFull{Session: Session{ID: id, SessionBase: session.SessionBase}, Agent: session.Agent})
8590
}
8691
}
8792
return

cookie/store.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,42 @@ func (l SessionList) MarshalJSON() ([]byte, error) {
9393
return json.Marshal(tmp)
9494
}
9595

96+
type AgentInfo struct {
97+
Name string `json:"name"`
98+
OS string `json:"os"`
99+
}
100+
101+
type SessionFull struct {
102+
Session
103+
Agent AgentInfo `json:"agent"`
104+
}
105+
106+
func (s *SessionFull) CreatedAt() time.Time {
107+
return s.Session.CreatedAt()
108+
}
109+
110+
func (s *SessionFull) ExpiresAt() time.Time {
111+
return s.Session.ExpiresAt()
112+
}
113+
114+
type SessionFullList []SessionFull
115+
116+
func (l SessionFullList) MarshalJSON() ([]byte, error) {
117+
if len(l) == 0 {
118+
return []byte("[]"), nil
119+
}
120+
var tmp []SessionFull = l
121+
return json.Marshal(tmp)
122+
}
123+
96124
type SignedRevocationList struct {
97125
Revoked json.RawMessage `json:"revoked"`
98126
Signature []byte `json:"signature"`
99127
}
100128

101129
type StoreBackend interface {
102-
Save(session Session) error
103-
ListUser(username string) (SessionList, error)
130+
Save(session SessionFull) error
131+
ListUser(username string) (SessionFullList, error)
104132
Revoke(session Session) error
105133
IsRevoked(session Session) (bool, error)
106134
ListRevoked() (SessionList, error)
@@ -327,7 +355,7 @@ func (st *Store) Options() (opts Options) {
327355
return
328356
}
329357

330-
func (st *Store) New(username string) (value string, opts Options, err error) {
358+
func (st *Store) New(username string, ai AgentInfo) (value string, opts Options, err error) {
331359
if st.signer == nil {
332360
err = fmt.Errorf("no signing key loaded")
333361
return
@@ -344,7 +372,7 @@ func (st *Store) New(username string) (value string, opts Options, err error) {
344372
return
345373
}
346374

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

396-
func (st *Store) ListUser(username string) (SessionList, error) {
424+
func (st *Store) ListUser(username string) (SessionFullList, error) {
397425
return st.backend.ListUser(username)
398426
}
399427

cookie/store_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ func TestNew(t *testing.T) {
137137
}
138138

139139
testUser := "test-user"
140-
_, _, err = st.New(testUser)
140+
testAgent := AgentInfo{Name: "test-agent", OS: "test-os"}
141+
_, _, err = st.New(testUser, testAgent)
141142
if err == nil {
142143
t.Fatal("calling New() on verify-only store must return an error")
143144
}
@@ -149,7 +150,7 @@ func TestNew(t *testing.T) {
149150
if err != nil {
150151
t.Fatal("unexpected error:", err)
151152
}
152-
value, opts, err := st.New(testUser)
153+
value, opts, err := st.New(testUser, testAgent)
153154
if err != nil {
154155
t.Fatal("unexpected error:", err)
155156
}
@@ -278,7 +279,8 @@ func TestNewThenVerifyMultipleKeys(t *testing.T) {
278279
}
279280

280281
testUser := "test-user"
281-
value, _, err := st.New(testUser)
282+
testAgent := AgentInfo{Name: "test-agent", OS: "test-os"}
283+
value, _, err := st.New(testUser, testAgent)
282284
if err != nil {
283285
t.Fatal("unexpected error:", err)
284286
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
2626
github.com/leodido/go-urn v1.2.4 // indirect
2727
github.com/mattn/go-isatty v0.0.20 // indirect
28+
github.com/mileusna/useragent v1.3.4 // indirect
2829
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
2930
github.com/modern-go/reflect2 v1.0.2 // indirect
3031
github.com/oklog/ulid/v2 v2.1.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
6666
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
6767
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
6868
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
69+
github.com/mileusna/useragent v1.3.4 h1:MiuRRuvGjEie1+yZHO88UBYg8YBC/ddF6T7F56i3PCk=
70+
github.com/mileusna/useragent v1.3.4/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
6971
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
7072
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
7173
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

ui/assets/fontawesome/css/brands.min.css

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.

ui/assets/logged-in.htmpl

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<link href="{{ login.BasePath }}/ui/bootstrap/css/bootstrap.min.css" rel="stylesheet">
1111
<link href="{{ login.BasePath }}/ui/fontawesome/css/fontawesome.min.css" rel="stylesheet">
1212
<link href="{{ login.BasePath }}/ui/fontawesome/css/solid.min.css" rel="stylesheet">
13+
<link href="{{ login.BasePath }}/ui/fontawesome/css/brands.min.css" rel="stylesheet">
1314
<link href="{{ login.BasePath }}/ui/css/main.css" rel="stylesheet">
1415
</head>
1516
<body>
@@ -69,13 +70,23 @@
6970
<div class="col-md-2"></div>
7071
</div>
7172
{% endif %}
72-
{% for s in sessions %}
73-
{% if s.ID != session.ID %}
73+
{% for other in sessions %}
74+
{% if other.ID != session.ID %}
7475
<div class="row">
75-
<div class="col-md-1"></div>
76-
<div class="col-md-4">{{ s.ID }}</div>
77-
<div class="col-md-3">{{ session.CreatedAt() | time:"Mon 1.2.2006 15:04:05" }}</div>
78-
<div class="col-md-3">{{ session.ExpiresAt() | time:"Mon 1.2.2006 15:04:05" }}</div>
76+
<div class="col-md-2"></div>
77+
<div class="col-md-3">
78+
{# TODO: lookup function for fontawesome name? #}
79+
<i class="fa-brands fa-{{ other.Agent.Name | lower }}" aria-hidden="true"></i> {{ other.Agent.Name | escape }} /
80+
<i class="fa-brands fa-{{ other.Agent.OS | lower }}" aria-hidden="true"></i> {{ other.Agent.OS | escape }}
81+
</div>
82+
<div class="col-md-2"> --FIXME-- {# other.CreatedAt() | time:"Mon 1.2.2006 15:04:05" #}</div>
83+
<div class="col-md-2"> --FIXME-- {# other.ExpiresAt() | time:"Mon 1.2.2006 15:04:05" #}</div>
84+
<div class="col-md-2">
85+
<form method="get" action="{{ login.BasePath }}/logout">
86+
<input type=hidden name=id value="{{ other.ID }}">
87+
<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>
88+
</form>
89+
</div>
7990
<div class="col-md-1"></div>
8091
</div>
8192
{% endif %}

0 commit comments

Comments
 (0)