Skip to content

Commit ce6b64d

Browse files
Integrate Casbin into middleware and remove built-in policy engine (#6)
* refactor: removed built-in policy engine * refactor: removed policy engine examples * refactor: updated tests to use casbin middleware * feat: added casbin middleware integration * refactor: removed policy engine from service and config * docs: removed policy engine references * refactor: removed generate-policy-config subcommand * refactor: removed more policy-related implementation in cmd * chore: updated JWTMiddleware tests for casbin policy * refactor: changed policy decision variable values * chore: updated go deps for JWT middleware * refactor: updated policy implementation to work properly * tests: tidied and separated policy tests * refactor: tidied code and minor changes * chore: addressed FIXME issues in PR * chore: use context key constants instead of string literals * Address linter errors --------- Signed-off-by: David Allen <davidallendj@gmail.com> Signed-off-by: David J. Allen <davidallendj@gmail.com> Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov> Co-authored-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
1 parent fa58e9b commit ce6b64d

27 files changed

+568
-4551
lines changed

README.md

Lines changed: 1 addition & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@ sequenceDiagram
4545

4646
## Features
4747

48-
- **Pluggable Policy Engine System**
49-
- **Static Policy Engine**: Simple hardcoded policy engine with configurable scopes, audiences, and permissions
50-
- **File-Based Policy Engine**: Dynamic policy engine with role-based access control (RBAC) from JSON configuration
51-
- **Extensible Architecture**: Easy to implement custom policy engines for complex authorization scenarios
52-
- **Hot Configuration Reloading**: File-based policy engine supports configuration updates without service restart
53-
- **Role-Based Access Control**: Support for user and group role mappings with fine-grained permissions
54-
5548
- **Identity Bridging**
5649
- Exchange external OIDC tokens for internal JWTs
5750
- Map external identities to internal service identities
@@ -71,108 +64,14 @@ sequenceDiagram
7164
- Scope-based authorization
7265
- Service-to-service authentication
7366
- Extensible claims handling
67+
- Casbin intergration for policy-based authorization
7468

7569
- **OIDC Provider Support**
7670
- Keycloak integration
7771
- Hydra integration
7872
- Authelia integration
7973
- Extensible provider interface
8074

81-
## Policy Engines
82-
83-
TokenSmith uses a pluggable policy engine system to determine scopes, audiences, and permissions for access tokens. This allows for flexible authorization policies that can be adapted to different use cases.
84-
85-
### Static Policy Engine
86-
87-
The static policy engine provides a simple, hardcoded approach to policy decisions. It's ideal for simple deployments or as a fallback when dynamic policies aren't needed.
88-
89-
**Features:**
90-
91-
- Fixed scopes, audiences, and permissions
92-
- Configurable token lifetime
93-
- Additional custom claims support
94-
- Zero configuration overhead
95-
96-
**Usage:**
97-
98-
```bash
99-
tokensmith serve --policy-engine static --provider hydra
100-
```
101-
102-
### File-Based Policy Engine
103-
104-
The file-based policy engine provides dynamic, role-based access control (RBAC) through JSON configuration files. It supports complex authorization scenarios with user and group role mappings.
105-
106-
**Features:**
107-
108-
- Role-based access control (RBAC)
109-
- User and group role mappings
110-
- Fine-grained permissions per role
111-
- Hot configuration reloading
112-
- Default policy fallback
113-
114-
**Usage:**
115-
116-
```bash
117-
# Generate a policy configuration file
118-
tokensmith generate-policy-config --policy-config policy.json
119-
120-
# Run with file-based policy engine
121-
tokensmith serve --policy-engine file-based --policy-config policy.json --provider hydra
122-
```
123-
124-
**Policy Configuration Example:**
125-
126-
```json
127-
{
128-
"version": "1.0.0",
129-
"default_policy": {
130-
"scopes": ["read"],
131-
"audiences": ["smd", "bss", "cloud-init"],
132-
"permissions": ["read:basic"]
133-
},
134-
"roles": {
135-
"admin": {
136-
"name": "Administrator",
137-
"description": "Full administrative access",
138-
"scopes": ["read", "write", "admin"],
139-
"audiences": ["smd", "bss", "cloud-init", "admin-service"],
140-
"permissions": ["read:all", "write:all", "admin:all"]
141-
},
142-
"user": {
143-
"name": "Regular User",
144-
"description": "Basic user access",
145-
"scopes": ["read"],
146-
"audiences": ["smd", "bss", "cloud-init"],
147-
"permissions": ["read:basic"]
148-
}
149-
},
150-
"user_role_mappings": {
151-
"adminuser": ["admin"],
152-
"regularuser": ["user"]
153-
},
154-
"group_role_mappings": {
155-
"admins": ["admin"],
156-
"users": ["user"]
157-
}
158-
}
159-
```
160-
161-
### Custom Policy Engines
162-
163-
You can implement custom policy engines by implementing the `policy.Engine` interface:
164-
165-
```go
166-
type Engine interface {
167-
EvaluatePolicy(ctx context.Context, policyCtx *PolicyContext) (*PolicyDecision, error)
168-
GetName() string
169-
GetVersion() string
170-
ValidateConfiguration() error
171-
}
172-
```
173-
174-
See the [policy package documentation](pkg/policy/README.md) for detailed implementation examples.
175-
17675
## Container Deployment
17776

17877
TokenSmith can be deployed using Docker. The following environment variables can be used to configure the service:
@@ -290,42 +189,6 @@ tokensmith generate-config --config config.json
290189

291190
- `tokensmith serve` - Start the token service
292191
- `tokensmith generate-config` - Generate a default configuration file
293-
- `tokensmith generate-policy-config` - Generate a default policy configuration file
294-
295-
#### Using Static Policy Engine (Default)
296-
297-
Start the service with the static policy engine:
298-
299-
```bash
300-
tokensmith serve \
301-
--provider=keycloak \
302-
--issuer=http://tokensmith:8080 \
303-
--port=8080 \
304-
--cluster-id=test-cluster-id \
305-
--openchami-id=test-openchami-id \
306-
--config=config.json \
307-
--policy-engine=static
308-
```
309-
310-
#### Using File-Based Policy Engine
311-
312-
Generate a policy configuration file and start the service:
313-
314-
```bash
315-
# Generate a policy configuration file
316-
tokensmith generate-policy-config --policy-config policy.json
317-
318-
# Start the service with file-based policy engine
319-
tokensmith serve \
320-
--provider=keycloak \
321-
--issuer=http://tokensmith:8080 \
322-
--port=8080 \
323-
--cluster-id=test-cluster-id \
324-
--openchami-id=test-openchami-id \
325-
--config=config.json \
326-
--policy-engine=file-based \
327-
--policy-config=policy.json
328-
```
329192

330193
#### Configuration File
331194

@@ -356,8 +219,6 @@ Configuration options:
356219
| `--keycloak-url` | Keycloak admin API URL | `http://keycloak:8080` |
357220
| `--keycloak-realm` | Keycloak realm | `openchami` |
358221
| `--config` | Path to configuration file | `""` |
359-
| `--policy-engine` | Policy engine type (static, file-based) | `static` |
360-
| `--policy-config` | Path to policy configuration file | `""` |
361222

362223
| Environment Variable | Description |
363224
|------|-------------|

cmd/tokenservice/main.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"fmt"
99
"os"
1010

11-
"github.com/openchami/tokensmith/pkg/policy"
1211
tokenservice "github.com/openchami/tokensmith/pkg/tokenservice"
1312
"github.com/spf13/cobra"
1413
)
@@ -25,9 +24,6 @@ var (
2524
keyFile string
2625
keyDir string
2726
nonEnforcing bool // Skip validation checks and only log errors
28-
// Policy engine configuration
29-
policyEngineType string
30-
policyConfigPath string
3127
)
3228

