Skip to content

Commit

Permalink
Initial project import
Browse files Browse the repository at this point in the history
  • Loading branch information
kmesiab committed Oct 16, 2024
0 parents commit c9b8591
Show file tree
Hide file tree
Showing 18 changed files with 886 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/go-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build

defaults:
run:
shell: bash

on:
push:
branches:
- '**'

jobs:

build_go:
name: "🏗 Compile"

runs-on: ubuntu-latest
steps:
- name: 🛒 Checkout Code
uses: actions/checkout@v3

- name: 🚀 Set up Go
uses: actions/setup-go@v4.1.0
with:
go-version: '1.21.4'
cache: true
check-latest: true

- name: 🧹 Tidy
run: go mod tidy

- name: 🤖 Build
run: go build ./...
51 changes: 51 additions & 0 deletions .github/workflows/go-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Lint

defaults:
run:
shell: bash

on:
push:
branches:
- '**'

jobs:

lint-markdown:

name: "🧹 Markdown"
continue-on-error: true
runs-on: ubuntu-latest
steps:
- name: 🛒 Checkout Code
uses: actions/checkout@v3

- name: 📦 Install Node.js and npm
uses: actions/setup-node@v3
with:
node-version: '20.0.0'

- name: 📚 Install markdownlint-cli
run: npm install -g markdownlint-cli

- name: 🖊️ Run markdownlint
run: find . -name '*.md' -exec markdownlint {} +

lint_go:
name: "️‍️🕵️ Golang"

runs-on: ubuntu-latest
steps:
- name: 🛒 Checkout Code
uses: actions/checkout@v3

- name: 🚀 Set up Go
uses: actions/setup-go@v4.1.0
with:
cache: true
check-latest: true

- name: 🕵️‍♂️ Run GolangCI-Lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.54
41 changes: 41 additions & 0 deletions .github/workflows/go-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Test

defaults:
run:
shell: bash

on:
push:
branches:
- '**'

jobs:
build:
name: 🧪 Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.21.4

- name: Set up gotestfmt
run: go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest

# Run tests with nice formatting. Save the original log in /tmp/gotest.log
- name: 🧪 Execute Tests
run: |
set -euo pipefail
go test -json -v ./... 2>&1 | tee /tmp/gotest.log | gotestfmt
# Upload the original go test log as an artifact for later review.
- name: Upload test log
uses: actions/upload-artifact@v2
if: always()
with:
name: test-log
path: /tmp/gotest.log
if-no-files-found: error
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.DS_Store
102 changes: 102 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# 🚀 Go Policy Enforcer

