Skip to content

Commit de2a1e2

Browse files
feat: add session manager and update README (#6)
1 parent 2c7d1ba commit de2a1e2

6 files changed

Lines changed: 426 additions & 171 deletions

File tree

README.md

Lines changed: 116 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,90 @@ Casbin-UCON extends Casbin with UCON (Usage Control) capabilities, enabling:
1111
- **Obligation execution** for required actions
1212
- **Continuous monitoring** for ongoing authorization
1313

14+
## Prerequisites
15+
16+
- Basic knowledge of [Casbin](https://github.com/casbin/casbin) is required,
17+
since Casbin-UCON extends Casbin with session-based usage control.
18+
1419
## Installation
1520

1621
```bash
1722
go get github.com/casbin/casbin-ucon
1823
```
1924

25+
## Continuous Authorization Behavior
26+
27+
It's important to understand how continuous authorization works in Casbin-UCON:
28+
29+
1. EnforceWithSession(sessionID) performs pre-checks (pre-conditions and pre-obligations) and automatically starts monitoring for ongoing conditions and obligations.
30+
31+
2. StartMonitoring(sessionID) only starts monitoring without pre-checks.
32+
33+
3. If a session no longer satisfies the conditions, session.IfActive() will return false, and you can use session.GetStopReason() to determine why the session stopped.
34+
35+
4. Your application is responsible for handling these notifications and deciding how to terminate the session.
36+
37+
Always call StopMonitoring() to clean up resources when done.
38+
Example:
39+
40+
```go
41+
go func() {
42+
for {
43+
if !session.IfActive() {
44+
if session.GetStopReason() == ucon.NormalStopReason {
45+
// NormalStopReason means the session was stopped by user code calling StopMonitoring().
46+
break
47+
}
48+
//TODO
49+
//decide how to handle session termination yourself
50+
// For example, clean up resources, close connections, write logs, notify the frontend, etc.
51+
fmt.Printf("%s %s %s is stopped because: %s\n", session.GetSubject(), session.GetAction(), session.GetObject(),session.GetStopReason())
52+
break
53+
}
54+
time.Sleep(200 * time.Millisecond)
55+
}
56+
}()
57+
```
58+
2059
## Quick Start
2160

61+
Casbin-UCON requires standard Casbin configuration files:
62+
63+
- **model.conf**: defines the access control model (RBAC, ABAC, etc.)
64+
- **policy.csv**: defines the access policies
65+
66+
For example:
67+
68+
**model.conf**
69+
70+
```conf
71+
[request_definition]
72+
r = sub, obj, act
73+
74+
[policy_definition]
75+
p = sub, obj, act
76+
77+
[policy_effect]
78+
e = some(where (p.eft == allow))
79+
80+
[matchers]
81+
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
82+
```
83+
84+
**policy.csv**
85+
86+
```csv
87+
p, alice, document1, read
88+
```
89+
2290
```go
2391
package main
2492

2593
import (
2694
"github.com/casbin/casbin/v2"
2795
"github.com/casbin/casbin-ucon"
96+
"fmt"
97+
"time"
2898
)
2999

30100
func main() {
@@ -38,7 +108,7 @@ func main() {
38108
condition := &ucon.Condition{
39109
ID: "location_condition",
40110
Name: "location",
41-
Type: "always",
111+
Kind: "always",
42112
Expr: "office",
43113
}
44114
uconE.AddCondition(condition)
@@ -47,7 +117,7 @@ func main() {
47117
obligation := &ucon.Obligation{
48118
ID: "post_log",
49119
Name: "access_logging",
50-
Type: "post",
120+
Kind: "post",
51121
Expr: "log_level:detailed",
52122
}
53123
uconE.AddObligation(obligation)
@@ -59,16 +129,37 @@ func main() {
59129
})
60130

61131
// UCON session-based enforcement
62-
if res, err := uconE.EnforceWithSession(sessionID); res {
63-
// the session has started
64-
}else{
65-
// deny the request, show an error
132+
session, err := uconE.EnforceWithSession(sessionID)
133+
if session == nil {
134+
// refused
135+
fmt.Println("session refused because: ",err )
136+
}
137+
138+
go func() {
139+
for {
140+
if !session.IfActive() {
141+
if session.GetStopReason() == ucon.NormalStopReason {
142+
break
143+
}
144+
//TODO
145+
//decide how to handle session termination yourself
146+
// For example, clean up resources, close connections, write logs, notify the frontend, etc.
147+
fmt.Printf("%s %s %s is stopped because: %s\n", session.GetSubject(), session.GetAction(), session.GetObject(),session.GetStopReason())
148+
break
149+
}
150+
time.Sleep(200 * time.Millisecond)
66151
}
67-
/*
68-
ongoing access
69-
*/
70-
71-
// Stop the seesion
152+
}()
153+
154+
/*
155+
alice read document1
156+
157+
//you could change the attribute by:
158+
session.UpdateAttribute("location", "home")
159+
*/
160+
161+
162+
// Stop the session
72163
_ = uconE.StopMonitoring(sessionID)
73164

74165
}
@@ -78,19 +169,19 @@ func main() {
78169

79170
```go
80171
// Enhanced enforcement
81-
EnforceWithSession(sessionID string) (bool, error)
172+
EnforceWithSession(sessionID string) (*Session, error)
82173

83174
// Session management
84175
CreateSession(subject, action, object string, attributes map[string]interface{}) (string, error)
85-
GetSession(sessionID string) (*SessionImpl, error)
176+
GetSession(sessionID string) (*Session, error)
86177
UpdateSessionAttribute(sessionID string, key string, val interface{}) error
87178
RevokeSession(sessionID string) error
88179

89180
// Condition management
90-
AddCondition(condition *ConditionImpl) error
181+
AddCondition(condition *Condition) error
91182
EvaluateConditions(sessionID string) (bool, error)
92183
// Obligation management
93-
AddObligation(obligation *ObligationImpl) error
184+
AddObligation(obligation *Obligation) error
94185
ExecuteObligations(sessionID string) error
95186
ExecuteObligationsByType(sessionID string, phase string) error
96187

@@ -110,6 +201,16 @@ StopMonitoring(sessionID string) error
110201
- Foundation for conditions, obligations, and monitoring
111202
- Full Casbin compatibility
112203

204+
## Future Plans
205+
206+
- Enhanced Condition & Obligation Management – Allow more flexible and customizable conditions and obligations.
207+
208+
- Improved Session Management – Additional features for session lifecycle and attribute handling.
209+
210+
- Advanced Monitoring – Configurable monitoring options for ongoing authorization and obligations.
211+
212+
- Comprehensive Documentation & Examples – Expanded guides, usage examples, and best practices.
213+
113214
## License
114215

115216
Apache 2.0 License - see [LICENSE](LICENSE) for details.

session.go

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// Copyright 2025 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package ucon
16+
17+
import (
18+
"fmt"
19+
"sync"
20+
"time"
21+
)
22+
23+
type Session struct {
24+
id string
25+
subject string
26+
action string
27+
object string
28+
29+
attributes map[string]interface{}
30+
active bool
31+
startTime time.Time
32+
endTime time.Time
33+
stopReason string
34+
35+
mutex sync.RWMutex
36+
}
37+
38+
const (
39+
NormalStopReason = ""
40+
)
41+
42+
func (s *Session) GetId() string {
43+
return s.id
44+
}
45+
46+
func (s *Session) GetSubject() string {
47+
return s.subject
48+
}
49+
50+
func (s *Session) GetAction() string {
51+
return s.action
52+
}
53+
54+
func (s *Session) GetObject() string {
55+
return s.object
56+
}
57+
58+
func (s *Session) GetAttribute(key string) interface{} {
59+
s.mutex.RLock()
60+
defer s.mutex.RUnlock()
61+
return s.attributes[key]
62+
}
63+
64+
func (s *Session) UpdateAttribute(key string, val interface{}) error {
65+
s.mutex.Lock()
66+
defer s.mutex.Unlock()
67+
s.attributes[key] = val
68+
return nil
69+
}
70+
71+
func (s *Session) Stop(reason string) error {
72+
s.mutex.Lock()
73+
if !s.active {
74+
s.mutex.Unlock()
75+
return fmt.Errorf("session already stopped")
76+
}
77+
78+
s.active = false
79+
s.endTime = time.Now()
80+
s.stopReason = reason
81+
s.mutex.Unlock()
82+
return nil
83+
}
84+
85+
func (s *Session) IfActive() bool {
86+
s.mutex.RLock()
87+
defer s.mutex.RUnlock()
88+
return s.active
89+
}
90+
91+
func (s *Session) GetStopReason() string {
92+
return s.stopReason
93+
}
94+
95+
func (s *Session) GetStartTime() time.Time {
96+
return s.startTime
97+
}
98+
99+
func (s *Session) GetEndTime() time.Time {
100+
return s.endTime
101+
}
102+
103+
func (s *Session) GetDuration() time.Duration {
104+
if s.active {
105+
return time.Since(s.startTime)
106+
}
107+
return s.endTime.Sub(s.startTime)
108+
}
109+
110+
type SessionManager struct {
111+
sessions map[string]*Session
112+
mutex sync.RWMutex
113+
}
114+
115+
func NewSessionManager() *SessionManager {
116+
return &SessionManager{
117+
sessions: make(map[string]*Session),
118+
mutex: sync.RWMutex{},
119+
}
120+
}
121+
122+
func (sm *SessionManager) GetSessionById(id string) (*Session, error) {
123+
sm.mutex.RLock()
124+
defer sm.mutex.RUnlock()
125+
s, exists := sm.sessions[id]
126+
if !exists {
127+
return nil, fmt.Errorf("cannot find session with id %s", id)
128+
}
129+
return s, nil
130+
}
131+
132+
func (sm *SessionManager) CreateSession(sub string, act string, obj string, attributes map[string]interface{}) (string, error) {
133+
sessionID := fmt.Sprintf("session_%d", time.Now().UnixNano())
134+
session := &Session{
135+
id: sessionID,
136+
subject: sub,
137+
action: act,
138+
object: obj,
139+
active: true,
140+
attributes: attributes,
141+
startTime: time.Now(),
142+
mutex: sync.RWMutex{},
143+
}
144+
145+
sm.mutex.Lock()
146+
sm.sessions[sessionID] = session
147+
sm.mutex.Unlock()
148+
return sessionID, nil
149+
}
150+
151+
func (sm *SessionManager) UpdateSessionAttribute(sessionID string, key string, val interface{}) error {
152+
session, err := sm.GetSessionById(sessionID)
153+
if err != nil {
154+
return err
155+
}
156+
if err := session.UpdateAttribute(key, val); err != nil {
157+
return err
158+
}
159+
return nil
160+
}
161+
162+
func (sm *SessionManager) DeleteSession(sessionID string) error {
163+
sm.mutex.Lock()
164+
defer sm.mutex.Unlock()
165+
delete(sm.sessions, sessionID)
166+
return nil
167+
}

0 commit comments

Comments
 (0)