3329
var rootCmd = &cobra.Command{
@@ -49,59 +45,12 @@ var generateConfigCmd = &cobra.Command{
4945
},
5046
}
5147

52-
var generatePolicyConfigCmd = &cobra.Command{
53-
Use: "generate-policy-config",
54-
Short: "Generate a default policy configuration file",
55-
RunE: func(cmd *cobra.Command, args []string) error {
56-
policyConfig := &policy.FileBasedConfig{
57-
Version: "1.0.0",
58-
DefaultPolicy: &policy.PolicyDecision{
59-
Scopes: []string{"read"},
60-
Audiences: []string{"smd", "bss", "cloud-init"},
61-
Permissions: []string{"read:basic"},
62-
},
63-
Roles: map[string]*policy.RolePolicy{
64-
"admin": {
65-
Name: "Administrator",
66-
Description: "Full administrative access",
67-
Scopes: []string{"read", "write", "admin"},
68-
Audiences: []string{"smd", "bss", "cloud-init", "admin-service"},
69-
Permissions: []string{"read:all", "write:all", "admin:all"},
70-
},
71-
"user": {
72-
Name: "Regular User",
73-
Description: "Basic user access",
74-
Scopes: []string{"read"},
75-
Audiences: []string{"smd", "bss", "cloud-init"},
76-
Permissions: []string{"read:basic"},
77-
},
78-
},
79-
UserRoleMappings: map[string][]string{
80-
"adminuser": {"admin"},
81-
"regularuser": {"user"},
82-
},
83-
GroupRoleMappings: map[string][]string{
84-
"admins": {"admin"},
85-
"users": {"user"},
86-
},
87-
}
88-
89-
if err := policy.SaveFileBasedConfig(policyConfig, policyConfigPath); err != nil {
90-
return fmt.Errorf("failed to save policy config: %w", err)
91-
}
92-
fmt.Printf("Generated policy configuration file at: %s\n", policyConfigPath)
93-
return nil
94-
},
95-
}
96-
9748
func init() {
9849

9950
rootCmd.AddCommand(generateConfigCmd)
100-
rootCmd.AddCommand(generatePolicyConfigCmd)
10151

10252
// Global flags
10353
rootCmd.PersistentFlags().StringVar(&configPath, "config", "", "Path to configuration file")
104-
rootCmd.PersistentFlags().StringVar(&policyConfigPath, "policy-config", "", "Path to policy configuration file")
10554
}
10655

