UPDATE
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# CropLogic Authorization Service
|
||||
|
||||
This service runs OPA as a standalone authorization engine for `backend/access_control`.
|
||||
|
||||
## Run standalone
|
||||
|
||||
```bash
|
||||
docker compose -f accsess/docker-compose.yaml up -d
|
||||
```
|
||||
|
||||
## Decision endpoints
|
||||
|
||||
- Single feature: `POST /v1/data/croplogic/authz/decision`
|
||||
- Batch features: `POST /v1/data/croplogic/authz/batch_decision`
|
||||
|
||||
The backend uses the batch endpoint and sends the farm context only. Users are treated as `farmer` by default inside the service, and features are allowed unless there is a feature-specific rule in `policies/authz.rego`.
|
||||
|
||||
## Example request
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8181/v1/data/croplogic/authz/batch_decision \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d @- <<'EOF'
|
||||
{
|
||||
"input": {
|
||||
"resource": {
|
||||
"farm_id": "farm-1001",
|
||||
"subscription_plan_codes": ["gold"],
|
||||
"farm_types": ["greenhouse"],
|
||||
"crop_types": ["tomato"],
|
||||
"cultivation_types": ["soil"],
|
||||
"sensor_codes": ["sensor-7-in-1"],
|
||||
"power_sensor": ["main-power"],
|
||||
"customization": ["default-layout"]
|
||||
},
|
||||
"features": ["sensor-7-in-1"],
|
||||
"action": "view"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Add new rules in code
|
||||
|
||||
Define feature-specific checks directly in `policies/authz.rego`.
|
||||
|
||||
- If a feature has no rule, every action is allowed.
|
||||
- If a feature rule exists, its conditions are evaluated and any failing condition denies access.
|
||||
- `sensor-7-in-1` currently requires `resource.sensor_codes` to include `sensor-7-in-1`.
|
||||
@@ -0,0 +1,4 @@
|
||||
services: {}
|
||||
labels:
|
||||
app: croplogic-authz
|
||||
plugins: {}
|
||||
@@ -0,0 +1,21 @@
|
||||
services:
|
||||
opa:
|
||||
image: mirror-docker.runflare.com/openpolicyagent/opa
|
||||
container_name: croplogic-accsess-opa
|
||||
command:
|
||||
- run
|
||||
- --server
|
||||
- --addr=0.0.0.0:8181
|
||||
- /policies
|
||||
ports:
|
||||
- "8181:8181"
|
||||
volumes:
|
||||
- ./policies:/policies:ro
|
||||
- ./config/opa-config.yaml:/config/opa-config.yaml:ro
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
- crop_network
|
||||
networks:
|
||||
crop_network:
|
||||
external: true
|
||||
@@ -0,0 +1,68 @@
|
||||
package croplogic.authz
|
||||
|
||||
import rego.v1
|
||||
|
||||
default allow := false
|
||||
|
||||
allow if {
|
||||
decision.allow
|
||||
}
|
||||
|
||||
decision := feature_decision(input.feature)
|
||||
|
||||
batch_decision := {
|
||||
"features": {
|
||||
feature: result |
|
||||
feature := input.features[_]
|
||||
result := feature_decision(feature)
|
||||
},
|
||||
}
|
||||
|
||||
feature_decision(feature) := {
|
||||
"allow": true,
|
||||
"matched_rules": [],
|
||||
"deny_rules": [],
|
||||
"allow_rules": [],
|
||||
} if {
|
||||
not has_feature_rule(feature)
|
||||
}
|
||||
|
||||
feature_decision(feature) := result if {
|
||||
has_feature_rule(feature)
|
||||
rule := feature_rule(feature)
|
||||
matched := [matched_rule | matched_rule := rule; action_match(matched_rule)]
|
||||
deny_rules := [matched_rule | matched_rule := matched[_]; not object.get(matched_rule, "allow", false)]
|
||||
allow_rules := [matched_rule | matched_rule := matched[_]; object.get(matched_rule, "allow", false)]
|
||||
count(deny_rules) == 0
|
||||
result := {
|
||||
"allow": true,
|
||||
"matched_rules": matched,
|
||||
"deny_rules": deny_rules,
|
||||
"allow_rules": allow_rules,
|
||||
}
|
||||
}
|
||||
|
||||
feature_decision(feature) := result if {
|
||||
has_feature_rule(feature)
|
||||
rule := feature_rule(feature)
|
||||
matched := [matched_rule | matched_rule := rule; action_match(matched_rule)]
|
||||
deny_rules := [matched_rule | matched_rule := matched[_]; not object.get(matched_rule, "allow", false)]
|
||||
allow_rules := [matched_rule | matched_rule := matched[_]; object.get(matched_rule, "allow", false)]
|
||||
count(deny_rules) > 0
|
||||
result := {
|
||||
"allow": false,
|
||||
"matched_rules": matched,
|
||||
"deny_rules": deny_rules,
|
||||
"allow_rules": allow_rules,
|
||||
}
|
||||
}
|
||||
|
||||
action_match(rule) if {
|
||||
count(object.get(rule, "actions_any", [])) == 0
|
||||
}
|
||||
|
||||
action_match(rule) if {
|
||||
requested_action := lower(sprintf("%v", [object.get(input, "action", "view")]))
|
||||
action := object.get(rule, "actions_any", [])[_]
|
||||
lower(sprintf("%v", [action])) == requested_action
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"authz": {}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package croplogic.authz
|
||||
|
||||
import rego.v1
|
||||
|
||||
has_feature_rule(feature) if {
|
||||
is_sensor_7_in_1_feature(feature)
|
||||
}
|
||||
|
||||
feature_rule(feature) := {
|
||||
"code": "sensor-7-in-1-requires-sensor-code",
|
||||
"allow": true,
|
||||
"reason": "sensor-7-in-1 feature requires sensor_codes to include sensor-7-in-1",
|
||||
} if {
|
||||
is_sensor_7_in_1_feature(feature)
|
||||
has_sensor_code("sensor-7-in-1")
|
||||
}
|
||||
|
||||
feature_rule(feature) := {
|
||||
"code": "sensor-7-in-1-requires-sensor-code",
|
||||
"allow": false,
|
||||
"reason": "sensor-7-in-1 feature requires sensor_codes to include sensor-7-in-1",
|
||||
} if {
|
||||
is_sensor_7_in_1_feature(feature)
|
||||
not has_sensor_code("sensor-7-in-1")
|
||||
}
|
||||
|
||||
is_sensor_7_in_1_feature(feature) if {
|
||||
lower(sprintf("%v", [feature])) == "sensor-7-in-1"
|
||||
}
|
||||
|
||||
has_sensor_code(code) if {
|
||||
sensor_codes := object.get(input.resource, "sensor_codes", [])
|
||||
is_array(sensor_codes)
|
||||
sensor_code := sensor_codes[_]
|
||||
lower(sprintf("%v", [sensor_code])) == lower(sprintf("%v", [code]))
|
||||
}
|
||||
|
||||
has_sensor_code(code) if {
|
||||
sensor_code := object.get(input.resource, "sensor_codes", null)
|
||||
sensor_code != null
|
||||
not is_array(sensor_code)
|
||||
lower(sprintf("%v", [sensor_code])) == lower(sprintf("%v", [code]))
|
||||
}
|
||||
Reference in New Issue
Block a user