![Golang](https://img.shields.io/badge/Go-00add8.svg?labelColor=171e21&style=for-the-badge&logo=go)

![Build](https://github.com/kmesiab/go-policy-enforcer/actions/workflows/go
-build.yml/badge.svg)
![Build](https://github.com/kmesiab/go-policy-enforcer/actions/workflows/go-lint.yml/badge.svg)
![Build](https://github.com/kmesiab/go-policy-enforcer/actions/workflows/go-test.yml/badge.svg)
[![Go Report Card](htt

Go Policy Enforcer is a flexible, lightweight library that allows
developers to dynamically enforce policies on Go structs using
customizable rules and reflection. This library is useful for
scenarios such as access control, validation, and policy-based
filtering of data.

## ✨ Features

-**Dynamic Policy Enforcement**: Apply rules dynamically without
hardcoding logic.
- 🔍 **Reflection-Based Field Access**: Leverages Go's reflection
to access and evaluate struct fields.
- 🛠️ **Custom Operators**: Supports custom operators like
`==`, `!=`, `<`, `>`, and more.
- 📦 **Extensible**: Easily add new rules and policies to
adapt to your use case.

## 📚 Installation

Install the package via `go get`:

```bash
go get github.com/kmesiab/go-policy-enforcer
```

## 🛠️ Usage

Here's a quick example of how to use Go Policy Enforcer to enforce rules
on your Go structs:

### Define Your Struct

```go
type Asset struct {
ID string
Type string
Finalized bool
}
```

### Define Your Policies

Policies can be written to define what rules need to be enforced on
a given struct.

```go
policies := []Policy{
{
Name: "FinalizedPolicy",
Rules: []Rule{
{Field: "Finalized", Operator: "==", Value: true},
},
{
Name: "TypePolicy",
Rules: []Rule{
{Field: "Type", Operator: "==", Value: "asset"},
},
}
```
### Enforce Policies
Create a policy enforcer and apply the rules to your struct:
```go
resource := Asset{
ID: "1",
Type: "asset",
Finalized: true,
}

enforcer := NewPolicyEnforcer(&policies)

if enforcer.Enforce(resource) {
fmt.Println("Asset passes all policies")
} else {
fmt.Println("Asset failed one or more policies")
}
```
## ✅ Running Tests
Run the following command to execute tests:
```bash
go test ./...
```
## 📝 License
This project is licensed under the MIT License. See the
[LICENSE](./LICENSE) file for more details.
Binary file added example/.DS_Store
Binary file not shown.
7 changes: 7 additions & 0 deletions example/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

type Asset struct {
ID string `json:"id"`
Type string `json:"type"`
Finalized bool `json:"state"`
}
63 changes: 63 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"fmt"
"log"

gopolicyenforcer "github.com/kmesiab/go-policy-enforcer"
)

const (
finalizedPolicyExampleFile = "./example/policies/finalized_policy.json"
idRequiredPolicyExampleFile = "./example/policies/id_required_policy.json"
)

func main() {

// Load a few policies from JSON files (./policies folder)
policyList, err := loadPolicies()
if err != nil {

log.Fatalf("error loading policies: %s", err)
}

// Create assets to test enforcement
allowedAsset := &Asset{ID: "1", Type: "asset", Finalized: true}
deniedAsset := &Asset{ID: "1", Type: "asset", Finalized: false}
assetList := []*Asset{allowedAsset, deniedAsset}

// Create a PolicyEnforcer instance with the policies
e := gopolicyenforcer.NewPolicyEnforcer(policyList)

// Enforce the policies on the assets and print results
for _, asset := range assetList {
if e.Enforce(asset) {
fmt.Printf("Asset %v is allowed\n", asset)
} else {
fmt.Printf("Asset %v is not allowed\n", asset)
}
}

}

func loadPolicies() (*[]gopolicyenforcer.Policy, error) {

finalizedPolicy, err := gopolicyenforcer.LoadPolicy(finalizedPolicyExampleFile)

if err != nil {
return nil, err
}

idRequiredPolicy, err := gopolicyenforcer.LoadPolicy(idRequiredPolicyExampleFile)

if err != nil {
fmt.Println("Error loading policy:", err)

}

return &[]gopolicyenforcer.Policy{
*finalizedPolicy,
*idRequiredPolicy,
}, nil

}
10 changes: 10 additions & 0 deletions example/policies/finalized_policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "FinalizedPolicy",
"rules": [
{
"field": "finalized",
"operator": "eq",
"value": true
}
]
}
10 changes: 10 additions & 0 deletions example/policies/id_required_policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "IdRequiredPolicy",
"rules": [
{
"field": "id",
"operator": ">",
"value": 0
}
]
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/kmesiab/go-policy-enforcer

go 1.23.2
58 changes: 58 additions & 0 deletions operators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package go_policy_enforcer

// PolicyCheckOperator is a function type that accepts two values of any type
// and returns a boolean result based on a comparison of the two values.
type PolicyCheckOperator func(leftVal, rightVal any) bool

// EqualsPolicyCheckOperator checks if two values are equal.
// Returns true if leftVal is equal to rightVal.
var EqualsPolicyCheckOperator = func(leftVal, rightVal any) bool {
return leftVal == rightVal
}

// NotEqualsPolicyCheckOperator checks if two values are not equal.
// Returns true if leftVal is not equal to rightVal.
var NotEqualsPolicyCheckOperator = func(leftVal, rightVal any) bool {
return leftVal != rightVal
}

// GreaterThanPolicyCheckOperator checks if the left value is greater than the right value.
// Assumes both values are integers. Returns true if leftVal is greater than rightVal.
var GreaterThanPolicyCheckOperator = func(leftVal, rightVal any) bool {
return leftVal.(int) > rightVal.(int)
}

// GreaterThanOrEqualsPolicyCheckOperator checks if the left value is greater than or equal to the right value.
// Assumes both values are integers. Returns true if leftVal is greater than or equal to rightVal.
var GreaterThanOrEqualsPolicyCheckOperator = func(leftVal, rightVal any) bool {
return leftVal.(int) >= rightVal.(int)
}

// LessThanPolicyCheckOperator checks if the left value is less than the right value.
// Assumes both values are integers. Returns true if leftVal is less than rightVal.
var LessThanPolicyCheckOperator = func(leftVal, rightVal any) bool {
return leftVal.(int) < rightVal.(int)
}

// policyCheckOperatorMap maps string representations of comparison operators
// to their corresponding PolicyCheckOperator functions.
var policyCheckOperatorMap = map[string]PolicyCheckOperator{
"==": EqualsPolicyCheckOperator,
"!=": NotEqualsPolicyCheckOperator,
">": GreaterThanPolicyCheckOperator,
">=": GreaterThanOrEqualsPolicyCheckOperator,
"<": LessThanPolicyCheckOperator,
}

// GetPolicyCheckOperator retrieves the appropriate PolicyCheckOperator function
// based on the provided operator string.
func GetPolicyCheckOperator(operator string) PolicyCheckOperator {
return policyCheckOperatorMap[operator]
}

// EvaluatePolicyCheckOperator takes a string operator, a left value, and a right value,
// retrieves the corresponding PolicyCheckOperator function, and evaluates it with the given values.
// Returns the result of the comparison as a boolean.
func EvaluatePolicyCheckOperator(operator string, leftVal, rightVal any) bool {
return GetPolicyCheckOperator(operator)(leftVal, rightVal)
}
Loading

0 comments on commit c9b8591

Please sign in to comment.