10756
func main() {

cmd/tokenservice/serve.go

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ import (
88
"fmt"
99
"os"
1010
"path/filepath"
11-
"time"
1211

1312
"github.com/openchami/tokensmith/pkg/keys"
14-
"github.com/openchami/tokensmith/pkg/policy"
1513
"github.com/openchami/tokensmith/pkg/tokenservice"
1614
"github.com/spf13/cobra"
1715
)
@@ -26,12 +24,6 @@ var serveCmd = &cobra.Command{
2624
return fmt.Errorf("failed to load config: %w", err)
2725
}
2826

29-
// Create policy engine configuration
30-
policyEngineConfig, err := createPolicyEngineConfig()
31-
if err != nil {
32-
return fmt.Errorf("failed to create policy engine config: %w", err)
33-
}
34-
3527
// Get OIDC credentials from environment variables if not provided via flags
3628
if oidcClientID == "" {
3729
oidcClientID = os.Getenv("OIDC_CLIENT_ID")
@@ -47,7 +39,6 @@ var serveCmd = &cobra.Command{
4739
ClusterID: clusterID,
4840
OpenCHAMIID: openCHAMIID,
4941
NonEnforcing: nonEnforcing,
50-
PolicyEngine: policyEngineConfig,
5142
OIDCIssuerURL: oidcIssuerURL,
5243
OIDCClientID: oidcClientID,
5344
OIDCClientSecret: oidcClientSecret,
@@ -109,46 +100,5 @@ func init() {
109100
serveCmd.Flags().StringVar(&keyDir, "key-dir", "", "Directory to save key files")
110101
serveCmd.Flags().BoolVar(&nonEnforcing, "non-enforcing", false, "Skip validation checks and only log errors")
111102

112-
// Policy engine flags
113-
serveCmd.Flags().StringVar(&policyEngineType, "policy-engine", "static", "Policy engine type (static, file-based)")
114-
serveCmd.Flags().StringVar(&policyConfigPath, "policy-config", "", "Path to policy configuration file (for file-based engine)")
115-
116103
rootCmd.AddCommand(serveCmd)
117104
}
118-
119-
// createPolicyEngineConfig creates a policy engine configuration based on command-line flags
120-
func createPolicyEngineConfig() (*tokenservice.PolicyEngineConfig, error) {
121-
switch policyEngineType {
122-
case "static":
123-
return &tokenservice.PolicyEngineConfig{
124-
Type: tokenservice.PolicyEngineTypeStatic,
125-
Static: &policy.StaticEngineConfig{
126-
Name: "tokensmith-static-engine",
127-
Version: "1.0.0",
128-
Scopes: []string{"read", "write"},
129-
Audiences: []string{"smd", "bss", "cloud-init"},
130-
Permissions: []string{"read:basic", "write:basic"},
131-
TokenLifetime: func() *time.Duration { d := time.Hour; return &d }(),
132-
AdditionalClaims: map[string]interface{}{
133-
"policy_engine": "static",
134-
"version": "1.0.0",
135-
},
136-
},
137-
}, nil
138-
case "file-based":
139-
if policyConfigPath == "" {
140-
return nil, fmt.Errorf("policy-config path is required for file-based policy engine")
141-
}
142-
return &tokenservice.PolicyEngineConfig{
143-
Type: tokenservice.PolicyEngineTypeFileBased,
144-
FileBased: &policy.FileBasedEngineConfig{
145-
Name: "tokensmith-file-engine",
146-
Version: "1.0.0",
147-
ConfigPath: policyConfigPath,
148-
ReloadInterval: func() *time.Duration { d := 5 * time.Minute; return &d }(),
149-
},
150-
}, nil
151-
default:
152-
return nil, fmt.Errorf("unsupported policy engine type: %s", policyEngineType)
153-
}
154-
}

0 commit comments

Comments
 (0)