Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/go-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,24 @@ jobs:
run: |
cd internal/aasrepository/integration_tests
go test -v .

aas-repository-security-tests:
name: Asset Administration Shell Repository Security Tests
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Get dependencies
run: go mod download

- name: Run aas repository security tests
run: |
cd internal/aasrepository/security_tests
go test -v .
1 change: 1 addition & 0 deletions basyxschema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ CREATE TABLE IF NOT EXISTS asset_information (
CREATE TABLE IF NOT EXISTS aas_submodel_reference (
id BIGSERIAL PRIMARY KEY,
aas_id BIGINT NOT NULL REFERENCES aas(id) ON DELETE CASCADE,
position INTEGER NOT NULL,
type int NOT NULL
);

Expand Down
10 changes: 10 additions & 0 deletions cmd/aasrepositoryservice/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ postgres:
maxIdleConnections: 500
connMaxLifetimeMinutes: 5

general:
enableImplicitCasts: false

oidc:
trustlistPath: "config/trustlist.json"

abac:
enabled: false
modelPath: "config/access_rules/access-rules.json"

# jws:
# privateKeyPath: "./rsa-key.pem"

Expand Down
84 changes: 84 additions & 0 deletions cmd/aasrepositoryservice/config/access_rules/access-rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"AllAccessPermissionRules": {
"DEFATTRIBUTES": [
{ "name": "anonym_attr", "attributes": [ { "GLOBAL": "ANONYMOUS" } ] },
{ "name": "role_attr", "attributes": [ { "CLAIM": "role" } ] },
{ "name": "member_attr", "attributes": [ { "CLAIM": "role" }, { "CLAIM": "clear" } ] }
],
"DEFOBJECTS": [
{ "name": "description", "objects": [ { "ROUTE": "/description" } ] },
{
"name": "all_shell_routes",
"objects": [
{ "ROUTE": "/shells" },
{ "ROUTE": "/shells/$reference" },
{ "ROUTE": "/shells/*" },
{ "ROUTE": "/shells/*/*" },
{ "ROUTE": "/shells/*/*/*" },
{ "ROUTE": "/shells/*/*/*/*" }
]
},
{
"name": "all_shell_identifiable",
"objects": [
{ "IDENTIFIABLE": "$aas(\"*\")" }
]
}
],
"DEFACLS": [
{ "name": "read_anonymous", "acl": { "USEATTRIBUTES": "anonym_attr", "RIGHTS": ["READ"], "ACCESS": "ALLOW" } },
{ "name": "read_role", "acl": { "USEATTRIBUTES": "role_attr", "RIGHTS": ["READ"], "ACCESS": "ALLOW" } },
{ "name": "write_role", "acl": { "USEATTRIBUTES": "role_attr", "RIGHTS": ["CREATE", "UPDATE", "DELETE"], "ACCESS": "ALLOW" } },
{ "name": "admin_all", "acl": { "USEATTRIBUTES": "member_attr", "RIGHTS": ["ALL"], "ACCESS": "ALLOW" } }
],
"DEFFORMULAS": [
{ "name": "always_true", "formula": { "$boolean": true } },
{
"name": "role_is_admin",
"formula": {
"$and": [
{ "$eq": [ { "$attribute": { "CLAIM": "role" } }, { "$strVal": "admin" } ] },
{ "$ge": [ { "$numCast": { "$attribute": { "CLAIM": "clear" } } }, { "$numVal": 10 } ] }
]
}
},
{
"name": "viewer_visible_aas",
"formula": {
"$and": [
{ "$eq": [ { "$attribute": { "CLAIM": "role" } }, { "$strVal": "viewer" } ] },
{ "$eq": [ { "$field": "$aas#assetInformation.assetType" }, { "$strVal": "viewer-visible" } ] }
]
}
},
{
"name": "editor_editable_aas",
"formula": {
"$and": [
{ "$eq": [ { "$attribute": { "CLAIM": "role" } }, { "$strVal": "editor" } ] },
{ "$eq": [ { "$field": "$aas#assetInformation.assetType" }, { "$strVal": "editor-allowed" } ] }
]
}
}
],
"rules": [
{ "USEACL": "read_anonymous", "USEOBJECTS": ["description"], "USEFORMULA": "always_true" },
{ "USEACL": "admin_all", "USEOBJECTS": ["all_shell_routes"], "USEFORMULA": "role_is_admin" },
{
"USEACL": "read_role",
"USEOBJECTS": ["all_shell_identifiable"],
"USEFORMULA": "viewer_visible_aas",
"FILTER": {
"FRAGMENT": "$aas#assetInformation.specificAssetIds[]",
"CONDITION": {
"$eq": [
{ "$field": "$aas#assetInformation.specificAssetIds[].name" },
{ "$strVal": "customerPartId" }
]
}
}
},
{ "USEACL": "write_role", "USEOBJECTS": ["all_shell_routes"], "USEFORMULA": "editor_editable_aas" }
]
}
}
7 changes: 7 additions & 0 deletions cmd/aasrepositoryservice/config/trustlist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"issuer": "http://localhost:8080/realms/basyx",
"audience": "discovery-service",
"scopes": ["email", "profile"]
}
]
24 changes: 19 additions & 5 deletions cmd/aasrepositoryservice/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/eclipse-basyx/basyx-go-components/internal/aasrepository/api"
persistencepostgresql "github.com/eclipse-basyx/basyx-go-components/internal/aasrepository/persistence"
"github.com/eclipse-basyx/basyx-go-components/internal/common"
auth "github.com/eclipse-basyx/basyx-go-components/internal/common/security"
openapi "github.com/eclipse-basyx/basyx-go-components/pkg/aasrepositoryapi/go"
)

Expand All @@ -35,7 +36,7 @@ func runServer(ctx context.Context, configPath string, databaseSchema string) er

// Create Chi router
r := chi.NewRouter()
common.AddDefaultRouterErrorHandlers(r, "AASRepositoryService")
r.Use(common.ConfigMiddleware(config))
common.AddCors(r, config)

// Add health endpoint
Expand All @@ -56,17 +57,30 @@ func runServer(ctx context.Context, configPath string, databaseSchema string) er

aasSvc := api.NewAssetAdministrationShellRepositoryAPIAPIService(*aasDatabase)
aasCtrl := openapi.NewAssetAdministrationShellRepositoryAPIAPIController(aasSvc, config.Server.ContextPath, config.Server.StrictVerification)
for _, rt := range aasCtrl.Routes() {
r.Method(rt.Method, rt.Pattern, rt.HandlerFunc)
}

// ==== Description Service ====
descSvc := openapi.NewDescriptionAPIAPIService()
descCtrl := openapi.NewDescriptionAPIAPIController(descSvc)

base := common.NormalizeBasePath(config.Server.ContextPath)

// === Protected API Subrouter ===
apiRouter := chi.NewRouter()
common.AddDefaultRouterErrorHandlers(apiRouter, "AASRepositoryService")

if err := auth.SetupSecurity(ctx, config, apiRouter); err != nil {
return err
}

for _, rt := range aasCtrl.Routes() {
apiRouter.Method(rt.Method, rt.Pattern, rt.HandlerFunc)
}
for _, rt := range descCtrl.Routes() {
r.Method(rt.Method, rt.Pattern, rt.HandlerFunc)
apiRouter.Method(rt.Method, rt.Pattern, rt.HandlerFunc)
}

r.Mount(base, apiRouter)

// Start the server
addr := "0.0.0.0:" + fmt.Sprintf("%d", config.Server.Port)
log.Printf("▶️ Asset Administration Shell Repository listening on %s\n", addr)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading