plug opa in auth
This commit is contained in:
parent
3d2bb613bd
commit
972d43ac30
|
@ -13,3 +13,4 @@ POSTGRES_IMAGE=postgres:15.0
|
||||||
|
|
||||||
# OPA
|
# OPA
|
||||||
OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless
|
OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless
|
||||||
|
OPA_URL=http://v3-opa:8181/v1/data/
|
||||||
|
|
|
@ -13,3 +13,4 @@ POSTGRES_IMAGE=postgres:15.0
|
||||||
|
|
||||||
# OPA
|
# OPA
|
||||||
OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless
|
OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless
|
||||||
|
OPA_URL=http://v3-opa:8181/v1/data/
|
||||||
|
|
|
@ -61,7 +61,6 @@ services:
|
||||||
- "--server"
|
- "--server"
|
||||||
- "--log-format=json-pretty"
|
- "--log-format=json-pretty"
|
||||||
- "--set=decision_logs.console=true"
|
- "--set=decision_logs.console=true"
|
||||||
- "--set=default_decision=example/allow"
|
|
||||||
- "./policies/"
|
- "./policies/"
|
||||||
volumes:
|
volumes:
|
||||||
- ./opa:/policies
|
- ./opa:/policies
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package user.me
|
package user.read
|
||||||
|
|
||||||
default allow := false
|
default allow := false
|
||||||
|
|
||||||
allow := true {
|
allow := true {
|
||||||
input.user == "jean"
|
input.uuid == "96d99d44-e0a6-458e-a656-de2a400d60a8"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@ package user.list
|
||||||
default allow := false
|
default allow := false
|
||||||
|
|
||||||
allow := true {
|
allow := true {
|
||||||
input.user == "pierre"
|
input.uuid == "96d99d44-e0a6-458e-a656-de2a400d60a9"
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@golevelup/nestjs-rabbitmq": "^3.4.0",
|
"@golevelup/nestjs-rabbitmq": "^3.4.0",
|
||||||
"@grpc/grpc-js": "^1.8.0",
|
"@grpc/grpc-js": "^1.8.0",
|
||||||
"@grpc/proto-loader": "^0.7.4",
|
"@grpc/proto-loader": "^0.7.4",
|
||||||
|
"@nestjs/axios": "^1.0.1",
|
||||||
"@nestjs/common": "^9.0.0",
|
"@nestjs/common": "^9.0.0",
|
||||||
"@nestjs/config": "^2.2.0",
|
"@nestjs/config": "^2.2.0",
|
||||||
"@nestjs/core": "^9.0.0",
|
"@nestjs/core": "^9.0.0",
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
"@nestjs/microservices": "^9.2.1",
|
"@nestjs/microservices": "^9.2.1",
|
||||||
"@nestjs/platform-express": "^9.0.0",
|
"@nestjs/platform-express": "^9.0.0",
|
||||||
"@prisma/client": "^4.7.1",
|
"@prisma/client": "^4.7.1",
|
||||||
|
"axios": "^1.2.2",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
|
@ -1642,6 +1644,29 @@
|
||||||
"node-pre-gyp": "bin/node-pre-gyp"
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nestjs/axios": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-TpoZM/0ZJ9xiC04qkRDFod93LCZ12TQARRU3ejDvBK2E8emvzM4HThOs5ePklVxce4Q1ZsnrIWqnImvoDmJYnQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "1.2.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0",
|
||||||
|
"reflect-metadata": "^0.1.12",
|
||||||
|
"rxjs": "^6.0.0 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nestjs/axios/node_modules/axios": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.0",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nestjs/cli": {
|
"node_modules/@nestjs/cli": {
|
||||||
"version": "9.1.5",
|
"version": "9.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.5.tgz",
|
||||||
|
@ -3173,8 +3198,17 @@
|
||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
"dev": true
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.0",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/babel-jest": {
|
"node_modules/babel-jest": {
|
||||||
"version": "28.1.3",
|
"version": "28.1.3",
|
||||||
|
@ -3813,7 +3847,6 @@
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -4015,7 +4048,6 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
|
@ -4854,6 +4886,25 @@
|
||||||
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
|
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fork-ts-checker-webpack-plugin": {
|
"node_modules/fork-ts-checker-webpack-plugin": {
|
||||||
"version": "7.2.13",
|
"version": "7.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz",
|
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz",
|
||||||
|
@ -4908,7 +4959,6 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
|
@ -7548,6 +7598,11 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
@ -10487,6 +10542,26 @@
|
||||||
"tar": "^6.1.11"
|
"tar": "^6.1.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nestjs/axios": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-TpoZM/0ZJ9xiC04qkRDFod93LCZ12TQARRU3ejDvBK2E8emvzM4HThOs5ePklVxce4Q1ZsnrIWqnImvoDmJYnQ==",
|
||||||
|
"requires": {
|
||||||
|
"axios": "1.2.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.15.0",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nestjs/cli": {
|
"@nestjs/cli": {
|
||||||
"version": "9.1.5",
|
"version": "9.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.5.tgz",
|
||||||
|
@ -11648,8 +11723,17 @@
|
||||||
"asynckit": {
|
"asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
"dev": true
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.15.0",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"babel-jest": {
|
"babel-jest": {
|
||||||
"version": "28.1.3",
|
"version": "28.1.3",
|
||||||
|
@ -12118,7 +12202,6 @@
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -12278,8 +12361,7 @@
|
||||||
"delayed-stream": {
|
"delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"delegates": {
|
"delegates": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -12926,6 +13008,11 @@
|
||||||
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.15.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
|
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
||||||
|
},
|
||||||
"fork-ts-checker-webpack-plugin": {
|
"fork-ts-checker-webpack-plugin": {
|
||||||
"version": "7.2.13",
|
"version": "7.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz",
|
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz",
|
||||||
|
@ -12962,7 +13049,6 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
|
@ -14917,6 +15003,11 @@
|
||||||
"ipaddr.js": "1.9.1"
|
"ipaddr.js": "1.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
|
},
|
||||||
"pump": {
|
"pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
"@golevelup/nestjs-rabbitmq": "^3.4.0",
|
"@golevelup/nestjs-rabbitmq": "^3.4.0",
|
||||||
"@grpc/grpc-js": "^1.8.0",
|
"@grpc/grpc-js": "^1.8.0",
|
||||||
"@grpc/proto-loader": "^0.7.4",
|
"@grpc/proto-loader": "^0.7.4",
|
||||||
|
"@nestjs/axios": "^1.0.1",
|
||||||
"@nestjs/common": "^9.0.0",
|
"@nestjs/common": "^9.0.0",
|
||||||
"@nestjs/config": "^2.2.0",
|
"@nestjs/config": "^2.2.0",
|
||||||
"@nestjs/core": "^9.0.0",
|
"@nestjs/core": "^9.0.0",
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
"@nestjs/microservices": "^9.2.1",
|
"@nestjs/microservices": "^9.2.1",
|
||||||
"@nestjs/platform-express": "^9.0.0",
|
"@nestjs/platform-express": "^9.0.0",
|
||||||
"@prisma/client": "^4.7.1",
|
"@prisma/client": "^4.7.1",
|
||||||
|
"axios": "^1.2.2",
|
||||||
"bcrypt": "^5.1.0",
|
"bcrypt": "^5.1.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
|
@ -80,7 +82,11 @@
|
||||||
"json",
|
"json",
|
||||||
"ts"
|
"ts"
|
||||||
],
|
],
|
||||||
"modulePathIgnorePatterns": [".controller.ts",".module.ts","main.ts"],
|
"modulePathIgnorePatterns": [
|
||||||
|
".controller.ts",
|
||||||
|
".module.ts",
|
||||||
|
"main.ts"
|
||||||
|
],
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
"transform": {
|
"transform": {
|
||||||
|
|
10
src/main.ts
10
src/main.ts
|
@ -9,11 +9,17 @@ async function bootstrap() {
|
||||||
{
|
{
|
||||||
transport: Transport.GRPC,
|
transport: Transport.GRPC,
|
||||||
options: {
|
options: {
|
||||||
package: 'authentication',
|
package: ['authentication', 'authorization'],
|
||||||
protoPath: join(
|
protoPath: [
|
||||||
|
join(
|
||||||
__dirname,
|
__dirname,
|
||||||
'modules/authentication/adapters/primaries/authentication.proto',
|
'modules/authentication/adapters/primaries/authentication.proto',
|
||||||
),
|
),
|
||||||
|
join(
|
||||||
|
__dirname,
|
||||||
|
'modules/authorization/adapters/primaries/authorization.proto',
|
||||||
|
),
|
||||||
|
],
|
||||||
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
||||||
loader: { keepCase: true, enums: String },
|
loader: { keepCase: true, enums: String },
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Controller } from '@nestjs/common';
|
||||||
|
import { QueryBus } from '@nestjs/cqrs';
|
||||||
|
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||||
|
import { DecisionRequest } from '../../domain/dtos/decision.request';
|
||||||
|
import { DecisionQuery } from '../../queries/decision.query';
|
||||||
|
import { DecisionResult } from '../secondaries/decision-result';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AuthorizationController {
|
||||||
|
constructor(private readonly _queryBus: QueryBus) {}
|
||||||
|
|
||||||
|
@GrpcMethod('AuthorizationService', 'Decide')
|
||||||
|
async decide(data: DecisionRequest): Promise<DecisionResult> {
|
||||||
|
try {
|
||||||
|
const decision: boolean = await this._queryBus.execute(
|
||||||
|
new DecisionQuery(data.uuid, data.domain, data.action, data.context),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
allow: decision,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw new RpcException({
|
||||||
|
code: 7,
|
||||||
|
message: 'Permission denied',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package authorization;
|
||||||
|
|
||||||
|
service AuthorizationService {
|
||||||
|
rpc Decide(AuthorizationRequest) returns (Decision);
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthorizationRequest {
|
||||||
|
string uuid = 1;
|
||||||
|
Domain domain = 2;
|
||||||
|
Action action = 3;
|
||||||
|
repeated Item context = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Domain {
|
||||||
|
user = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Action {
|
||||||
|
create = 0;
|
||||||
|
read = 1;
|
||||||
|
update = 2;
|
||||||
|
delete = 3;
|
||||||
|
list = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Item {
|
||||||
|
string name = 1;
|
||||||
|
string value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Decision {
|
||||||
|
bool allow = 1;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export class DecisionResult {
|
||||||
|
allow: boolean;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { DecisionResult } from './decision-result';
|
||||||
|
|
||||||
|
export class Decision {
|
||||||
|
decision_id: string;
|
||||||
|
result: DecisionResult;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
import { Action } from '../../domain/dtos/action.enum';
|
||||||
|
import { Domain } from '../../domain/dtos/domain.enum';
|
||||||
|
import { IMakeDecision } from '../../domain/interfaces/decision-maker';
|
||||||
|
import { Decision } from './decision';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OpaDecisionMaker extends IMakeDecision {
|
||||||
|
constructor(
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
private readonly httpService: HttpService,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async decide(
|
||||||
|
uuid: string,
|
||||||
|
domain: Domain,
|
||||||
|
action: Action,
|
||||||
|
context: Array<{ name: string; value: string }>,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { data } = await lastValueFrom(
|
||||||
|
this.httpService.post<Decision>(
|
||||||
|
this.configService.get<string>('OPA_URL') + domain + '/' + action,
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
uuid,
|
||||||
|
...context,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return data.result.allow;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,15 @@
|
||||||
|
import { HttpModule } from '@nestjs/axios';
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { CqrsModule } from '@nestjs/cqrs';
|
import { CqrsModule } from '@nestjs/cqrs';
|
||||||
import { ValidateAuthenticationUseCase } from '../authentication/domain/usecases/validate-authentication.usecase';
|
|
||||||
import { DatabaseModule } from '../database/database.module';
|
import { DatabaseModule } from '../database/database.module';
|
||||||
|
import { AuthorizationController } from './adapters/primaries/authorization.controller';
|
||||||
|
import { OpaDecisionMaker } from './adapters/secondaries/opa.decision-maker';
|
||||||
|
import { DecisionUseCase } from './domain/usecases/decision.usecase';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [DatabaseModule, CqrsModule],
|
imports: [DatabaseModule, CqrsModule, HttpModule],
|
||||||
exports: [],
|
exports: [],
|
||||||
providers: [ValidateAuthenticationUseCase],
|
controllers: [AuthorizationController],
|
||||||
|
providers: [OpaDecisionMaker, DecisionUseCase],
|
||||||
})
|
})
|
||||||
export class AuthorizationModule {}
|
export class AuthorizationModule {}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export enum Action {
|
||||||
|
create = 'create',
|
||||||
|
read = 'read',
|
||||||
|
update = 'update',
|
||||||
|
delete = 'delete',
|
||||||
|
list = 'list',
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { IsArray, IsNotEmpty, IsString } from 'class-validator';
|
||||||
|
import { Action } from './action.enum';
|
||||||
|
import { Domain } from './domain.enum';
|
||||||
|
|
||||||
|
export class DecisionRequest {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
uuid: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
domain: Domain;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
action: Action;
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
context?: Array<{ name: string; value: string }>;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export enum Domain {
|
||||||
|
user = 'user',
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class ValidateAuthorizationRequest {
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
uuid: string;
|
|
||||||
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
action: string;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
export class Authorization {
|
|
||||||
uuid: string;
|
|
||||||
action: string;
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Action } from '../dtos/action.enum';
|
||||||
|
import { Domain } from '../dtos/domain.enum';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export abstract class IMakeDecision {
|
||||||
|
abstract decide(
|
||||||
|
uuid: string,
|
||||||
|
domain: Domain,
|
||||||
|
action: Action,
|
||||||
|
context: Array<{ name: string; value: string }>,
|
||||||
|
): Promise<boolean>;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { QueryHandler } from '@nestjs/cqrs';
|
||||||
|
import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker';
|
||||||
|
import { DecisionQuery } from '../../queries/decision.query';
|
||||||
|
|
||||||
|
@QueryHandler(DecisionQuery)
|
||||||
|
export class DecisionUseCase {
|
||||||
|
constructor(private readonly _decisionMaker: OpaDecisionMaker) {}
|
||||||
|
|
||||||
|
async execute(decisionQuery: DecisionQuery): Promise<boolean> {
|
||||||
|
return this._decisionMaker.decide(
|
||||||
|
decisionQuery.uuid,
|
||||||
|
decisionQuery.domain,
|
||||||
|
decisionQuery.action,
|
||||||
|
decisionQuery.context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
import { QueryHandler } from '@nestjs/cqrs';
|
|
||||||
import { ValidateAuthorizationQuery } from '../../queries/validate-authorization.query';
|
|
||||||
|
|
||||||
@QueryHandler(ValidateAuthorizationQuery)
|
|
||||||
export class ValidateAuthorizationUseCase {
|
|
||||||
async execute(validate: ValidateAuthorizationQuery): Promise<boolean> {
|
|
||||||
return Promise.resolve(validate.action == 'authorized');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Action } from '../domain/dtos/action.enum';
|
||||||
|
import { Domain } from '../domain/dtos/domain.enum';
|
||||||
|
|
||||||
|
export class DecisionQuery {
|
||||||
|
readonly uuid: string;
|
||||||
|
readonly domain: Domain;
|
||||||
|
readonly action: Action;
|
||||||
|
readonly context: Array<{ name: string; value: string }>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
uuid: string,
|
||||||
|
domain: Domain,
|
||||||
|
action: Action,
|
||||||
|
context?: Array<{ name: string; value: string }>,
|
||||||
|
) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.domain = domain;
|
||||||
|
this.action = action;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
export class ValidateAuthorizationQuery {
|
|
||||||
readonly uuid: string;
|
|
||||||
readonly action: string;
|
|
||||||
|
|
||||||
constructor(uuid: string, action: string) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
this.action = action;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { classes } from '@automapper/classes';
|
||||||
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker';
|
||||||
|
import { Action } from '../../domain/dtos/action.enum';
|
||||||
|
import { DecisionRequest } from '../../domain/dtos/decision.request';
|
||||||
|
import { Domain } from '../../domain/dtos/domain.enum';
|
||||||
|
import { DecisionUseCase } from '../../domain/usecases/decision.usecase';
|
||||||
|
import { DecisionQuery } from '../../queries/decision.query';
|
||||||
|
|
||||||
|
const mockOpaDecisionMaker = {
|
||||||
|
decide: jest.fn().mockResolvedValue(Promise.resolve(true)),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('DecisionUseCase', () => {
|
||||||
|
let decisionUseCase: DecisionUseCase;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: OpaDecisionMaker,
|
||||||
|
useValue: mockOpaDecisionMaker,
|
||||||
|
},
|
||||||
|
DecisionUseCase,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
decisionUseCase = module.get<DecisionUseCase>(DecisionUseCase);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(decisionUseCase).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should validate an authorization', async () => {
|
||||||
|
const decisionRequest: DecisionRequest = new DecisionRequest();
|
||||||
|
decisionRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91';
|
||||||
|
decisionRequest.domain = Domain.user;
|
||||||
|
decisionRequest.action = Action.create;
|
||||||
|
decisionRequest.context = [
|
||||||
|
{
|
||||||
|
name: 'context1',
|
||||||
|
value: 'value1',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
decisionUseCase.execute(
|
||||||
|
new DecisionQuery(
|
||||||
|
decisionRequest.uuid,
|
||||||
|
decisionRequest.domain,
|
||||||
|
decisionRequest.action,
|
||||||
|
decisionRequest.context,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { OpaDecisionMaker } from '../../adapters/secondaries/opa.decision-maker';
|
||||||
|
import { Action } from '../../domain/dtos/action.enum';
|
||||||
|
import { Domain } from '../../domain/dtos/domain.enum';
|
||||||
|
|
||||||
|
const mockHttpService = {
|
||||||
|
post: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return of({
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
decision_id: '96d99d44-e0a6-458e-a656-de2a400d60a8',
|
||||||
|
result: {
|
||||||
|
allow: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return of({
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
decision_id: '96d99d44-e0a6-458e-a656-de2a400d60a9',
|
||||||
|
result: {
|
||||||
|
allow: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockConfigService = {
|
||||||
|
get: jest.fn().mockResolvedValue({
|
||||||
|
OPA_URL: 'http://url/',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('OpaDecisionMaker', () => {
|
||||||
|
let opaDecisionMaker: OpaDecisionMaker;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: HttpService,
|
||||||
|
useValue: mockHttpService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useValue: mockConfigService,
|
||||||
|
},
|
||||||
|
OpaDecisionMaker,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
opaDecisionMaker = module.get<OpaDecisionMaker>(OpaDecisionMaker);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(opaDecisionMaker).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should return a truthy decision', async () => {
|
||||||
|
const decision = await opaDecisionMaker.decide(
|
||||||
|
'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
Domain.user,
|
||||||
|
Action.read,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
expect(decision).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('should return a falsy decision', async () => {
|
||||||
|
const decision = await opaDecisionMaker.decide(
|
||||||
|
'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
Domain.user,
|
||||||
|
Action.read,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
expect(decision).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,44 +0,0 @@
|
||||||
import { classes } from '@automapper/classes';
|
|
||||||
import { AutomapperModule } from '@automapper/nestjs';
|
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { ValidateAuthorizationRequest } from '../../domain/dtos/validate-authorization.request';
|
|
||||||
import { ValidateAuthorizationUseCase } from '../../domain/usecases/validate-authorization.usecase';
|
|
||||||
import { ValidateAuthorizationQuery } from '../../queries/validate-authorization.query';
|
|
||||||
|
|
||||||
describe('ValidateAuthorizationUseCase', () => {
|
|
||||||
let validateAuthorizationUseCase: ValidateAuthorizationUseCase;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
|
|
||||||
providers: [ValidateAuthorizationUseCase],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
validateAuthorizationUseCase = module.get<ValidateAuthorizationUseCase>(
|
|
||||||
ValidateAuthorizationUseCase,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be defined', () => {
|
|
||||||
expect(validateAuthorizationUseCase).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('execute', () => {
|
|
||||||
it('should validate an authorization', async () => {
|
|
||||||
const validateAuthorizationRequest: ValidateAuthorizationRequest =
|
|
||||||
new ValidateAuthorizationRequest();
|
|
||||||
validateAuthorizationRequest.uuid =
|
|
||||||
'bb281075-1b98-4456-89d6-c643d3044a91';
|
|
||||||
validateAuthorizationRequest.action = 'authorized';
|
|
||||||
|
|
||||||
expect(
|
|
||||||
validateAuthorizationUseCase.execute(
|
|
||||||
new ValidateAuthorizationQuery(
|
|
||||||
validateAuthorizationRequest.uuid,
|
|
||||||
validateAuthorizationRequest.action,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue