mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-06 23:32:47 +01:00
Compare commits
370 Commits
v0.9.1
...
v0.10.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f804305cd | ||
|
|
2626d156bc | ||
|
|
b25f3f4708 | ||
|
|
5f4fd667a9 | ||
|
|
fc42d4043b | ||
|
|
e987e46cbd | ||
|
|
b085ef4f1b | ||
|
|
9915a10ca2 | ||
|
|
94ebcade12 | ||
|
|
f73e91d142 | ||
|
|
4eb0658888 | ||
|
|
215e56deb1 | ||
|
|
a50133f6fd | ||
|
|
5b5bba30ca | ||
|
|
2a6183b368 | ||
|
|
3f6cd5b366 | ||
|
|
24eb325058 | ||
|
|
210ab54a8c | ||
|
|
a59dc977d0 | ||
|
|
88191fd7e6 | ||
|
|
1ecba482ef | ||
|
|
213ecd03d3 | ||
|
|
3525582f3f | ||
|
|
b14a0c5e77 | ||
|
|
b3f9789801 | ||
|
|
7c800ec8f2 | ||
|
|
654f188f11 | ||
|
|
8338ba9f69 | ||
|
|
9dd8116dd8 | ||
|
|
ba689f2482 | ||
|
|
255f668757 | ||
|
|
29ea4145a2 | ||
|
|
efaf0ed266 | ||
|
|
522e93c8f1 | ||
|
|
79c2450958 | ||
|
|
06e57b6e4c | ||
|
|
7275202f63 | ||
|
|
b2d2fe25e1 | ||
|
|
59fcd3ce35 | ||
|
|
52cebf8d0a | ||
|
|
979212f16a | ||
|
|
6093019c4a | ||
|
|
0f45821394 | ||
|
|
c93853a2bd | ||
|
|
dc94bc59fb | ||
|
|
4d299554c5 | ||
|
|
f661b548d6 | ||
|
|
91775bd09b | ||
|
|
1fc358a989 | ||
|
|
20e8285816 | ||
|
|
fd8e793361 | ||
|
|
2b7be7c212 | ||
|
|
ae913f7cd4 | ||
|
|
f9f2c17709 | ||
|
|
6354fd2735 | ||
|
|
4e49835add | ||
|
|
26246ce9f3 | ||
|
|
bcb5d2bf78 | ||
|
|
d80da3e77d | ||
|
|
8693ccd489 | ||
|
|
ce77fbad47 | ||
|
|
c93ec52986 | ||
|
|
cee31465c7 | ||
|
|
4baf1358f1 | ||
|
|
603029909d | ||
|
|
add7730036 | ||
|
|
cda8295c67 | ||
|
|
77cd336377 | ||
|
|
459fb2f5e7 | ||
|
|
e84ba818c5 | ||
|
|
fb1de82382 | ||
|
|
900d0e8693 | ||
|
|
0a172c0290 | ||
|
|
baa74761ad | ||
|
|
c12c5fe768 | ||
|
|
e063ab0936 | ||
|
|
b4c20f670c | ||
|
|
b86c2b2efe | ||
|
|
09800662ff | ||
|
|
40d63d8dee | ||
|
|
98b36f3a58 | ||
|
|
9c9c23fa0d | ||
|
|
44b7744bb7 | ||
|
|
e490deff76 | ||
|
|
3fbbc2ff65 | ||
|
|
6b21bc72e6 | ||
|
|
f3cfa6688d | ||
|
|
bc287b7600 | ||
|
|
8e5e50c85a | ||
|
|
ca5f02d94a | ||
|
|
b283717321 | ||
|
|
edb575c2aa | ||
|
|
c8f32ff0a7 | ||
|
|
e77a311340 | ||
|
|
b8da0734ec | ||
|
|
9744b8ba96 | ||
|
|
3d95fece81 | ||
|
|
210fdeb88d | ||
|
|
8914c97c57 | ||
|
|
d75d4efdd1 | ||
|
|
6cafad44c5 | ||
|
|
25d67f40af | ||
|
|
f01e1b197f | ||
|
|
6798427d65 | ||
|
|
16e95df060 | ||
|
|
664e079016 | ||
|
|
5e67816351 | ||
|
|
abd6632578 | ||
|
|
50cc19ddf7 | ||
|
|
7cef009e48 | ||
|
|
bba0b82034 | ||
|
|
91d96b45e3 | ||
|
|
0fe5ef0784 | ||
|
|
0652512d86 | ||
|
|
7d19621807 | ||
|
|
3a18988ee6 | ||
|
|
7fe6f779e1 | ||
|
|
bc31882e16 | ||
|
|
67339e18a4 | ||
|
|
eb373c3773 | ||
|
|
42d4d68320 | ||
|
|
8f65b99f26 | ||
|
|
4b19642a26 | ||
|
|
26f5e11b61 | ||
|
|
4c6f17670a | ||
|
|
c85b507c28 | ||
|
|
736cc24f74 | ||
|
|
37fea143d1 | ||
|
|
947bb4ba34 | ||
|
|
49ad9f74f2 | ||
|
|
c0b6626301 | ||
|
|
2f3acc36f2 | ||
|
|
c1b05b9775 | ||
|
|
3445d4ed8e | ||
|
|
b9b98dad7d | ||
|
|
d5fbff5042 | ||
|
|
97b30b2abb | ||
|
|
07ae6fba2d | ||
|
|
f66897289b | ||
|
|
a3e33edec6 | ||
|
|
10240b559c | ||
|
|
c4cb632185 | ||
|
|
9a0fad5f3f | ||
|
|
ae5b7305c8 | ||
|
|
3d4401cdcc | ||
|
|
a4148f673b | ||
|
|
63cca38fcb | ||
|
|
812bd6f782 | ||
|
|
8d3cae82a5 | ||
|
|
f21dde77b6 | ||
|
|
1a9215673b | ||
|
|
d4c73b603f | ||
|
|
69631c7062 | ||
|
|
7318f34cbb | ||
|
|
88b1776ad9 | ||
|
|
461e501267 | ||
|
|
23e85d5c9e | ||
|
|
42eb593f5a | ||
|
|
676ef788b1 | ||
|
|
2e341f8d55 | ||
|
|
0040c1b6e1 | ||
|
|
1c59dfdb44 | ||
|
|
3943474c09 | ||
|
|
e679781981 | ||
|
|
b2fde8f26a | ||
|
|
9ff10e1f34 | ||
|
|
227c3f4f4e | ||
|
|
c7e9957749 | ||
|
|
ba8e39e8b1 | ||
|
|
2895630770 | ||
|
|
ea0672d8bc | ||
|
|
c6000ab54c | ||
|
|
ba7b6e685a | ||
|
|
6be51a40d6 | ||
|
|
4d3a8fb857 | ||
|
|
edb462592d | ||
|
|
6de65c75f1 | ||
|
|
0fd4bd4919 | ||
|
|
0867671a15 | ||
|
|
ee37b68604 | ||
|
|
5af2fa9cf2 | ||
|
|
9671eccd2b | ||
|
|
d186abe251 | ||
|
|
f10eba7f90 | ||
|
|
9b60c44e4d | ||
|
|
1976ed403a | ||
|
|
299b094f54 | ||
|
|
d1f619b861 | ||
|
|
8343f29562 | ||
|
|
2026cb3c6a | ||
|
|
2ac1dd29ad | ||
|
|
ed5d3d2cf9 | ||
|
|
12ed0d0f30 | ||
|
|
885a4e3c13 | ||
|
|
d5b944e00d | ||
|
|
0577e7c347 | ||
|
|
af690832eb | ||
|
|
6a5f6b3320 | ||
|
|
043460d4e5 | ||
|
|
4b36b8eb1f | ||
|
|
a9178ed44c | ||
|
|
9bff8d2d09 | ||
|
|
004ff933ba | ||
|
|
7caf676571 | ||
|
|
48459b8a8b | ||
|
|
2abbb482da | ||
|
|
7ab37efc0c | ||
|
|
f304d1e492 | ||
|
|
38d3252b2e | ||
|
|
a91fd8cccd | ||
|
|
1a3c690f79 | ||
|
|
7e7290393c | ||
|
|
e403cb34f9 | ||
|
|
8c26dc72b4 | ||
|
|
224b8c5c42 | ||
|
|
705f0cadea | ||
|
|
be2ab900dc | ||
|
|
9f1041d7ef | ||
|
|
4883646f39 | ||
|
|
6b381d46a8 | ||
|
|
6a7b28c294 | ||
|
|
2a46041f07 | ||
|
|
781f16286d | ||
|
|
6ad2c83fa0 | ||
|
|
efd63a0151 | ||
|
|
cef947d2f8 | ||
|
|
baaa6c989f | ||
|
|
6e8ed2cab6 | ||
|
|
f374a927e4 | ||
|
|
288dfeae5c | ||
|
|
86bc0b085e | ||
|
|
b492897999 | ||
|
|
648ad78e07 | ||
|
|
76322cc757 | ||
|
|
8bc6c473f4 | ||
|
|
298906cae0 | ||
|
|
9a658d6206 | ||
|
|
e819d917ac | ||
|
|
529c5eb4c1 | ||
|
|
3d150ac002 | ||
|
|
390318c038 | ||
|
|
bbcd66eec8 | ||
|
|
0f427c059d | ||
|
|
8eb8b37abd | ||
|
|
9585ec23c5 | ||
|
|
36d1ba049a | ||
|
|
d0df4d77f3 | ||
|
|
e994907970 | ||
|
|
99134ee3ae | ||
|
|
3d27413b8f | ||
|
|
7acb853e1a | ||
|
|
65bec75b6b | ||
|
|
d1511db529 | ||
|
|
7aee08ed32 | ||
|
|
3a984f5591 | ||
|
|
01ed124f98 | ||
|
|
2ccf3ce351 | ||
|
|
4468012c9b | ||
|
|
0614e146ad | ||
|
|
0f3c8dc344 | ||
|
|
dfb501f8b9 | ||
|
|
894e607efb | ||
|
|
d40ee1047c | ||
|
|
2e176374a2 | ||
|
|
3122af7263 | ||
|
|
395d730dbf | ||
|
|
c9fa3ef456 | ||
|
|
fe2a86ba55 | ||
|
|
30f963a23e | ||
|
|
51d7079534 | ||
|
|
b2476b420b | ||
|
|
d423943439 | ||
|
|
41d5994a02 | ||
|
|
8956fe6033 | ||
|
|
246bd17228 | ||
|
|
2d7d6bff3d | ||
|
|
fc8646041e | ||
|
|
c6ba604d61 | ||
|
|
7916149cf2 | ||
|
|
d96a090538 | ||
|
|
6755602bb5 | ||
|
|
5ef778271e | ||
|
|
62f0ac2f38 | ||
|
|
c222674b47 | ||
|
|
67901ef8dc | ||
|
|
8be35384e1 | ||
|
|
b0707c0213 | ||
|
|
58f20c5b1a | ||
|
|
60c32433a4 | ||
|
|
86bc9b779c | ||
|
|
5d9f790002 | ||
|
|
70169b70aa | ||
|
|
65833dfd52 | ||
|
|
cefa556f79 | ||
|
|
8489a2d8ff | ||
|
|
8a275fb6b9 | ||
|
|
4bfdbffbeb | ||
|
|
2b60558abb | ||
|
|
24b9f54cc5 | ||
|
|
11f4882ad4 | ||
|
|
5f8845d02a | ||
|
|
43a136137c | ||
|
|
d45ffc5ec4 | ||
|
|
6b21a283d4 | ||
|
|
f934516908 | ||
|
|
1c20ed9552 | ||
|
|
fbcc3e1943 | ||
|
|
1fa4b9f613 | ||
|
|
48b3f877ee | ||
|
|
5bd4a9db2d | ||
|
|
785a47f2b9 | ||
|
|
28c6136d80 | ||
|
|
ebcb6dc4a2 | ||
|
|
48324c7d1d | ||
|
|
47303dec84 | ||
|
|
b757e5b7ed | ||
|
|
89a33bc8f8 | ||
|
|
f0d9e43414 | ||
|
|
c98b11d9e8 | ||
|
|
41e4b091f8 | ||
|
|
951c968694 | ||
|
|
a7dca29c19 | ||
|
|
6f6c615d16 | ||
|
|
2227f85db5 | ||
|
|
c4b32e22ae | ||
|
|
373cc52c05 | ||
|
|
7c75bff214 | ||
|
|
252b4bde2d | ||
|
|
0fb30ca51f | ||
|
|
ec3774d5aa | ||
|
|
dd259d56a4 | ||
|
|
352c4ee7a0 | ||
|
|
3b9d37bbc9 | ||
|
|
61e0d40e5a | ||
|
|
3865d760ba | ||
|
|
d2e7048d02 | ||
|
|
b914b58b23 | ||
|
|
2011dae77c | ||
|
|
3389389881 | ||
|
|
549274083b | ||
|
|
0dfe1ddf6a | ||
|
|
986860e054 | ||
|
|
9ea7c8db21 | ||
|
|
f8d30fc8a8 | ||
|
|
9a7326cd0a | ||
|
|
de61f2385f | ||
|
|
2a21492f96 | ||
|
|
4895e0e8ba | ||
|
|
77dba21d63 | ||
|
|
dee2827e3f | ||
|
|
3c50d4608e | ||
|
|
d58182dd9f | ||
|
|
3c9d98b4ef | ||
|
|
b6bb875af4 | ||
|
|
7627d187c8 | ||
|
|
a55bba4d0c | ||
|
|
187d3eb8fd | ||
|
|
4eb0e95032 | ||
|
|
84eb1b07cd | ||
|
|
4dcb2e5c7b | ||
|
|
86f6cd72aa | ||
|
|
3da58b6eee | ||
|
|
19adc38247 | ||
|
|
f15ce29d7e | ||
|
|
7bf9a03eec | ||
|
|
3fe863a735 | ||
|
|
9c8ed2a089 | ||
|
|
2158899983 | ||
|
|
68618d2bdb | ||
|
|
2bad1f5f28 |
36
.github/renovate.json
vendored
Normal file
36
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"datasources": [
|
||||
"go"
|
||||
],
|
||||
"extends": [
|
||||
":automergeDigest",
|
||||
":automergeMinor",
|
||||
":automergePr"
|
||||
],
|
||||
"postUpdateOptions": [
|
||||
"gomodTidy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasources": [
|
||||
"npm"
|
||||
],
|
||||
"extends": [
|
||||
":automergeDigest",
|
||||
":automergeMinor",
|
||||
":automergePr"
|
||||
]
|
||||
}
|
||||
],
|
||||
"assignees": [
|
||||
"cugu"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"sass-loader"
|
||||
]
|
||||
}
|
||||
18
.github/stale.yml
vendored
Normal file
18
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- feature
|
||||
- bug
|
||||
- enhancement
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
119
.github/workflows/ci.yml
vendored
119
.github/workflows/ci.yml
vendored
@@ -9,20 +9,31 @@ env:
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
env: { GIN_MODE: test }
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with: { go-version: '1.17' }
|
||||
- uses: actions/setup-node@v2
|
||||
with: { node-version: '14' }
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with: { go-version: '1.19', cache: true }
|
||||
- run: |
|
||||
mkdir -p ui/dist/img
|
||||
touch ui/dist/index.html ui/dist/favicon.ico ui/dist/manifest.json ui/dist/img/fake.png
|
||||
- run: docker-compose up -d
|
||||
- uses: golangci/golangci-lint-action@v3
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with: { node-version: '14', cache: 'yarn', cache-dependency-path: 'ui/yarn.lock' }
|
||||
- uses: actions/setup-go@v3
|
||||
with: { go-version: '1.19', cache: true }
|
||||
- run: |
|
||||
mkdir -p ui/dist/img
|
||||
touch ui/dist/index.html ui/dist/favicon.ico ui/dist/manifest.json ui/dist/img/fake.png
|
||||
- run: docker compose -f docker-compose-with-keycloak.yml up --quiet-pull --detach
|
||||
working-directory: dev
|
||||
- name: Install ArangoDB
|
||||
run: |
|
||||
@@ -32,17 +43,75 @@ jobs:
|
||||
sudo apt-get update -y && sudo apt-get -y install arangodb3
|
||||
- run: go test -coverprofile=cover.out -coverpkg=./... ./...
|
||||
- run: go tool cover -func=cover.out
|
||||
- uses: codecov/codecov-action@v3
|
||||
|
||||
cypress:
|
||||
strategy:
|
||||
matrix:
|
||||
test: [ tickets, templates, playbooks ]
|
||||
auth: [ keycloak ] # simple
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with: { go-version: '1.18' }
|
||||
- uses: actions/setup-node@v3
|
||||
with: { node-version: '14' }
|
||||
# run UI
|
||||
- run: |
|
||||
yarn install
|
||||
yarn serve &
|
||||
working-directory: ui
|
||||
- run: curl --head -X GET --retry 60 --retry-connrefused --retry-delay 10 http://localhost:8080
|
||||
# run containers
|
||||
- run: |
|
||||
sed -i 's/host.docker.internal/172.17.0.1/g' dev/nginx.conf
|
||||
sed -i 's/host.docker.internal/172.17.0.1/g' dev/nginx-with-keycloak.conf
|
||||
- run: docker compose up --quiet-pull --detach
|
||||
working-directory: dev
|
||||
if: matrix.auth == 'simple'
|
||||
- run: docker compose -f docker-compose-with-keycloak.yml up --quiet-pull --detach
|
||||
working-directory: dev
|
||||
if: matrix.auth == 'keycloak'
|
||||
- run: curl --head -X GET --retry 60 --retry-connrefused --retry-delay 10 http://localhost:9002/auth/realms/catalyst
|
||||
if: matrix.auth == 'keycloak'
|
||||
# run catalyst
|
||||
- run: |
|
||||
mkdir -p ui/dist/img
|
||||
touch ui/dist/index.html ui/dist/favicon.ico ui/dist/manifest.json ui/dist/img/fake.png
|
||||
- run: go mod download
|
||||
- run: bash start_dev.sh &
|
||||
working-directory: dev
|
||||
if: matrix.auth == 'simple'
|
||||
- run: bash start_dev_with_keycloak.sh &
|
||||
working-directory: dev
|
||||
if: matrix.auth == 'keycloak'
|
||||
- run: curl --head -X GET --retry 60 --retry-connrefused --retry-delay 10 http://localhost:8000
|
||||
# run cypress
|
||||
- uses: cypress-io/github-action@v4
|
||||
env:
|
||||
CYPRESS_AUTH: ${{ matrix.auth }}
|
||||
CYPRESS_TEST: ${{ matrix.test }}
|
||||
with:
|
||||
browser: chrome
|
||||
working-directory: ui
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always() && matrix.auth == 'simple'
|
||||
with:
|
||||
name: cypress-videos
|
||||
path: ui/cypress/videos
|
||||
retention-days: 1
|
||||
|
||||
build-npm:
|
||||
name: Build npm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-node@v2
|
||||
with: { node-version: '14' }
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with: { node-version: '14', cache: 'yarn', cache-dependency-path: 'ui/yarn.lock' }
|
||||
- run: yarn install && yarn build
|
||||
working-directory: ui
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with: { name: ui, path: ui/dist, retention-days: 1 }
|
||||
|
||||
build:
|
||||
@@ -51,28 +120,28 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build-npm, test ]
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with: { go-version: '1.17' }
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with: { go-version: '1.19', cache: true }
|
||||
- uses: actions/download-artifact@v3
|
||||
with: { name: ui, path: ui/dist }
|
||||
- name: Version
|
||||
if: github.ref_type == 'tag' && github.ref_name != ''
|
||||
run: |
|
||||
echo ${{ github.ref_name }}
|
||||
echo ${{ github.ref_name }} > VERSION
|
||||
- run: go build -o catalyst ./cmd/catalyst/.
|
||||
- uses: docker/login-action@v1
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Version
|
||||
if: ${{ github.ref != '' }}
|
||||
run: |
|
||||
echo ${{ github.ref_name }}
|
||||
echo ${{ github.ref_name }} > VERSION
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- uses: docker/build-push-action@v2
|
||||
- uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
121
.golangci.yml
Normal file
121
.golangci.yml
Normal file
@@ -0,0 +1,121 @@
|
||||
run:
|
||||
go: "1.19"
|
||||
timeout: 5m
|
||||
skip-dirs:
|
||||
- generated
|
||||
- internal
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- containedctx
|
||||
- decorder
|
||||
- depguard
|
||||
- dogsled
|
||||
- durationcheck
|
||||
- errchkjson
|
||||
- errname
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- gci
|
||||
- gocritic
|
||||
- godot
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goheader
|
||||
- goimports
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- grouper
|
||||
- importas
|
||||
- ireturn
|
||||
- misspell
|
||||
- nakedret
|
||||
- nilnil
|
||||
- nlreturn
|
||||
- nolintlint
|
||||
- paralleltest
|
||||
- predeclared
|
||||
- promlinter
|
||||
- revive
|
||||
- tenv
|
||||
- thelper
|
||||
- unconvert
|
||||
- whitespace
|
||||
|
||||
disable:
|
||||
# go 1.18
|
||||
- bodyclose
|
||||
- contextcheck
|
||||
- gosimple
|
||||
- ifshort
|
||||
- nilerr
|
||||
- noctx
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- tparallel
|
||||
- unparam
|
||||
- unused
|
||||
- wastedassign
|
||||
|
||||
# complexity
|
||||
- cyclop
|
||||
- gocognit
|
||||
- gocyclo
|
||||
- maintidx
|
||||
- nestif
|
||||
|
||||
# disable
|
||||
- dupl
|
||||
- exhaustivestruct
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- goconst
|
||||
- godox
|
||||
- goerr113
|
||||
- gomnd
|
||||
- gomoddirectives
|
||||
- lll
|
||||
- makezero
|
||||
- prealloc
|
||||
- structcheck
|
||||
- tagliatelle
|
||||
- testpackage
|
||||
- varnamelen
|
||||
- wrapcheck
|
||||
- wsl
|
||||
linters-settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(github.com/SecurityBrewery/catalyst)
|
||||
ireturn:
|
||||
allow:
|
||||
- error
|
||||
- context.Context
|
||||
- go-driver.Cursor
|
||||
- go-driver.Collection
|
||||
- go-driver.Database
|
||||
- go-driver.Client
|
||||
- chi.Router
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: caql
|
||||
text: "var-naming: don't use underscores"
|
||||
- path: database/user.go
|
||||
text: "G404"
|
||||
linters: [ gosec ]
|
||||
- path: caql/function.go
|
||||
text: "G404"
|
||||
linters: [ gosec ]
|
||||
- path: caql
|
||||
linters: [ forcetypeassert ]
|
||||
- text: github.com/go-chi/chi/v5.Router
|
||||
linters: [ ireturn ]
|
||||
@@ -72,12 +72,17 @@ Automations are scripts that automate tasks or enrich artifacts. Automations are
|
||||
run in their own Docker containers. This enables them to be created in different
|
||||
scripting languages and run securely in their own environment.
|
||||
|
||||
### Users
|
||||
### Dashboards
|
||||
|
||||
<center>
|
||||
<img alt="Screenshot of the playbook part of a ticket" src="docs/screenshots/roles.png" />
|
||||
<img alt="Screenshot of the dashboard editor" src="docs/screenshots/dashboard.png" />
|
||||
</center>
|
||||
|
||||
Catalyst comes with a dashboard editor that allows you to create custom dashboards
|
||||
for your organisation. Dashboards can be created with line, bar, and pie charts.
|
||||
|
||||
### Users
|
||||
|
||||
Catalyst has two different types of users, normal users accessing the platform
|
||||
via OIDC authentication and API keys for external script. A
|
||||
fine-grained access model is available for both types and allows to define
|
||||
|
||||
380
auth.go
380
auth.go
@@ -3,356 +3,106 @@ package catalyst
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/api"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
)
|
||||
|
||||
type AuthConfig struct {
|
||||
OIDCIssuer string
|
||||
OAuth2 *oauth2.Config
|
||||
|
||||
OIDCClaimUsername string
|
||||
OIDCClaimEmail string
|
||||
// OIDCClaimGroups string
|
||||
OIDCClaimName string
|
||||
AuthBlockNew bool
|
||||
AuthDefaultRoles []role.Role
|
||||
AuthAdminUsers []string
|
||||
|
||||
provider *oidc.Provider
|
||||
type catalystResolver struct {
|
||||
database *database.Database
|
||||
}
|
||||
|
||||
func (c *AuthConfig) Verifier(ctx context.Context) (*oidc.IDTokenVerifier, error) {
|
||||
if c.provider == nil {
|
||||
err := c.Load(ctx)
|
||||
func newCatalystResolver(db *database.Database) *catalystResolver {
|
||||
return &catalystResolver{
|
||||
database: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *catalystResolver) UserCreateIfNotExists(ctx context.Context, user *maut.User, password string) (err error) {
|
||||
if user != nil {
|
||||
if _, err := c.database.UserGet(ctx, user.ID); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if user == nil || user.APIKey {
|
||||
_, err = c.database.UserCreateSetupAPIKey(ctx, password)
|
||||
} else {
|
||||
_, err = c.database.UserCreate(ctx, &model.UserForm{
|
||||
Apikey: user.APIKey,
|
||||
Blocked: user.Blocked,
|
||||
ID: user.ID,
|
||||
Password: &password,
|
||||
Roles: user.Roles,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.provider.Verifier(&oidc.Config{SkipClientIDCheck: true}), nil
|
||||
}
|
||||
|
||||
func (c *AuthConfig) Load(ctx context.Context) error {
|
||||
provider, err := oidc.NewProvider(ctx, c.OIDCIssuer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.provider = provider
|
||||
c.OAuth2.Endpoint = provider.Endpoint()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Authenticate(db *database.Database, config *AuthConfig) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
keyHeader := r.Header.Get("PRIVATE-TOKEN")
|
||||
authHeader := r.Header.Get("User")
|
||||
|
||||
switch {
|
||||
case keyHeader != "":
|
||||
keyAuth(db, keyHeader)(next).ServeHTTP(w, r)
|
||||
case authHeader != "":
|
||||
iss := config.OIDCIssuer
|
||||
bearerAuth(db, authHeader, iss, config)(next).ServeHTTP(w, r)
|
||||
default:
|
||||
sessionAuth(db, config)(next).ServeHTTP(w, r)
|
||||
}
|
||||
err = c.database.UserDataCreate(ctx, user.ID, &model.UserData{
|
||||
Email: user.Email,
|
||||
Image: nil,
|
||||
Name: user.Name,
|
||||
Timeformat: nil,
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func bearerAuth(db *database.Database, authHeader string, iss string, config *AuthConfig) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
api.JSONErrorStatus(w, http.StatusUnauthorized, errors.New("no bearer token"))
|
||||
return
|
||||
}
|
||||
|
||||
claims, apiError := verifyClaims(r, config, authHeader[7:])
|
||||
if apiError != nil {
|
||||
api.JSONErrorStatus(w, apiError.Status, apiError.Internal)
|
||||
return
|
||||
}
|
||||
|
||||
// if claims.Iss != iss {
|
||||
// return &api.HTTPError{Status: http.StatusInternalServerError, Internal: "wrong issuer"})
|
||||
// return
|
||||
// }
|
||||
|
||||
setClaimsCookie(w, claims)
|
||||
|
||||
r, err := setContextClaims(r, db, claims, config)
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not load user: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func keyAuth(db *database.Database, keyHeader string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h := fmt.Sprintf("%x", sha256.Sum256([]byte(keyHeader)))
|
||||
|
||||
key, err := db.UserByHash(r.Context(), h)
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not verify private token: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
r = setContextUser(r, key, db.Hooks)
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func sessionAuth(db *database.Database, config *AuthConfig) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, noCookie, err := claimsCookie(r)
|
||||
if err != nil {
|
||||
api.JSONError(w, err)
|
||||
return
|
||||
}
|
||||
if noCookie {
|
||||
redirectToLogin(w, r, config.OAuth2)
|
||||
return
|
||||
}
|
||||
|
||||
r, err = setContextClaims(r, db, claims, config)
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not load user: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setContextClaims(r *http.Request, db *database.Database, claims map[string]interface{}, config *AuthConfig) (*http.Request, error) {
|
||||
newUser, newSetting, err := mapUserAndSettings(claims, config)
|
||||
func (c *catalystResolver) User(ctx context.Context, userID string) (*maut.User, error) {
|
||||
user, err := c.database.UserGet(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, ok := busdb.UserFromContext(r.Context()); !ok {
|
||||
r = busdb.SetContext(r, &model.UserResponse{ID: "auth", Roles: []string{role.Admin}, Apikey: false, Blocked: false})
|
||||
}
|
||||
return mapMautUser(user), nil
|
||||
}
|
||||
|
||||
user, err := db.UserGetOrCreate(r.Context(), newUser)
|
||||
func (c *catalystResolver) UserAPIKeyByHash(ctx context.Context, key string) (*maut.User, error) {
|
||||
sha256Hash := fmt.Sprintf("%x", sha256.Sum256([]byte(key)))
|
||||
user, err := c.database.UserAPIKeyByHash(ctx, sha256Hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = db.UserDataGetOrCreate(r.Context(), newUser.ID, newSetting)
|
||||
return mapMautUser(user), nil
|
||||
}
|
||||
|
||||
func (c *catalystResolver) UserByIDAndPassword(ctx context.Context, username string, password string) (*maut.User, error) {
|
||||
user, err := c.database.UserByIDAndPassword(ctx, username, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return setContextUser(r, user, db.Hooks), nil
|
||||
return mapMautUser(user), nil
|
||||
}
|
||||
|
||||
func setContextUser(r *http.Request, user *model.UserResponse, hooks *hooks.Hooks) *http.Request {
|
||||
groups, err := hooks.GetGroups(r.Context(), user.ID)
|
||||
if err == nil {
|
||||
r = busdb.SetGroupContext(r, groups)
|
||||
func (c *catalystResolver) Role(ctx context.Context, roleID string) (r *maut.Role, err error) {
|
||||
switch roleID {
|
||||
case "admin":
|
||||
return Admin, nil
|
||||
case "engineer":
|
||||
return engineer, nil
|
||||
case "analyst":
|
||||
return analyst, nil
|
||||
}
|
||||
|
||||
return busdb.SetContext(r, user)
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func mapUserAndSettings(claims map[string]interface{}, config *AuthConfig) (*model.UserForm, *model.UserData, error) {
|
||||
// handle Bearer tokens
|
||||
// if typ, ok := claims["typ"]; ok && typ == "Bearer" {
|
||||
// return &model.User{
|
||||
// Username: "bot",
|
||||
// Blocked: false,
|
||||
// Email: pointer.String("bot@example.org"),
|
||||
// Roles: []string{"user:read", "settings:read", "ticket", "backup:read", "backup:restore"},
|
||||
// Name: pointer.String("Bot"),
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
username, err := getString(claims, config.OIDCClaimUsername)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
email, err := getString(claims, config.OIDCClaimEmail)
|
||||
if err != nil {
|
||||
email = ""
|
||||
}
|
||||
|
||||
name, err := getString(claims, config.OIDCClaimName)
|
||||
if err != nil {
|
||||
name = ""
|
||||
}
|
||||
|
||||
var roles = role.Strings(config.AuthDefaultRoles)
|
||||
if contains(config.AuthAdminUsers, username) {
|
||||
roles = append(roles, role.Admin)
|
||||
}
|
||||
|
||||
return &model.UserForm{
|
||||
ID: username,
|
||||
Blocked: config.AuthBlockNew,
|
||||
Roles: roles,
|
||||
}, &model.UserData{
|
||||
Email: &email,
|
||||
Name: &name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func contains(l []string, s string) bool {
|
||||
for _, e := range l {
|
||||
if e == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getString(m map[string]interface{}, key string) (string, error) {
|
||||
if v, ok := m[key]; ok {
|
||||
if s, ok := v.(string); ok {
|
||||
return s, nil
|
||||
}
|
||||
return "", fmt.Errorf("mapping of %s failed, wrong type (%T)", key, v)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("mapping of %s failed, missing value", key)
|
||||
}
|
||||
|
||||
func redirectToLogin(w http.ResponseWriter, r *http.Request, oauth2Config *oauth2.Config) {
|
||||
state, err := state()
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("generating state failed"))
|
||||
return
|
||||
}
|
||||
|
||||
setStateCookie(w, state)
|
||||
|
||||
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
func AuthorizeBlockedUser() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := busdb.UserFromContext(r.Context())
|
||||
if !ok {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("no user in context"))
|
||||
return
|
||||
}
|
||||
|
||||
if user.Blocked {
|
||||
api.JSONErrorStatus(w, http.StatusForbidden, errors.New("user is blocked"))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
func mapMautUser(user *model.UserResponse) *maut.User {
|
||||
return &maut.User{
|
||||
ID: user.ID,
|
||||
APIKey: user.Apikey,
|
||||
Blocked: user.Blocked,
|
||||
// Email: user.Email, // TODO
|
||||
// Groups: user.Groups, // TODO
|
||||
// Name: user.Name, // TODO
|
||||
Roles: user.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func AuthorizeRole(roles []string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := busdb.UserFromContext(r.Context())
|
||||
if !ok {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("no user in context"))
|
||||
return
|
||||
}
|
||||
|
||||
if !role.UserHasRoles(user, role.FromStrings(roles)) {
|
||||
api.JSONErrorStatus(w, http.StatusForbidden, fmt.Errorf("missing role %s has %s", roles, user.Roles))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func callback(config *AuthConfig) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
state, err := stateCookie(r)
|
||||
if err != nil || state == "" {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("state missing"))
|
||||
return
|
||||
}
|
||||
|
||||
if state != r.URL.Query().Get("state") {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("state mismatch"))
|
||||
return
|
||||
}
|
||||
|
||||
oauth2Token, err := config.OAuth2.Exchange(r.Context(), r.URL.Query().Get("code"))
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("oauth2 exchange failed: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the ID Token from OAuth2 token.
|
||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("missing id token"))
|
||||
return
|
||||
}
|
||||
|
||||
claims, apiError := verifyClaims(r, config, rawIDToken)
|
||||
if apiError != nil {
|
||||
api.JSONErrorStatus(w, apiError.Status, apiError.Internal)
|
||||
return
|
||||
}
|
||||
|
||||
setClaimsCookie(w, claims)
|
||||
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func state() (string, error) {
|
||||
rnd := make([]byte, 32)
|
||||
if _, err := rand.Read(rnd); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(rnd), nil
|
||||
}
|
||||
|
||||
func verifyClaims(r *http.Request, config *AuthConfig, rawIDToken string) (map[string]interface{}, *api.HTTPError) {
|
||||
verifier, err := config.Verifier(r.Context())
|
||||
if err != nil {
|
||||
return nil, &api.HTTPError{Status: http.StatusUnauthorized, Internal: fmt.Errorf("could not verify: %w", err)}
|
||||
}
|
||||
authToken, err := verifier.Verify(r.Context(), rawIDToken)
|
||||
if err != nil {
|
||||
return nil, &api.HTTPError{Status: http.StatusInternalServerError, Internal: fmt.Errorf("could not verify bearer token: %w", err)}
|
||||
}
|
||||
|
||||
var claims map[string]interface{}
|
||||
if err := authToken.Claims(&claims); err != nil {
|
||||
return nil, &api.HTTPError{Status: http.StatusInternalServerError, Internal: fmt.Errorf("failed to parse claims: %w", err)}
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ func Backup(catalystStorage *storage.Storage, c *database.Config, writer io.Writ
|
||||
archive := zip.NewWriter(writer)
|
||||
defer archive.Close()
|
||||
|
||||
archive.SetComment(GetVersion())
|
||||
err := archive.SetComment(GetVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// S3
|
||||
if err := backupS3(catalystStorage, archive); err != nil {
|
||||
@@ -86,6 +89,7 @@ func backupS3(catalystStorage *storage.Storage, archive *zip.Writer) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -105,6 +109,7 @@ func backupArango(c *database.Config, archive *zip.Writer) error {
|
||||
|
||||
func zipDump(dir string, archive *zip.Writer) error {
|
||||
fsys := os.DirFS(dir)
|
||||
|
||||
return fs.WalkDir(fsys, ".", func(p string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -127,6 +132,7 @@ func zipDump(dir string, archive *zip.Writer) error {
|
||||
if _, err := io.Copy(a, f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -144,5 +150,6 @@ func arangodump(dir string, config *database.Config) error {
|
||||
"--server.database", name,
|
||||
}
|
||||
cmd := exec.Command("arangodump", args...)
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
104
bus/bus.go
104
bus/bus.go
@@ -1,69 +1,69 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"github.com/arangodb/go-driver"
|
||||
|
||||
emitter "github.com/emitter-io/go/v2"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
|
||||
type ResultMsg struct {
|
||||
Automation string `json:"automation"`
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
Target *model.Origin `json:"target"`
|
||||
}
|
||||
|
||||
type RequestMsg struct {
|
||||
IDs []driver.DocumentID `json:"ids"`
|
||||
Function string `json:"function"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
type JobMsg struct {
|
||||
ID string `json:"id"`
|
||||
Automation string `json:"automation"`
|
||||
Origin *model.Origin `json:"origin"`
|
||||
Message *model.Message `json:"message"`
|
||||
}
|
||||
|
||||
type DatabaseUpdateType string
|
||||
|
||||
const (
|
||||
DatabaseEntryRead DatabaseUpdateType = "read"
|
||||
DatabaseEntryCreated DatabaseUpdateType = "created"
|
||||
DatabaseEntryUpdated DatabaseUpdateType = "updated"
|
||||
)
|
||||
|
||||
type DatabaseUpdateMsg struct {
|
||||
IDs []driver.DocumentID `json:"ids"`
|
||||
Type DatabaseUpdateType `json:"type"`
|
||||
}
|
||||
|
||||
type Bus struct {
|
||||
config *Config
|
||||
client *emitter.Client
|
||||
ResultChannel *Channel[*ResultMsg]
|
||||
RequestChannel *Channel[*RequestMsg]
|
||||
JobChannel *Channel[*JobMsg]
|
||||
DatabaseChannel *Channel[*DatabaseUpdateMsg]
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Host string
|
||||
Key string
|
||||
databaseUpdateBusKey string
|
||||
jobBusKey string
|
||||
resultBusKey string
|
||||
requestKey string
|
||||
APIUrl string
|
||||
func New() *Bus {
|
||||
return &Bus{
|
||||
ResultChannel: &Channel[*ResultMsg]{},
|
||||
RequestChannel: &Channel[*RequestMsg]{},
|
||||
JobChannel: &Channel[*JobMsg]{},
|
||||
DatabaseChannel: &Channel[*DatabaseUpdateMsg]{},
|
||||
}
|
||||
}
|
||||
|
||||
func New(c *Config) (*Bus, error) {
|
||||
client, err := emitter.Connect(c.Host, func(_ *emitter.Client, msg emitter.Message) {
|
||||
log.Printf("received: '%s' topic: '%s'\n", msg.Payload(), msg.Topic())
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.databaseUpdateBusKey, err = client.GenerateKey(c.Key, channelDatabaseUpdate+"/", "rwls", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.jobBusKey, err = client.GenerateKey(c.Key, channelJob+"/", "rwls", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.resultBusKey, err = client.GenerateKey(c.Key, channelResult+"/", "rwls", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.requestKey, err = client.GenerateKey(c.Key, ChannelRequest+"/", "rwls", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Bus{config: c, client: client}, err
|
||||
type Channel[T any] struct {
|
||||
Subscriber []func(T)
|
||||
}
|
||||
|
||||
func (b *Bus) jsonPublish(msg interface{}, channel, key string) error {
|
||||
payload, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
func (c *Channel[T]) Publish(msg T) {
|
||||
for _, s := range c.Subscriber {
|
||||
go s(msg)
|
||||
}
|
||||
|
||||
return b.client.Publish(key, channel, payload)
|
||||
}
|
||||
|
||||
func (b *Bus) safeSubscribe(key, channel string, handler func(c *emitter.Client, m emitter.Message)) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("Recovered %s in channel %s\n", r, channel)
|
||||
}
|
||||
}()
|
||||
return b.client.Subscribe(key, channel, handler)
|
||||
func (c *Channel[T]) Subscribe(handler func(T)) {
|
||||
c.Subscriber = append(c.Subscriber, handler)
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
emitter "github.com/emitter-io/go/v2"
|
||||
)
|
||||
|
||||
const channelDatabaseUpdate = "databaseupdate"
|
||||
|
||||
type DatabaseUpdateType string
|
||||
|
||||
const (
|
||||
DatabaseEntryRead DatabaseUpdateType = "read"
|
||||
DatabaseEntryCreated DatabaseUpdateType = "created"
|
||||
DatabaseEntryUpdated DatabaseUpdateType = "updated"
|
||||
)
|
||||
|
||||
type DatabaseUpdateMsg struct {
|
||||
IDs []driver.DocumentID `json:"ids"`
|
||||
Type DatabaseUpdateType `json:"type"`
|
||||
}
|
||||
|
||||
func (b *Bus) PublishDatabaseUpdate(ids []driver.DocumentID, databaseUpdateType DatabaseUpdateType) error {
|
||||
return b.jsonPublish(&DatabaseUpdateMsg{
|
||||
IDs: ids,
|
||||
Type: databaseUpdateType,
|
||||
}, channelDatabaseUpdate, b.config.databaseUpdateBusKey)
|
||||
}
|
||||
|
||||
func (b *Bus) SubscribeDatabaseUpdate(f func(msg *DatabaseUpdateMsg)) error {
|
||||
return b.safeSubscribe(b.config.databaseUpdateBusKey, channelDatabaseUpdate, func(c *emitter.Client, m emitter.Message) {
|
||||
var msg DatabaseUpdateMsg
|
||||
if err := json.Unmarshal(m.Payload(), &msg); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
go f(&msg)
|
||||
})
|
||||
}
|
||||
42
bus/job.go
42
bus/job.go
@@ -1,42 +0,0 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
emitter "github.com/emitter-io/go/v2"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
|
||||
const channelJob = "job"
|
||||
|
||||
type JobMsg struct {
|
||||
ID string `json:"id"`
|
||||
Automation string `json:"automation"`
|
||||
Origin *model.Origin `json:"origin"`
|
||||
Message *model.Message `json:"message"`
|
||||
}
|
||||
|
||||
func (b *Bus) PublishJob(id, automation string, payload interface{}, context *model.Context, origin *model.Origin) error {
|
||||
return b.jsonPublish(&JobMsg{
|
||||
ID: id,
|
||||
Automation: automation,
|
||||
Origin: origin,
|
||||
Message: &model.Message{
|
||||
Context: context,
|
||||
Payload: payload,
|
||||
},
|
||||
}, channelJob, b.config.jobBusKey)
|
||||
}
|
||||
|
||||
func (b *Bus) SubscribeJob(f func(msg *JobMsg)) error {
|
||||
return b.safeSubscribe(b.config.jobBusKey, channelJob, func(c *emitter.Client, m emitter.Message) {
|
||||
var msg JobMsg
|
||||
if err := json.Unmarshal(m.Payload(), &msg); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
go f(&msg)
|
||||
})
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
emitter "github.com/emitter-io/go/v2"
|
||||
)
|
||||
|
||||
const ChannelRequest = "request"
|
||||
|
||||
type RequestMsg struct {
|
||||
IDs []driver.DocumentID `json:"ids"`
|
||||
Function string `json:"function"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
func (b *Bus) PublishRequest(user, f string, ids []driver.DocumentID) error {
|
||||
return b.jsonPublish(&RequestMsg{
|
||||
User: user,
|
||||
Function: f,
|
||||
IDs: ids,
|
||||
}, ChannelRequest, b.config.requestKey)
|
||||
}
|
||||
|
||||
func (b *Bus) SubscribeRequest(f func(msg *RequestMsg)) error {
|
||||
return b.safeSubscribe(b.config.requestKey, ChannelRequest, func(c *emitter.Client, m emitter.Message) {
|
||||
msg := &RequestMsg{}
|
||||
if err := json.Unmarshal(m.Payload(), msg); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
go f(msg)
|
||||
})
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
emitter "github.com/emitter-io/go/v2"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
|
||||
const channelResult = "result"
|
||||
|
||||
type ResultMsg struct {
|
||||
Automation string `json:"automation"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Target *model.Origin `json:"target"`
|
||||
}
|
||||
|
||||
func (b *Bus) PublishResult(automation string, data map[string]interface{}, target *model.Origin) error {
|
||||
return b.jsonPublish(&ResultMsg{Automation: automation, Data: data, Target: target}, channelResult, b.config.resultBusKey)
|
||||
}
|
||||
|
||||
func (b *Bus) SubscribeResult(f func(msg *ResultMsg)) error {
|
||||
return b.safeSubscribe(b.config.resultBusKey, channelResult, func(c *emitter.Client, m emitter.Message) {
|
||||
msg := &ResultMsg{}
|
||||
if err := json.Unmarshal(m.Payload(), msg); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
go f(msg)
|
||||
})
|
||||
}
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/generated/time"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
)
|
||||
|
||||
type busService struct {
|
||||
@@ -20,34 +20,26 @@ type busService struct {
|
||||
network string
|
||||
}
|
||||
|
||||
func New(apiURL, apikey, network string, catalystBus *bus.Bus, db *database.Database) error {
|
||||
|
||||
func New(apiURL, apikey, network string, catalystBus *bus.Bus, db *database.Database) {
|
||||
h := &busService{db: db, apiURL: apiURL, apiKey: apikey, network: network, catalystBus: catalystBus}
|
||||
|
||||
if err := catalystBus.SubscribeRequest(h.logRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := catalystBus.SubscribeResult(h.handleResult); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := catalystBus.SubscribeJob(h.handleJob); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
catalystBus.RequestChannel.Subscribe(h.logRequest)
|
||||
catalystBus.ResultChannel.Subscribe(h.handleResult)
|
||||
catalystBus.JobChannel.Subscribe(h.handleJob)
|
||||
}
|
||||
|
||||
func busContext() context.Context {
|
||||
// TODO: change roles?
|
||||
bot := &model.UserResponse{ID: "bot", Roles: []string{role.Admin}}
|
||||
return busdb.UserContext(context.Background(), bot)
|
||||
bot := &maut.User{ID: "bot", Roles: []string{maut.AdminRole}}
|
||||
|
||||
return maut.UserContext(context.Background(), bot, nil) // TODO add permissions ?
|
||||
}
|
||||
|
||||
func (h *busService) logRequest(msg *bus.RequestMsg) {
|
||||
var logEntries []*model.LogEntry
|
||||
for _, i := range msg.IDs {
|
||||
logEntries = append(logEntries, &model.LogEntry{
|
||||
Type: bus.ChannelRequest,
|
||||
Type: "request",
|
||||
Reference: i.String(),
|
||||
Creator: msg.User,
|
||||
Message: msg.Function,
|
||||
|
||||
@@ -59,13 +59,15 @@ func pullImage(ctx context.Context, cli *client.Client, image string) (string, e
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
_, err = io.Copy(buf, reader)
|
||||
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
func copyFile(ctx context.Context, cli *client.Client, path string, contentString string, id string) error {
|
||||
tarBuf := &bytes.Buffer{}
|
||||
tw := tar.NewWriter(tarBuf)
|
||||
if err := tw.WriteHeader(&tar.Header{Name: path, Mode: 0755, Size: int64(len(contentString))}); err != nil {
|
||||
header := &tar.Header{Name: path, Mode: 0o755, Size: int64(len(contentString))}
|
||||
if err := tw.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -90,7 +92,12 @@ func runDocker(ctx context.Context, jobID, containerID string, db *database.Data
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer cli.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{Force: true})
|
||||
defer func(cli *client.Client, ctx context.Context, containerID string, options types.ContainerRemoveOptions) {
|
||||
err := cli.ContainerRemove(ctx, containerID, options)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}(cli, ctx, containerID, types.ContainerRemoveOptions{Force: true})
|
||||
|
||||
if err := cli.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil {
|
||||
return nil, nil, err
|
||||
@@ -123,13 +130,16 @@ func streamStdErr(ctx context.Context, cli *client.Client, jobID, containerID st
|
||||
err := scanLines(ctx, jobID, containerLogs, stderrBuf, db)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
if err := containerLogs.Close(); err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
return stderrBuf, nil
|
||||
}
|
||||
|
||||
@@ -139,24 +149,28 @@ func scanLines(ctx context.Context, jobID string, input io.ReadCloser, output io
|
||||
_, err := stdcopy.StdCopy(w, w, input)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
b := s.Bytes()
|
||||
output.Write(b)
|
||||
output.Write([]byte("\n"))
|
||||
_, _ = output.Write(b)
|
||||
_, _ = output.Write([]byte("\n"))
|
||||
|
||||
if err := db.JobLogAppend(ctx, jobID, string(b)+"\n"); err != nil {
|
||||
log.Println(err)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return s.Err()
|
||||
}
|
||||
|
||||
@@ -172,6 +186,7 @@ func waitForContainer(ctx context.Context, cli *client.Client, containerID strin
|
||||
return fmt.Errorf("container returned status code %d: stderr: %s", exitStatus.StatusCode, stderrBuf.String())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,17 +19,20 @@ func (h *busService) handleJob(automationMsg *bus.JobMsg) {
|
||||
})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
automation, err := h.db.AutomationGet(ctx, automationMsg.Automation)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if automation.Script == "" {
|
||||
log.Println("automation is empty")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,11 +42,17 @@ func (h *busService) handleJob(automationMsg *bus.JobMsg) {
|
||||
automationMsg.Message.Secrets["catalyst_apikey"] = h.apiKey
|
||||
automationMsg.Message.Secrets["catalyst_apiurl"] = h.apiURL
|
||||
|
||||
scriptMessage, _ := json.Marshal(automationMsg.Message)
|
||||
scriptMessage, err := json.Marshal(automationMsg.Message)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
containerID, logs, err := createContainer(ctx, automation.Image, automation.Script, string(scriptMessage), h.network)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,29 +64,29 @@ func (h *busService) handleJob(automationMsg *bus.JobMsg) {
|
||||
Status: job.Status,
|
||||
}); err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
var result map[string]any
|
||||
|
||||
stdout, _, err := runDocker(ctx, automationMsg.ID, containerID, h.db)
|
||||
if err != nil {
|
||||
result = map[string]interface{}{"error": fmt.Sprintf("error running script %s %s", err, string(stdout))}
|
||||
result = map[string]any{"error": fmt.Sprintf("error running script %s %s", err, string(stdout))}
|
||||
} else {
|
||||
var data map[string]interface{}
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal(stdout, &data); err != nil {
|
||||
result = map[string]interface{}{"error": string(stdout)}
|
||||
result = map[string]any{"error": string(stdout)}
|
||||
} else {
|
||||
result = data
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.catalystBus.PublishResult(automationMsg.Automation, result, automationMsg.Origin); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
h.catalystBus.ResultChannel.Publish(&bus.ResultMsg{Automation: automationMsg.Automation, Data: result, Target: automationMsg.Origin})
|
||||
|
||||
if err := h.db.JobComplete(ctx, automationMsg.ID, result); err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/SecurityBrewery/catalyst/generated/caql/parser"
|
||||
)
|
||||
|
||||
var TooComplexError = errors.New("unsupported features for index queries, use advanced search instead")
|
||||
var ErrTooComplex = errors.New("unsupported features for index queries, use advanced search instead")
|
||||
|
||||
type bleveBuilder struct {
|
||||
*parser.BaseCAQLParserListener
|
||||
@@ -35,8 +35,9 @@ func (s *bleveBuilder) pop() (n string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *bleveBuilder) binaryPop() (interface{}, interface{}) {
|
||||
func (s *bleveBuilder) binaryPop() (any, any) {
|
||||
right, left := s.pop(), s.pop()
|
||||
|
||||
return left, right
|
||||
}
|
||||
|
||||
@@ -48,9 +49,7 @@ func (s *bleveBuilder) ExitExpression(ctx *parser.ExpressionContext) {
|
||||
case ctx.Reference() != nil:
|
||||
// pass
|
||||
case ctx.Operator_unary() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_PLUS() != nil:
|
||||
fallthrough
|
||||
case ctx.T_MINUS() != nil:
|
||||
@@ -60,13 +59,9 @@ func (s *bleveBuilder) ExitExpression(ctx *parser.ExpressionContext) {
|
||||
case ctx.T_DIV() != nil:
|
||||
fallthrough
|
||||
case ctx.T_MOD() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_RANGE() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_LT() != nil && ctx.GetEq_op() == nil:
|
||||
left, right := s.binaryPop()
|
||||
s.push(fmt.Sprintf("%s:<%s", left, right))
|
||||
@@ -79,64 +74,46 @@ func (s *bleveBuilder) ExitExpression(ctx *parser.ExpressionContext) {
|
||||
case ctx.T_GE() != nil && ctx.GetEq_op() == nil:
|
||||
left, right := s.binaryPop()
|
||||
s.push(fmt.Sprintf("%s:>=%s", left, right))
|
||||
|
||||
case ctx.T_IN() != nil && ctx.GetEq_op() == nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_EQ() != nil && ctx.GetEq_op() == nil:
|
||||
left, right := s.binaryPop()
|
||||
s.push(fmt.Sprintf("%s:%s", left, right))
|
||||
case ctx.T_NE() != nil && ctx.GetEq_op() == nil:
|
||||
left, right := s.binaryPop()
|
||||
s.push(fmt.Sprintf("-%s:%s", left, right))
|
||||
|
||||
case ctx.T_ALL() != nil && ctx.GetEq_op() != nil:
|
||||
fallthrough
|
||||
case ctx.T_ANY() != nil && ctx.GetEq_op() != nil:
|
||||
fallthrough
|
||||
case ctx.T_NONE() != nil && ctx.GetEq_op() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_ALL() != nil && ctx.T_NOT() != nil && ctx.T_IN() != nil:
|
||||
fallthrough
|
||||
case ctx.T_ANY() != nil && ctx.T_NOT() != nil && ctx.T_IN() != nil:
|
||||
fallthrough
|
||||
case ctx.T_NONE() != nil && ctx.T_NOT() != nil && ctx.T_IN() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_LIKE() != nil:
|
||||
s.err = errors.New("index queries are like queries by default")
|
||||
return
|
||||
|
||||
case ctx.T_REGEX_MATCH() != nil:
|
||||
left, right := s.binaryPop()
|
||||
if ctx.T_NOT() != nil {
|
||||
s.err = TooComplexError
|
||||
return
|
||||
s.err = ErrTooComplex
|
||||
} else {
|
||||
s.push(fmt.Sprintf("%s:/%s/", left, right))
|
||||
}
|
||||
case ctx.T_REGEX_NON_MATCH() != nil:
|
||||
s.err = errors.New("index query cannot contain regex non matches, use advanced search instead")
|
||||
return
|
||||
|
||||
case ctx.T_AND() != nil:
|
||||
left, right := s.binaryPop()
|
||||
s.push(fmt.Sprintf("%s %s", left, right))
|
||||
case ctx.T_OR() != nil:
|
||||
s.err = errors.New("index query cannot contain OR, use advanced search instead")
|
||||
return
|
||||
|
||||
case ctx.T_QUESTION() != nil && len(ctx.AllExpression()) == 3:
|
||||
s.err = errors.New("index query cannot contain ternary operations, use advanced search instead")
|
||||
return
|
||||
case ctx.T_QUESTION() != nil && len(ctx.AllExpression()) == 2:
|
||||
s.err = errors.New("index query cannot contain ternary operations, use advanced search instead")
|
||||
return
|
||||
|
||||
default:
|
||||
panic("unknown expression")
|
||||
}
|
||||
@@ -152,17 +129,13 @@ func (s *bleveBuilder) ExitReference(ctx *parser.ReferenceContext) {
|
||||
case ctx.T_STRING() != nil:
|
||||
s.push(ctx.T_STRING().GetText())
|
||||
case ctx.Compound_value() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
s.err = ErrTooComplex
|
||||
case ctx.Function_call() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_OPEN() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
s.err = ErrTooComplex
|
||||
case ctx.T_ARRAY_OPEN() != nil:
|
||||
s.err = TooComplexError
|
||||
return
|
||||
s.err = ErrTooComplex
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected value: %s", ctx.GetText()))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package caql
|
||||
package caql_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/caql"
|
||||
)
|
||||
|
||||
func TestBleveBuilder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
saql string
|
||||
@@ -18,15 +22,20 @@ func TestBleveBuilder(t *testing.T) {
|
||||
{name: "Search 4", saql: `title == 'malware' AND 'wannacry'`, wantBleve: `title:"malware" "wannacry"`},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
parser := &Parser{}
|
||||
tt := tt
|
||||
|
||||
parser := &caql.Parser{}
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expr, err := parser.Parse(tt.saql)
|
||||
if (err != nil) != tt.wantParseErr {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
if expr != nil {
|
||||
t.Error(expr.String())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -37,6 +46,7 @@ func TestBleveBuilder(t *testing.T) {
|
||||
if (err != nil) != tt.wantRebuildErr {
|
||||
t.Error(expr.String())
|
||||
t.Errorf("String() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/generated/caql/parser"
|
||||
)
|
||||
|
||||
@@ -40,6 +42,7 @@ func (s *aqlBuilder) pop() (n string) {
|
||||
|
||||
func (s *aqlBuilder) binaryPop() (string, string) {
|
||||
right, left := s.pop(), s.pop()
|
||||
|
||||
return left, right
|
||||
}
|
||||
|
||||
@@ -181,8 +184,10 @@ func (s *aqlBuilder) toBoolString(v string) string {
|
||||
if err != nil {
|
||||
panic("invalid search " + err.Error())
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`d._key IN ["%s"]`, strings.Join(ids, `","`))
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -246,7 +251,7 @@ func (s *aqlBuilder) ExitFunction_call(ctx *parser.Function_callContext) {
|
||||
}
|
||||
parameter := strings.Join(array, ", ")
|
||||
|
||||
if !stringSliceContains(functionNames, strings.ToUpper(ctx.T_STRING().GetText())) {
|
||||
if !slices.Contains(functionNames, strings.ToUpper(ctx.T_STRING().GetText())) {
|
||||
panic("unknown function")
|
||||
}
|
||||
|
||||
|
||||
136
caql/function.go
136
caql/function.go
@@ -16,7 +16,6 @@ import (
|
||||
|
||||
func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
switch strings.ToUpper(ctx.T_STRING().GetText()) {
|
||||
|
||||
default:
|
||||
s.appendErrors(errors.New("unknown function"))
|
||||
|
||||
@@ -26,8 +25,8 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
if len(ctx.AllExpression()) == 3 {
|
||||
u = s.pop().(bool)
|
||||
}
|
||||
seen := map[interface{}]bool{}
|
||||
values, anyArray := s.pop().([]interface{}), s.pop().([]interface{})
|
||||
seen := map[any]bool{}
|
||||
values, anyArray := s.pop().([]any), s.pop().([]any)
|
||||
|
||||
if u {
|
||||
for _, e := range anyArray {
|
||||
@@ -45,18 +44,18 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
s.push(anyArray)
|
||||
case "COUNT_DISTINCT", "COUNT_UNIQUE":
|
||||
count := 0
|
||||
seen := map[interface{}]bool{}
|
||||
array := s.pop().([]interface{})
|
||||
seen := map[any]bool{}
|
||||
array := s.pop().([]any)
|
||||
for _, e := range array {
|
||||
_, ok := seen[e]
|
||||
if !ok {
|
||||
seen[e] = true
|
||||
count += 1
|
||||
count++
|
||||
}
|
||||
}
|
||||
s.push(float64(count))
|
||||
case "FIRST":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
if len(array) == 0 {
|
||||
s.push(nil)
|
||||
} else {
|
||||
@@ -65,16 +64,16 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
// case "FLATTEN":
|
||||
// case "INTERLEAVE":
|
||||
case "INTERSECTION":
|
||||
iset := New(s.pop().([]interface{})...)
|
||||
iset := NewSet(s.pop().([]any)...)
|
||||
|
||||
for i := 1; i < len(ctx.AllExpression()); i++ {
|
||||
iset = iset.Intersection(New(s.pop().([]interface{})...))
|
||||
iset = iset.Intersection(NewSet(s.pop().([]any)...))
|
||||
}
|
||||
|
||||
s.push(iset.Values())
|
||||
// case "JACCARD":
|
||||
case "LAST":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
if len(array) == 0 {
|
||||
s.push(nil)
|
||||
} else {
|
||||
@@ -94,9 +93,9 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
s.push(float64(len(fmt.Sprint(v))))
|
||||
case string:
|
||||
s.push(float64(utf8.RuneCountInString(v)))
|
||||
case []interface{}:
|
||||
case []any:
|
||||
s.push(float64(len(v)))
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
s.push(float64(len(v)))
|
||||
default:
|
||||
panic("unknown type")
|
||||
@@ -104,7 +103,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
case "MINUS":
|
||||
var sets []*Set
|
||||
for i := 0; i < len(ctx.AllExpression()); i++ {
|
||||
sets = append(sets, New(s.pop().([]interface{})...))
|
||||
sets = append(sets, NewSet(s.pop().([]any)...))
|
||||
}
|
||||
|
||||
iset := sets[len(sets)-1]
|
||||
@@ -116,7 +115,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
s.push(iset.Values())
|
||||
case "NTH":
|
||||
pos := s.pop().(float64)
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
if int(pos) >= len(array) || pos < 0 {
|
||||
s.push(nil)
|
||||
} else {
|
||||
@@ -124,16 +123,16 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
// case "OUTERSECTION":
|
||||
// array := s.pop().([]interface{})
|
||||
// union := New(array...)
|
||||
// intersection := New(s.pop().([]interface{})...)
|
||||
// union := NewSet(array...)
|
||||
// intersection := NewSet(s.pop().([]interface{})...)
|
||||
// for i := 1; i < len(ctx.AllExpression()); i++ {
|
||||
// array = s.pop().([]interface{})
|
||||
// union = union.Union(New(array...))
|
||||
// intersection = intersection.Intersection(New(array...))
|
||||
// union = union.Union(NewSet(array...))
|
||||
// intersection = intersection.Intersection(NewSet(array...))
|
||||
// }
|
||||
// s.push(union.Minus(intersection).Values())
|
||||
case "POP":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
s.push(array[:len(array)-1])
|
||||
case "POSITION", "CONTAINS_ARRAY":
|
||||
returnIndex := false
|
||||
@@ -141,7 +140,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
returnIndex = s.pop().(bool)
|
||||
}
|
||||
search := s.pop()
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
|
||||
for idx, e := range array {
|
||||
if e == search {
|
||||
@@ -164,7 +163,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
u = s.pop().(bool)
|
||||
}
|
||||
element := s.pop()
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
|
||||
if u && contains(array, element) {
|
||||
s.push(array)
|
||||
@@ -173,13 +172,13 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
case "REMOVE_NTH":
|
||||
position := s.pop().(float64)
|
||||
anyArray := s.pop().([]interface{})
|
||||
anyArray := s.pop().([]any)
|
||||
|
||||
if position < 0 {
|
||||
position = float64(len(anyArray) + int(position))
|
||||
}
|
||||
|
||||
result := []interface{}{}
|
||||
result := []any{}
|
||||
for idx, e := range anyArray {
|
||||
if idx != int(position) {
|
||||
result = append(result, e)
|
||||
@@ -193,7 +192,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
replaceValue := s.pop().(string)
|
||||
position := s.pop().(float64)
|
||||
anyArray := s.pop().([]interface{})
|
||||
anyArray := s.pop().([]any)
|
||||
|
||||
if position < 0 {
|
||||
position = float64(len(anyArray) + int(position))
|
||||
@@ -224,8 +223,8 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
limit = s.pop().(float64)
|
||||
}
|
||||
value := s.pop()
|
||||
array := s.pop().([]interface{})
|
||||
result := []interface{}{}
|
||||
array := s.pop().([]any)
|
||||
result := []any{}
|
||||
for idx, e := range array {
|
||||
if e != value || float64(idx) > limit {
|
||||
result = append(result, e)
|
||||
@@ -233,9 +232,9 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
s.push(result)
|
||||
case "REMOVE_VALUES":
|
||||
values := s.pop().([]interface{})
|
||||
array := s.pop().([]interface{})
|
||||
result := []interface{}{}
|
||||
values := s.pop().([]any)
|
||||
array := s.pop().([]any)
|
||||
result := []any{}
|
||||
for _, e := range array {
|
||||
if !contains(values, e) {
|
||||
result = append(result, e)
|
||||
@@ -243,14 +242,14 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
s.push(result)
|
||||
case "REVERSE":
|
||||
array := s.pop().([]interface{})
|
||||
var reverse []interface{}
|
||||
array := s.pop().([]any)
|
||||
var reverse []any
|
||||
for _, e := range array {
|
||||
reverse = append([]interface{}{e}, reverse...)
|
||||
reverse = append([]any{e}, reverse...)
|
||||
}
|
||||
s.push(reverse)
|
||||
case "SHIFT":
|
||||
s.push(s.pop().([]interface{})[1:])
|
||||
s.push(s.pop().([]any)[1:])
|
||||
case "SLICE":
|
||||
length := float64(-1)
|
||||
full := true
|
||||
@@ -259,7 +258,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
full = false
|
||||
}
|
||||
start := int64(s.pop().(float64))
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
|
||||
if start < 0 {
|
||||
start = int64(len(array)) + start
|
||||
@@ -276,43 +275,43 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
s.push(array[start:end])
|
||||
case "SORTED":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
sort.Slice(array, func(i, j int) bool { return lt(array[i], array[j]) })
|
||||
s.push(array)
|
||||
case "SORTED_UNIQUE":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
sort.Slice(array, func(i, j int) bool { return lt(array[i], array[j]) })
|
||||
s.push(unique(array))
|
||||
case "UNION":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
|
||||
for i := 1; i < len(ctx.AllExpression()); i++ {
|
||||
array = append(array, s.pop().([]interface{})...)
|
||||
array = append(array, s.pop().([]any)...)
|
||||
}
|
||||
|
||||
sort.Slice(array, func(i, j int) bool { return lt(array[i], array[j]) })
|
||||
s.push(array)
|
||||
case "UNION_DISTINCT":
|
||||
iset := New(s.pop().([]interface{})...)
|
||||
iset := NewSet(s.pop().([]any)...)
|
||||
|
||||
for i := 1; i < len(ctx.AllExpression()); i++ {
|
||||
iset = iset.Union(New(s.pop().([]interface{})...))
|
||||
iset = iset.Union(NewSet(s.pop().([]any)...))
|
||||
}
|
||||
|
||||
s.push(unique(iset.Values()))
|
||||
case "UNIQUE":
|
||||
s.push(unique(s.pop().([]interface{})))
|
||||
s.push(unique(s.pop().([]any)))
|
||||
case "UNSHIFT":
|
||||
u := false
|
||||
if len(ctx.AllExpression()) == 3 {
|
||||
u = s.pop().(bool)
|
||||
}
|
||||
element := s.pop()
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
if u && contains(array, element) {
|
||||
s.push(array)
|
||||
} else {
|
||||
s.push(append([]interface{}{element}, array...))
|
||||
s.push(append([]any{element}, array...))
|
||||
}
|
||||
|
||||
// Bit https://www.arangodb.com/docs/stable/aql/functions-bit.html
|
||||
@@ -367,8 +366,8 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
if len(ctx.AllExpression()) >= 2 {
|
||||
removeInternal = s.pop().(bool)
|
||||
}
|
||||
var keys []interface{}
|
||||
for k := range s.pop().(map[string]interface{}) {
|
||||
var keys []any
|
||||
for k := range s.pop().(map[string]any) {
|
||||
isInternalKey := strings.HasPrefix(k, "_")
|
||||
if !removeInternal || !isInternalKey {
|
||||
keys = append(keys, k)
|
||||
@@ -379,20 +378,20 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
// case "COUNT":
|
||||
case "HAS":
|
||||
right, left := s.pop(), s.pop()
|
||||
_, ok := left.(map[string]interface{})[right.(string)]
|
||||
_, ok := left.(map[string]any)[right.(string)]
|
||||
s.push(ok)
|
||||
// case "KEEP":
|
||||
// case "LENGTH":
|
||||
// case "MATCHES":
|
||||
case "MERGE":
|
||||
var docs []map[string]interface{}
|
||||
var docs []map[string]any
|
||||
if len(ctx.AllExpression()) == 1 {
|
||||
for _, doc := range s.pop().([]interface{}) {
|
||||
docs = append([]map[string]interface{}{doc.(map[string]interface{})}, docs...)
|
||||
for _, doc := range s.pop().([]any) {
|
||||
docs = append([]map[string]any{doc.(map[string]any)}, docs...)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < len(ctx.AllExpression()); i++ {
|
||||
docs = append(docs, s.pop().(map[string]interface{}))
|
||||
docs = append(docs, s.pop().(map[string]any))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,9 +403,9 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
}
|
||||
s.push(doc)
|
||||
case "MERGE_RECURSIVE":
|
||||
var doc map[string]interface{}
|
||||
var doc map[string]any
|
||||
for i := 0; i < len(ctx.AllExpression()); i++ {
|
||||
err := mergo.Merge(&doc, s.pop().(map[string]interface{}))
|
||||
err := mergo.Merge(&doc, s.pop().(map[string]any))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -421,8 +420,8 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
if len(ctx.AllExpression()) == 2 {
|
||||
removeInternal = s.pop().(bool)
|
||||
}
|
||||
var values []interface{}
|
||||
for k, v := range s.pop().(map[string]interface{}) {
|
||||
var values []any
|
||||
for k, v := range s.pop().(map[string]any) {
|
||||
isInternalKey := strings.HasPrefix(k, "_")
|
||||
if !removeInternal || !isInternalKey {
|
||||
values = append(values, v)
|
||||
@@ -458,10 +457,10 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
case "AVERAGE", "AVG":
|
||||
count := 0
|
||||
sum := float64(0)
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
for _, element := range array {
|
||||
if element != nil {
|
||||
count += 1
|
||||
count++
|
||||
sum += toNumber(element)
|
||||
}
|
||||
}
|
||||
@@ -506,7 +505,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
case "MAX":
|
||||
var set bool
|
||||
var max float64
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
for _, element := range array {
|
||||
if element != nil {
|
||||
if !set || toNumber(element) > max {
|
||||
@@ -521,7 +520,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
s.push(nil)
|
||||
}
|
||||
case "MEDIAN":
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
var numbers []float64
|
||||
for _, element := range array {
|
||||
if f, ok := element.(float64); ok {
|
||||
@@ -544,7 +543,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
case "MIN":
|
||||
var set bool
|
||||
var min float64
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
for _, element := range array {
|
||||
if element != nil {
|
||||
if !set || toNumber(element) < min {
|
||||
@@ -566,7 +565,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
s.push(math.Pow(left.(float64), right.(float64)))
|
||||
case "PRODUCT":
|
||||
product := float64(1)
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
for _, element := range array {
|
||||
if element != nil {
|
||||
product *= toNumber(element)
|
||||
@@ -578,7 +577,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
case "RAND":
|
||||
s.push(rand.Float64())
|
||||
case "RANGE":
|
||||
var array []interface{}
|
||||
var array []any
|
||||
var start, end, step float64
|
||||
if len(ctx.AllExpression()) == 2 {
|
||||
right, left := s.pop(), s.pop()
|
||||
@@ -612,7 +611,7 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
// case "STDDEV":
|
||||
case "SUM":
|
||||
sum := float64(0)
|
||||
array := s.pop().([]interface{})
|
||||
array := s.pop().([]any)
|
||||
for _, element := range array {
|
||||
sum += toNumber(element)
|
||||
}
|
||||
@@ -691,7 +690,6 @@ func (s *aqlInterpreter) function(ctx *parser.Function_callContext) {
|
||||
// case "IS_IPV4":
|
||||
// case "IS_KEY":
|
||||
// case "TYPENAME":
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,6 +703,7 @@ func unique(array []interface{}) []interface{} {
|
||||
filtered = append(filtered, e)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
@@ -714,15 +713,7 @@ func contains(values []interface{}, e interface{}) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func stringSliceContains(values []string, e string) bool {
|
||||
for _, v := range values {
|
||||
if e == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -747,4 +738,5 @@ var functionNames = []string{
|
||||
"REGEX_REPLACE", "REVERSE", "RIGHT", "RTRIM", "SHA1", "SHA512", "SOUNDEX", "SPLIT", "STARTS_WITH", "SUBSTITUTE",
|
||||
"SUBSTRING", "TOKENS", "TO_BASE64", "TO_HEX", "TRIM", "UPPER", "UUID", "TO_BOOL", "TO_NUMBER", "TO_STRING",
|
||||
"TO_ARRAY", "TO_LIST", "IS_NULL", "IS_BOOL", "IS_NUMBER", "IS_STRING", "IS_ARRAY", "IS_LIST", "IS_OBJECT",
|
||||
"IS_DOCUMENT", "IS_DATESTRING", "IS_IPV4", "IS_KEY", "TYPENAME"}
|
||||
"IS_DOCUMENT", "IS_DATESTRING", "IS_IPV4", "IS_KEY", "TYPENAME",
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package caql
|
||||
package caql_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/caql"
|
||||
)
|
||||
|
||||
func TestFunctions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
saql string
|
||||
wantRebuild string
|
||||
wantValue interface{}
|
||||
wantValue any
|
||||
wantParseErr bool
|
||||
wantRebuildErr bool
|
||||
wantEvalErr bool
|
||||
@@ -266,13 +270,13 @@ func TestFunctions(t *testing.T) {
|
||||
{name: "RADIANS", saql: `RADIANS(0)`, wantRebuild: `RADIANS(0)`, wantValue: 0},
|
||||
// {name: "RAND", saql: `RAND()`, wantRebuild: `RAND()`, wantValue: 0.3503170117504508},
|
||||
// {name: "RAND", saql: `RAND()`, wantRebuild: `RAND()`, wantValue: 0.6138226173882478},
|
||||
{name: "RANGE", saql: `RANGE(1, 4)`, wantRebuild: `RANGE(1, 4)`, wantValue: []interface{}{float64(1), float64(2), float64(3), float64(4)}},
|
||||
{name: "RANGE", saql: `RANGE(1, 4, 2)`, wantRebuild: `RANGE(1, 4, 2)`, wantValue: []interface{}{float64(1), float64(3)}},
|
||||
{name: "RANGE", saql: `RANGE(1, 4, 3)`, wantRebuild: `RANGE(1, 4, 3)`, wantValue: []interface{}{float64(1), float64(4)}},
|
||||
{name: "RANGE", saql: `RANGE(1.5, 2.5)`, wantRebuild: `RANGE(1.5, 2.5)`, wantValue: []interface{}{float64(1), float64(2)}},
|
||||
{name: "RANGE", saql: `RANGE(1.5, 2.5, 1)`, wantRebuild: `RANGE(1.5, 2.5, 1)`, wantValue: []interface{}{1.5, 2.5}},
|
||||
{name: "RANGE", saql: `RANGE(1.5, 2.5, 0.5)`, wantRebuild: `RANGE(1.5, 2.5, 0.5)`, wantValue: []interface{}{1.5, 2.0, 2.5}},
|
||||
{name: "RANGE", saql: `RANGE(-0.75, 1.1, 0.5)`, wantRebuild: `RANGE(-0.75, 1.1, 0.5)`, wantValue: []interface{}{-0.75, -0.25, 0.25, 0.75}},
|
||||
{name: "RANGE", saql: `RANGE(1, 4)`, wantRebuild: `RANGE(1, 4)`, wantValue: []any{float64(1), float64(2), float64(3), float64(4)}},
|
||||
{name: "RANGE", saql: `RANGE(1, 4, 2)`, wantRebuild: `RANGE(1, 4, 2)`, wantValue: []any{float64(1), float64(3)}},
|
||||
{name: "RANGE", saql: `RANGE(1, 4, 3)`, wantRebuild: `RANGE(1, 4, 3)`, wantValue: []any{float64(1), float64(4)}},
|
||||
{name: "RANGE", saql: `RANGE(1.5, 2.5)`, wantRebuild: `RANGE(1.5, 2.5)`, wantValue: []any{float64(1), float64(2)}},
|
||||
{name: "RANGE", saql: `RANGE(1.5, 2.5, 1)`, wantRebuild: `RANGE(1.5, 2.5, 1)`, wantValue: []any{1.5, 2.5}},
|
||||
{name: "RANGE", saql: `RANGE(1.5, 2.5, 0.5)`, wantRebuild: `RANGE(1.5, 2.5, 0.5)`, wantValue: []any{1.5, 2.0, 2.5}},
|
||||
{name: "RANGE", saql: `RANGE(-0.75, 1.1, 0.5)`, wantRebuild: `RANGE(-0.75, 1.1, 0.5)`, wantValue: []any{-0.75, -0.25, 0.25, 0.75}},
|
||||
{name: "ROUND", saql: `ROUND(2.49)`, wantRebuild: `ROUND(2.49)`, wantValue: 2},
|
||||
{name: "ROUND", saql: `ROUND(2.50)`, wantRebuild: `ROUND(2.50)`, wantValue: 3},
|
||||
{name: "ROUND", saql: `ROUND(-2.50)`, wantRebuild: `ROUND(-2.50)`, wantValue: -2},
|
||||
@@ -299,15 +303,20 @@ func TestFunctions(t *testing.T) {
|
||||
{name: "Function Error 3", saql: `ABS("abs")`, wantRebuild: `ABS("abs")`, wantEvalErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
parser := &Parser{}
|
||||
tt := tt
|
||||
|
||||
parser := &caql.Parser{}
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expr, err := parser.Parse(tt.saql)
|
||||
if (err != nil) != tt.wantParseErr {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
if expr != nil {
|
||||
t.Error(expr.String())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -318,6 +327,7 @@ func TestFunctions(t *testing.T) {
|
||||
if (err != nil) != tt.wantRebuildErr {
|
||||
t.Error(expr.String())
|
||||
t.Errorf("String() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -327,18 +337,19 @@ func TestFunctions(t *testing.T) {
|
||||
t.Errorf("String() got = %v, want %v", got, tt.wantRebuild)
|
||||
}
|
||||
|
||||
var myJson map[string]interface{}
|
||||
var myJSON map[string]any
|
||||
if tt.values != "" {
|
||||
err = json.Unmarshal([]byte(tt.values), &myJson)
|
||||
err = json.Unmarshal([]byte(tt.values), &myJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
value, err := expr.Eval(myJson)
|
||||
value, err := expr.Eval(myJSON)
|
||||
if (err != nil) != tt.wantEvalErr {
|
||||
t.Error(expr.String())
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -367,14 +378,15 @@ func TestFunctions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func jsonParse(s string) interface{} {
|
||||
func jsonParse(s string) any {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
var j interface{}
|
||||
var j any
|
||||
err := json.Unmarshal([]byte(s), &j)
|
||||
if err != nil {
|
||||
panic(s + err.Error())
|
||||
}
|
||||
|
||||
return j
|
||||
}
|
||||
|
||||
@@ -10,22 +10,23 @@ import (
|
||||
|
||||
type aqlInterpreter struct {
|
||||
*parser.BaseCAQLParserListener
|
||||
values map[string]interface{}
|
||||
stack []interface{}
|
||||
values map[string]any
|
||||
stack []any
|
||||
errs []error
|
||||
}
|
||||
|
||||
// push is a helper function for pushing new node to the listener Stack.
|
||||
func (s *aqlInterpreter) push(i interface{}) {
|
||||
func (s *aqlInterpreter) push(i any) {
|
||||
s.stack = append(s.stack, i)
|
||||
}
|
||||
|
||||
// pop is a helper function for poping a node from the listener Stack.
|
||||
func (s *aqlInterpreter) pop() (n interface{}) {
|
||||
func (s *aqlInterpreter) pop() (n any) {
|
||||
// Check that we have nodes in the stack.
|
||||
size := len(s.stack)
|
||||
if size < 1 {
|
||||
s.appendErrors(ErrStack)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -35,8 +36,9 @@ func (s *aqlInterpreter) pop() (n interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *aqlInterpreter) binaryPop() (interface{}, interface{}) {
|
||||
func (s *aqlInterpreter) binaryPop() (any, any) {
|
||||
right, left := s.pop(), s.pop()
|
||||
|
||||
return left, right
|
||||
}
|
||||
|
||||
@@ -54,17 +56,14 @@ func (s *aqlInterpreter) ExitExpression(ctx *parser.ExpressionContext) {
|
||||
s.push(plus(s.binaryPop()))
|
||||
case ctx.T_MINUS() != nil:
|
||||
s.push(minus(s.binaryPop()))
|
||||
|
||||
case ctx.T_TIMES() != nil:
|
||||
s.push(times(s.binaryPop()))
|
||||
case ctx.T_DIV() != nil:
|
||||
s.push(div(s.binaryPop()))
|
||||
case ctx.T_MOD() != nil:
|
||||
s.push(mod(s.binaryPop()))
|
||||
|
||||
case ctx.T_RANGE() != nil:
|
||||
s.push(aqlrange(s.binaryPop()))
|
||||
|
||||
case ctx.T_LT() != nil && ctx.GetEq_op() == nil:
|
||||
s.push(lt(s.binaryPop()))
|
||||
case ctx.T_GT() != nil && ctx.GetEq_op() == nil:
|
||||
@@ -73,35 +72,30 @@ func (s *aqlInterpreter) ExitExpression(ctx *parser.ExpressionContext) {
|
||||
s.push(le(s.binaryPop()))
|
||||
case ctx.T_GE() != nil && ctx.GetEq_op() == nil:
|
||||
s.push(ge(s.binaryPop()))
|
||||
|
||||
case ctx.T_IN() != nil && ctx.GetEq_op() == nil:
|
||||
s.push(maybeNot(ctx, in(s.binaryPop())))
|
||||
|
||||
case ctx.T_EQ() != nil && ctx.GetEq_op() == nil:
|
||||
s.push(eq(s.binaryPop()))
|
||||
case ctx.T_NE() != nil && ctx.GetEq_op() == nil:
|
||||
s.push(ne(s.binaryPop()))
|
||||
|
||||
case ctx.T_ALL() != nil && ctx.GetEq_op() != nil:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(all(left.([]interface{}), getOp(ctx.GetEq_op().GetTokenType()), right))
|
||||
s.push(all(left.([]any), getOp(ctx.GetEq_op().GetTokenType()), right))
|
||||
case ctx.T_ANY() != nil && ctx.GetEq_op() != nil:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(any(left.([]interface{}), getOp(ctx.GetEq_op().GetTokenType()), right))
|
||||
s.push(anyElement(left.([]any), getOp(ctx.GetEq_op().GetTokenType()), right))
|
||||
case ctx.T_NONE() != nil && ctx.GetEq_op() != nil:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(none(left.([]interface{}), getOp(ctx.GetEq_op().GetTokenType()), right))
|
||||
|
||||
s.push(none(left.([]any), getOp(ctx.GetEq_op().GetTokenType()), right))
|
||||
case ctx.T_ALL() != nil && ctx.T_NOT() != nil && ctx.T_IN() != nil:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(all(left.([]interface{}), in, right))
|
||||
s.push(all(left.([]any), in, right))
|
||||
case ctx.T_ANY() != nil && ctx.T_NOT() != nil && ctx.T_IN() != nil:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(any(left.([]interface{}), in, right))
|
||||
s.push(anyElement(left.([]any), in, right))
|
||||
case ctx.T_NONE() != nil && ctx.T_NOT() != nil && ctx.T_IN() != nil:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(none(left.([]interface{}), in, right))
|
||||
|
||||
s.push(none(left.([]any), in, right))
|
||||
case ctx.T_LIKE() != nil:
|
||||
m, err := like(s.binaryPop())
|
||||
s.appendErrors(err)
|
||||
@@ -114,21 +108,18 @@ func (s *aqlInterpreter) ExitExpression(ctx *parser.ExpressionContext) {
|
||||
m, err := regexNonMatch(s.binaryPop())
|
||||
s.appendErrors(err)
|
||||
s.push(maybeNot(ctx, m))
|
||||
|
||||
case ctx.T_AND() != nil:
|
||||
s.push(and(s.binaryPop()))
|
||||
case ctx.T_OR() != nil:
|
||||
s.push(or(s.binaryPop()))
|
||||
|
||||
case ctx.T_QUESTION() != nil && len(ctx.AllExpression()) == 3:
|
||||
right, middle, left := s.pop(), s.pop(), s.pop()
|
||||
s.push(ternary(left, middle, right))
|
||||
case ctx.T_QUESTION() != nil && len(ctx.AllExpression()) == 2:
|
||||
right, left := s.pop(), s.pop()
|
||||
s.push(ternary(left, nil, right))
|
||||
|
||||
default:
|
||||
panic("unkown expression")
|
||||
panic("unknown expression")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +150,7 @@ func (s *aqlInterpreter) ExitReference(ctx *parser.ReferenceContext) {
|
||||
case ctx.DOT() != nil:
|
||||
reference := s.pop()
|
||||
|
||||
s.push(reference.(map[string]interface{})[ctx.T_STRING().GetText()])
|
||||
s.push(reference.(map[string]any)[ctx.T_STRING().GetText()])
|
||||
case ctx.T_STRING() != nil:
|
||||
s.push(s.getVar(ctx.T_STRING().GetText()))
|
||||
case ctx.Compound_value() != nil:
|
||||
@@ -175,14 +166,15 @@ func (s *aqlInterpreter) ExitReference(ctx *parser.ReferenceContext) {
|
||||
if f, ok := key.(float64); ok {
|
||||
index := int(f)
|
||||
if index < 0 {
|
||||
index = len(reference.([]interface{})) + index
|
||||
index = len(reference.([]any)) + index
|
||||
}
|
||||
|
||||
s.push(reference.([]interface{})[index])
|
||||
s.push(reference.([]any)[index])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s.push(reference.(map[string]interface{})[key.(string)])
|
||||
s.push(reference.(map[string]any)[key.(string)])
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected value: %s", ctx.GetText()))
|
||||
}
|
||||
@@ -239,17 +231,17 @@ func (s *aqlInterpreter) ExitValue_literal(ctx *parser.Value_literalContext) {
|
||||
|
||||
// ExitArray is called when production array is exited.
|
||||
func (s *aqlInterpreter) ExitArray(ctx *parser.ArrayContext) {
|
||||
array := []interface{}{}
|
||||
array := []any{}
|
||||
for range ctx.AllExpression() {
|
||||
// prepend element
|
||||
array = append([]interface{}{s.pop()}, array...)
|
||||
array = append([]any{s.pop()}, array...)
|
||||
}
|
||||
s.push(array)
|
||||
}
|
||||
|
||||
// ExitObject is called when production object is exited.
|
||||
func (s *aqlInterpreter) ExitObject(ctx *parser.ObjectContext) {
|
||||
object := map[string]interface{}{}
|
||||
object := map[string]any{}
|
||||
for range ctx.AllObject_element() {
|
||||
key, value := s.pop(), s.pop()
|
||||
|
||||
@@ -290,7 +282,7 @@ func (s *aqlInterpreter) ExitObject_element_name(ctx *parser.Object_element_name
|
||||
}
|
||||
}
|
||||
|
||||
func (s *aqlInterpreter) getVar(identifier string) interface{} {
|
||||
func (s *aqlInterpreter) getVar(identifier string) any {
|
||||
v, ok := s.values[identifier]
|
||||
if !ok {
|
||||
s.appendErrors(ErrUndefined)
|
||||
@@ -303,10 +295,11 @@ func maybeNot(ctx *parser.ExpressionContext, m bool) bool {
|
||||
if ctx.T_NOT() != nil {
|
||||
return !m
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func getOp(tokenType int) func(left, right interface{}) bool {
|
||||
func getOp(tokenType int) func(left, right any) bool {
|
||||
switch tokenType {
|
||||
case parser.CAQLLexerT_EQ:
|
||||
return eq
|
||||
@@ -323,33 +316,36 @@ func getOp(tokenType int) func(left, right interface{}) bool {
|
||||
case parser.CAQLLexerT_IN:
|
||||
return in
|
||||
default:
|
||||
panic("unkown token type")
|
||||
panic("unknown token type")
|
||||
}
|
||||
}
|
||||
|
||||
func all(slice []interface{}, op func(interface{}, interface{}) bool, expr interface{}) bool {
|
||||
func all(slice []any, op func(any, any) bool, expr any) bool {
|
||||
for _, e := range slice {
|
||||
if !op(e, expr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func any(slice []interface{}, op func(interface{}, interface{}) bool, expr interface{}) bool {
|
||||
func anyElement(slice []any, op func(any, any) bool, expr any) bool {
|
||||
for _, e := range slice {
|
||||
if op(e, expr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func none(slice []interface{}, op func(interface{}, interface{}) bool, expr interface{}) bool {
|
||||
func none(slice []any, op func(any, any) bool, expr any) bool {
|
||||
for _, e := range slice {
|
||||
if op(e, expr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -10,21 +10,23 @@ import (
|
||||
|
||||
// Logical operators https://www.arangodb.com/docs/3.7/aql/operators.html#logical-operators
|
||||
|
||||
func or(left, right interface{}) interface{} {
|
||||
func or(left, right any) any {
|
||||
if toBool(left) {
|
||||
return left
|
||||
}
|
||||
|
||||
return right
|
||||
}
|
||||
|
||||
func and(left, right interface{}) interface{} {
|
||||
func and(left, right any) any {
|
||||
if !toBool(left) {
|
||||
return left
|
||||
}
|
||||
|
||||
return right
|
||||
}
|
||||
|
||||
func toBool(i interface{}) bool {
|
||||
func toBool(i any) bool {
|
||||
switch v := i.(type) {
|
||||
case nil:
|
||||
return false
|
||||
@@ -36,9 +38,9 @@ func toBool(i interface{}) bool {
|
||||
return v != 0
|
||||
case string:
|
||||
return v != ""
|
||||
case []interface{}:
|
||||
case []any:
|
||||
return true
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
return true
|
||||
default:
|
||||
panic("bool conversion failed")
|
||||
@@ -47,15 +49,15 @@ func toBool(i interface{}) bool {
|
||||
|
||||
// Arithmetic operators https://www.arangodb.com/docs/3.7/aql/operators.html#arithmetic-operators
|
||||
|
||||
func plus(left, right interface{}) float64 {
|
||||
func plus(left, right any) float64 {
|
||||
return toNumber(left) + toNumber(right)
|
||||
}
|
||||
|
||||
func minus(left, right interface{}) float64 {
|
||||
func minus(left, right any) float64 {
|
||||
return toNumber(left) - toNumber(right)
|
||||
}
|
||||
|
||||
func times(left, right interface{}) float64 {
|
||||
func times(left, right any) float64 {
|
||||
return round(toNumber(left) * toNumber(right))
|
||||
}
|
||||
|
||||
@@ -63,19 +65,20 @@ func round(r float64) float64 {
|
||||
return math.Round(r*100000) / 100000
|
||||
}
|
||||
|
||||
func div(left, right interface{}) float64 {
|
||||
func div(left, right any) float64 {
|
||||
b := toNumber(right)
|
||||
if b == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return round(toNumber(left) / b)
|
||||
}
|
||||
|
||||
func mod(left, right interface{}) float64 {
|
||||
func mod(left, right any) float64 {
|
||||
return math.Mod(toNumber(left), toNumber(right))
|
||||
}
|
||||
|
||||
func toNumber(i interface{}) float64 {
|
||||
func toNumber(i any) float64 {
|
||||
switch v := i.(type) {
|
||||
case nil:
|
||||
return 0
|
||||
@@ -83,6 +86,7 @@ func toNumber(i interface{}) float64 {
|
||||
if v {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
case float64:
|
||||
switch {
|
||||
@@ -91,22 +95,25 @@ func toNumber(i interface{}) float64 {
|
||||
case math.IsInf(v, 0):
|
||||
return 0
|
||||
}
|
||||
|
||||
return v
|
||||
case string:
|
||||
f, err := strconv.ParseFloat(strings.TrimSpace(v), 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return f
|
||||
case []interface{}:
|
||||
case []any:
|
||||
if len(v) == 0 {
|
||||
return 0
|
||||
}
|
||||
if len(v) == 1 {
|
||||
return toNumber(v[0])
|
||||
}
|
||||
|
||||
return 0
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
return 0
|
||||
default:
|
||||
panic("number conversion error")
|
||||
@@ -116,7 +123,7 @@ func toNumber(i interface{}) float64 {
|
||||
// Logical operators https://www.arangodb.com/docs/3.7/aql/operators.html#logical-operators
|
||||
// Order https://www.arangodb.com/docs/3.7/aql/fundamentals-type-value-order.html
|
||||
|
||||
func eq(left, right interface{}) bool {
|
||||
func eq(left, right any) bool {
|
||||
leftV, rightV := typeValue(left), typeValue(right)
|
||||
if leftV != rightV {
|
||||
return false
|
||||
@@ -126,15 +133,15 @@ func eq(left, right interface{}) bool {
|
||||
return true
|
||||
case bool, float64, string:
|
||||
return left == right
|
||||
case []interface{}:
|
||||
ra := right.([]interface{})
|
||||
case []any:
|
||||
ra := right.([]any)
|
||||
max := len(l)
|
||||
if len(ra) > max {
|
||||
max = len(ra)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if len(l) > i {
|
||||
li = l[i]
|
||||
}
|
||||
@@ -146,13 +153,14 @@ func eq(left, right interface{}) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
case map[string]interface{}:
|
||||
ro := right.(map[string]interface{})
|
||||
case map[string]any:
|
||||
ro := right.(map[string]any)
|
||||
|
||||
for _, key := range keys(l, ro) {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if lv, ok := l[key]; ok {
|
||||
li = lv
|
||||
}
|
||||
@@ -164,17 +172,18 @@ func eq(left, right interface{}) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func ne(left, right interface{}) bool {
|
||||
func ne(left, right any) bool {
|
||||
return !eq(left, right)
|
||||
}
|
||||
|
||||
func lt(left, right interface{}) bool {
|
||||
func lt(left, right any) bool {
|
||||
leftV, rightV := typeValue(left), typeValue(right)
|
||||
if leftV != rightV {
|
||||
return leftV < rightV
|
||||
@@ -190,15 +199,15 @@ func lt(left, right interface{}) bool {
|
||||
return l < right.(float64)
|
||||
case string:
|
||||
return l < right.(string)
|
||||
case []interface{}:
|
||||
ra := right.([]interface{})
|
||||
case []any:
|
||||
ra := right.([]any)
|
||||
max := len(l)
|
||||
if len(ra) > max {
|
||||
max = len(ra)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if len(l) > i {
|
||||
li = l[i]
|
||||
}
|
||||
@@ -210,13 +219,14 @@ func lt(left, right interface{}) bool {
|
||||
return lt(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
case map[string]interface{}:
|
||||
ro := right.(map[string]interface{})
|
||||
case map[string]any:
|
||||
ro := right.(map[string]any)
|
||||
|
||||
for _, key := range keys(l, ro) {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if lv, ok := l[key]; ok {
|
||||
li = lv
|
||||
}
|
||||
@@ -228,16 +238,17 @@ func lt(left, right interface{}) bool {
|
||||
return lt(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func keys(l map[string]interface{}, ro map[string]interface{}) []string {
|
||||
func keys(l map[string]any, ro map[string]any) []string {
|
||||
var keys []string
|
||||
seen := map[string]bool{}
|
||||
for _, a := range []map[string]interface{}{l, ro} {
|
||||
for _, a := range []map[string]any{l, ro} {
|
||||
for k := range a {
|
||||
if _, ok := seen[k]; !ok {
|
||||
seen[k] = true
|
||||
@@ -246,10 +257,11 @@ func keys(l map[string]interface{}, ro map[string]interface{}) []string {
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
func gt(left, right interface{}) bool {
|
||||
func gt(left, right any) bool {
|
||||
leftV, rightV := typeValue(left), typeValue(right)
|
||||
if leftV != rightV {
|
||||
return leftV > rightV
|
||||
@@ -265,15 +277,15 @@ func gt(left, right interface{}) bool {
|
||||
return l > right.(float64)
|
||||
case string:
|
||||
return l > right.(string)
|
||||
case []interface{}:
|
||||
ra := right.([]interface{})
|
||||
case []any:
|
||||
ra := right.([]any)
|
||||
max := len(l)
|
||||
if len(ra) > max {
|
||||
max = len(ra)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if len(l) > i {
|
||||
li = l[i]
|
||||
}
|
||||
@@ -285,13 +297,14 @@ func gt(left, right interface{}) bool {
|
||||
return gt(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
case map[string]interface{}:
|
||||
ro := right.(map[string]interface{})
|
||||
case map[string]any:
|
||||
ro := right.(map[string]any)
|
||||
|
||||
for _, key := range keys(l, ro) {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if lv, ok := l[key]; ok {
|
||||
li = lv
|
||||
}
|
||||
@@ -303,13 +316,14 @@ func gt(left, right interface{}) bool {
|
||||
return gt(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func le(left, right interface{}) bool {
|
||||
func le(left, right any) bool {
|
||||
leftV, rightV := typeValue(left), typeValue(right)
|
||||
if leftV != rightV {
|
||||
return leftV <= rightV
|
||||
@@ -325,15 +339,15 @@ func le(left, right interface{}) bool {
|
||||
return l <= right.(float64)
|
||||
case string:
|
||||
return l <= right.(string)
|
||||
case []interface{}:
|
||||
ra := right.([]interface{})
|
||||
case []any:
|
||||
ra := right.([]any)
|
||||
max := len(l)
|
||||
if len(ra) > max {
|
||||
max = len(ra)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if len(l) > i {
|
||||
li = l[i]
|
||||
}
|
||||
@@ -345,13 +359,14 @@ func le(left, right interface{}) bool {
|
||||
return le(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
case map[string]interface{}:
|
||||
ro := right.(map[string]interface{})
|
||||
case map[string]any:
|
||||
ro := right.(map[string]any)
|
||||
|
||||
for _, key := range keys(l, ro) {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if lv, ok := l[key]; ok {
|
||||
li = lv
|
||||
}
|
||||
@@ -363,13 +378,14 @@ func le(left, right interface{}) bool {
|
||||
return lt(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func ge(left, right interface{}) bool {
|
||||
func ge(left, right any) bool {
|
||||
leftV, rightV := typeValue(left), typeValue(right)
|
||||
if leftV != rightV {
|
||||
return leftV >= rightV
|
||||
@@ -385,15 +401,15 @@ func ge(left, right interface{}) bool {
|
||||
return l >= right.(float64)
|
||||
case string:
|
||||
return l >= right.(string)
|
||||
case []interface{}:
|
||||
ra := right.([]interface{})
|
||||
case []any:
|
||||
ra := right.([]any)
|
||||
max := len(l)
|
||||
if len(ra) > max {
|
||||
max = len(ra)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if len(l) > i {
|
||||
li = l[i]
|
||||
}
|
||||
@@ -405,13 +421,14 @@ func ge(left, right interface{}) bool {
|
||||
return ge(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
case map[string]interface{}:
|
||||
ro := right.(map[string]interface{})
|
||||
case map[string]any:
|
||||
ro := right.(map[string]any)
|
||||
|
||||
for _, key := range keys(l, ro) {
|
||||
var li interface{} = nil
|
||||
var rai interface{} = nil
|
||||
var li any
|
||||
var rai any
|
||||
if lv, ok := l[key]; ok {
|
||||
li = lv
|
||||
}
|
||||
@@ -423,14 +440,15 @@ func ge(left, right interface{}) bool {
|
||||
return gt(li, rai)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
default:
|
||||
panic("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func in(left, right interface{}) bool {
|
||||
a, ok := right.([]interface{})
|
||||
func in(left, right any) bool {
|
||||
a, ok := right.([]any)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@@ -439,23 +457,25 @@ func in(left, right interface{}) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func like(left, right interface{}) (bool, error) {
|
||||
func like(left, right any) (bool, error) {
|
||||
return match(right.(string), left.(string))
|
||||
}
|
||||
|
||||
func regexMatch(left, right interface{}) (bool, error) {
|
||||
func regexMatch(left, right any) (bool, error) {
|
||||
return regexp.Match(right.(string), []byte(left.(string)))
|
||||
}
|
||||
|
||||
func regexNonMatch(left, right interface{}) (bool, error) {
|
||||
func regexNonMatch(left, right any) (bool, error) {
|
||||
m, err := regexp.Match(right.(string), []byte(left.(string)))
|
||||
|
||||
return !m, err
|
||||
}
|
||||
|
||||
func typeValue(v interface{}) int {
|
||||
func typeValue(v any) int {
|
||||
switch v.(type) {
|
||||
case nil:
|
||||
return 0
|
||||
@@ -465,9 +485,9 @@ func typeValue(v interface{}) int {
|
||||
return 2
|
||||
case string:
|
||||
return 3
|
||||
case []interface{}:
|
||||
case []any:
|
||||
return 4
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
return 5
|
||||
default:
|
||||
panic("unknown type")
|
||||
@@ -476,22 +496,25 @@ func typeValue(v interface{}) int {
|
||||
|
||||
// Ternary operator https://www.arangodb.com/docs/3.7/aql/operators.html#ternary-operator
|
||||
|
||||
func ternary(left, middle, right interface{}) interface{} {
|
||||
func ternary(left, middle, right any) any {
|
||||
if toBool(left) {
|
||||
if middle != nil {
|
||||
return middle
|
||||
}
|
||||
|
||||
return left
|
||||
}
|
||||
|
||||
return right
|
||||
}
|
||||
|
||||
// Range operators https://www.arangodb.com/docs/3.7/aql/operators.html#range-operator
|
||||
|
||||
func aqlrange(left, right interface{}) []float64 {
|
||||
func aqlrange(left, right any) []float64 {
|
||||
var v []float64
|
||||
for i := int(left.(float64)); i <= int(right.(float64)); i++ {
|
||||
v = append(v, float64(i))
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func (p *Parser) Parse(aql string) (t *Tree, err error) {
|
||||
err = fmt.Errorf("%s", r)
|
||||
}
|
||||
}()
|
||||
// Setup the input
|
||||
// Set up the input
|
||||
inputStream := antlr.NewInputStream(aql)
|
||||
|
||||
errorListener := &errorListener{}
|
||||
@@ -52,7 +52,7 @@ type Tree struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (t *Tree) Eval(values map[string]interface{}) (i interface{}, err error) {
|
||||
func (t *Tree) Eval(values map[string]any) (i any, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("%s", r)
|
||||
@@ -65,6 +65,7 @@ func (t *Tree) Eval(values map[string]interface{}) (i interface{}, err error) {
|
||||
if interpreter.errs != nil {
|
||||
return nil, interpreter.errs[0]
|
||||
}
|
||||
|
||||
return interpreter.stack[0], nil
|
||||
}
|
||||
|
||||
@@ -103,7 +104,7 @@ type errorListener struct {
|
||||
errs []error
|
||||
}
|
||||
|
||||
func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
|
||||
func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) {
|
||||
el.errs = append(el.errs, fmt.Errorf("line "+strconv.Itoa(line)+":"+strconv.Itoa(column)+" "+msg))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package caql
|
||||
package caql_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/caql"
|
||||
)
|
||||
|
||||
type MockSearcher struct{}
|
||||
@@ -13,11 +15,13 @@ func (m MockSearcher) Search(_ string) (ids []string, err error) {
|
||||
}
|
||||
|
||||
func TestParseSAQLEval(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
saql string
|
||||
wantRebuild string
|
||||
wantValue interface{}
|
||||
wantValue any
|
||||
wantParseErr bool
|
||||
wantRebuildErr bool
|
||||
wantEvalErr bool
|
||||
@@ -89,15 +93,15 @@ func TestParseSAQLEval(t *testing.T) {
|
||||
// {name: "String 9", saql: `'this is a longer string.'`, wantRebuild: `"this is a longer string."`, wantValue: "this is a longer string."},
|
||||
// {name: "String 10", saql: `'the path separator on Windows is \\'`, wantRebuild: `"the path separator on Windows is \\"`, wantValue: `the path separator on Windows is \`},
|
||||
|
||||
{name: "Array 1", saql: "[]", wantRebuild: "[]", wantValue: []interface{}{}},
|
||||
{name: "Array 2", saql: `[true]`, wantRebuild: `[true]`, wantValue: []interface{}{true}},
|
||||
{name: "Array 3", saql: `[1, 2, 3]`, wantRebuild: `[1, 2, 3]`, wantValue: []interface{}{float64(1), float64(2), float64(3)}},
|
||||
{name: "Array 1", saql: "[]", wantRebuild: "[]", wantValue: []any{}},
|
||||
{name: "Array 2", saql: `[true]`, wantRebuild: `[true]`, wantValue: []any{true}},
|
||||
{name: "Array 3", saql: `[1, 2, 3]`, wantRebuild: `[1, 2, 3]`, wantValue: []any{float64(1), float64(2), float64(3)}},
|
||||
{
|
||||
name: "Array 4", saql: `[-99, "yikes!", [false, ["no"], []], 1]`, wantRebuild: `[-99, "yikes!", [false, ["no"], []], 1]`,
|
||||
wantValue: []interface{}{-99.0, "yikes!", []interface{}{false, []interface{}{"no"}, []interface{}{}}, float64(1)},
|
||||
wantValue: []any{-99.0, "yikes!", []any{false, []any{"no"}, []any{}}, float64(1)},
|
||||
},
|
||||
{name: "Array 5", saql: `[["fox", "marshal"]]`, wantRebuild: `[["fox", "marshal"]]`, wantValue: []interface{}{[]interface{}{"fox", "marshal"}}},
|
||||
{name: "Array 6", saql: `[1, 2, 3,]`, wantRebuild: `[1, 2, 3]`, wantValue: []interface{}{float64(1), float64(2), float64(3)}},
|
||||
{name: "Array 5", saql: `[["fox", "marshal"]]`, wantRebuild: `[["fox", "marshal"]]`, wantValue: []any{[]any{"fox", "marshal"}}},
|
||||
{name: "Array 6", saql: `[1, 2, 3,]`, wantRebuild: `[1, 2, 3]`, wantValue: []any{float64(1), float64(2), float64(3)}},
|
||||
|
||||
{name: "Array Error 1", saql: "(1,2,3)", wantParseErr: true},
|
||||
{name: "Array Access 1", saql: "u.friends[0]", wantRebuild: "u.friends[0]", wantValue: 7, values: `{"u": {"friends": [7,8,9]}}`},
|
||||
@@ -105,14 +109,14 @@ func TestParseSAQLEval(t *testing.T) {
|
||||
{name: "Array Access 3", saql: "u.friends[-1]", wantRebuild: "u.friends[-1]", wantValue: 9, values: `{"u": {"friends": [7,8,9]}}`},
|
||||
{name: "Array Access 4", saql: "u.friends[-2]", wantRebuild: "u.friends[-2]", wantValue: 8, values: `{"u": {"friends": [7,8,9]}}`},
|
||||
|
||||
{name: "Object 1", saql: "{}", wantRebuild: "{}", wantValue: map[string]interface{}{}},
|
||||
{name: "Object 2", saql: `{a: 1}`, wantRebuild: "{a: 1}", wantValue: map[string]interface{}{"a": float64(1)}},
|
||||
{name: "Object 3", saql: `{'a': 1}`, wantRebuild: `{'a': 1}`, wantValue: map[string]interface{}{"a": float64(1)}},
|
||||
{name: "Object 4", saql: `{"a": 1}`, wantRebuild: `{"a": 1}`, wantValue: map[string]interface{}{"a": float64(1)}},
|
||||
{name: "Object 5", saql: `{'return': 1}`, wantRebuild: `{'return': 1}`, wantValue: map[string]interface{}{"return": float64(1)}},
|
||||
{name: "Object 6", saql: `{"return": 1}`, wantRebuild: `{"return": 1}`, wantValue: map[string]interface{}{"return": float64(1)}},
|
||||
{name: "Object 9", saql: `{a: 1,}`, wantRebuild: "{a: 1}", wantValue: map[string]interface{}{"a": float64(1)}},
|
||||
{name: "Object 10", saql: `{"a": 1,}`, wantRebuild: `{"a": 1}`, wantValue: map[string]interface{}{"a": float64(1)}},
|
||||
{name: "Object 1", saql: "{}", wantRebuild: "{}", wantValue: map[string]any{}},
|
||||
{name: "Object 2", saql: `{a: 1}`, wantRebuild: "{a: 1}", wantValue: map[string]any{"a": float64(1)}},
|
||||
{name: "Object 3", saql: `{'a': 1}`, wantRebuild: `{'a': 1}`, wantValue: map[string]any{"a": float64(1)}},
|
||||
{name: "Object 4", saql: `{"a": 1}`, wantRebuild: `{"a": 1}`, wantValue: map[string]any{"a": float64(1)}},
|
||||
{name: "Object 5", saql: `{'return': 1}`, wantRebuild: `{'return': 1}`, wantValue: map[string]any{"return": float64(1)}},
|
||||
{name: "Object 6", saql: `{"return": 1}`, wantRebuild: `{"return": 1}`, wantValue: map[string]any{"return": float64(1)}},
|
||||
{name: "Object 9", saql: `{a: 1,}`, wantRebuild: "{a: 1}", wantValue: map[string]any{"a": float64(1)}},
|
||||
{name: "Object 10", saql: `{"a": 1,}`, wantRebuild: `{"a": 1}`, wantValue: map[string]any{"a": float64(1)}},
|
||||
// {"Object 8", "{`return`: 1}", `{"return": 1}`, true},
|
||||
// {"Object 7", "{´return´: 1}", `{"return": 1}`, true},
|
||||
{name: "Object Error 1: return is a keyword", saql: `{like: 1}`, wantParseErr: true},
|
||||
@@ -272,7 +276,7 @@ func TestParseSAQLEval(t *testing.T) {
|
||||
{name: "Arithmetic 17", saql: `23 * {}`, wantRebuild: `23 * {}`, wantValue: 0},
|
||||
{name: "Arithmetic 18", saql: `5 * [7]`, wantRebuild: `5 * [7]`, wantValue: 35},
|
||||
{name: "Arithmetic 19", saql: `24 / "12"`, wantRebuild: `24 / "12"`, wantValue: 2},
|
||||
{name: "Arithmetic Error 1: Divison by zero", saql: `1 / 0`, wantRebuild: `1 / 0`, wantValue: 0},
|
||||
{name: "Arithmetic Error 1: Division by zero", saql: `1 / 0`, wantRebuild: `1 / 0`, wantValue: 0},
|
||||
|
||||
// https://www.arangodb.com/docs/3.7/aql/operators.html#ternary-operator
|
||||
{name: "Ternary 1", saql: `u.age > 15 || u.active == true ? u.userId : null`, wantRebuild: `u.age > 15 OR u.active == true ? u.userId : null`, wantValue: 45, values: `{"u": {"active": true, "age": 2, "userId": 45}}`},
|
||||
@@ -287,20 +291,24 @@ func TestParseSAQLEval(t *testing.T) {
|
||||
{name: "Security 2", saql: `doc.value == 1 || true INSERT {foo: "bar"} IN collection //`, wantParseErr: true},
|
||||
|
||||
// https://www.arangodb.com/docs/3.7/aql/operators.html#operator-precedence
|
||||
{name: "Precendence", saql: `2 > 15 && "a" != ""`, wantRebuild: `2 > 15 AND "a" != ""`, wantValue: false},
|
||||
{name: "Precedence", saql: `2 > 15 && "a" != ""`, wantRebuild: `2 > 15 AND "a" != ""`, wantValue: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
parser := &Parser{
|
||||
tt := tt
|
||||
parser := &caql.Parser{
|
||||
Searcher: &MockSearcher{},
|
||||
}
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expr, err := parser.Parse(tt.saql)
|
||||
if (err != nil) != tt.wantParseErr {
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
if expr != nil {
|
||||
t.Error(expr.String())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -311,6 +319,7 @@ func TestParseSAQLEval(t *testing.T) {
|
||||
if (err != nil) != tt.wantRebuildErr {
|
||||
t.Error(expr.String())
|
||||
t.Errorf("String() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -320,18 +329,19 @@ func TestParseSAQLEval(t *testing.T) {
|
||||
t.Errorf("String() got = %v, want %v", got, tt.wantRebuild)
|
||||
}
|
||||
|
||||
var myJson map[string]interface{}
|
||||
var myJSON map[string]any
|
||||
if tt.values != "" {
|
||||
err = json.Unmarshal([]byte(tt.values), &myJson)
|
||||
err = json.Unmarshal([]byte(tt.values), &myJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
value, err := expr.Eval(myJson)
|
||||
value, err := expr.Eval(myJSON)
|
||||
if (err != nil) != tt.wantEvalErr {
|
||||
t.Error(expr.String())
|
||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantParseErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
57
caql/set.go
57
caql/set.go
@@ -22,19 +22,18 @@
|
||||
|
||||
package caql
|
||||
|
||||
import "sort"
|
||||
|
||||
type (
|
||||
Set struct {
|
||||
hash map[interface{}]nothing
|
||||
}
|
||||
|
||||
nothing struct{}
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Create a new set
|
||||
func New(initial ...interface{}) *Set {
|
||||
s := &Set{make(map[interface{}]nothing)}
|
||||
type Set struct {
|
||||
hash map[any]nothing
|
||||
}
|
||||
|
||||
type nothing struct{}
|
||||
|
||||
func NewSet(initial ...any) *Set {
|
||||
s := &Set{make(map[any]nothing)}
|
||||
|
||||
for _, v := range initial {
|
||||
s.Insert(v)
|
||||
@@ -43,9 +42,8 @@ func New(initial ...interface{}) *Set {
|
||||
return s
|
||||
}
|
||||
|
||||
// Find the difference between two sets
|
||||
func (s *Set) Difference(set *Set) *Set {
|
||||
n := make(map[interface{}]nothing)
|
||||
n := make(map[any]nothing)
|
||||
|
||||
for k := range s.hash {
|
||||
if _, exists := set.hash[k]; !exists {
|
||||
@@ -56,27 +54,18 @@ func (s *Set) Difference(set *Set) *Set {
|
||||
return &Set{n}
|
||||
}
|
||||
|
||||
// Call f for each item in the set
|
||||
func (s *Set) Do(f func(interface{})) {
|
||||
for k := range s.hash {
|
||||
f(k)
|
||||
}
|
||||
}
|
||||
|
||||
// Test to see whether or not the element is in the set
|
||||
func (s *Set) Has(element interface{}) bool {
|
||||
func (s *Set) Has(element any) bool {
|
||||
_, exists := s.hash[element]
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// Add an element to the set
|
||||
func (s *Set) Insert(element interface{}) {
|
||||
func (s *Set) Insert(element any) {
|
||||
s.hash[element] = nothing{}
|
||||
}
|
||||
|
||||
// Find the intersection of two sets
|
||||
func (s *Set) Intersection(set *Set) *Set {
|
||||
n := make(map[interface{}]nothing)
|
||||
n := make(map[any]nothing)
|
||||
|
||||
for k := range s.hash {
|
||||
if _, exists := set.hash[k]; exists {
|
||||
@@ -87,23 +76,20 @@ func (s *Set) Intersection(set *Set) *Set {
|
||||
return &Set{n}
|
||||
}
|
||||
|
||||
// Return the number of items in the set
|
||||
func (s *Set) Len() int {
|
||||
return len(s.hash)
|
||||
}
|
||||
|
||||
// Test whether or not this set is a proper subset of "set"
|
||||
func (s *Set) ProperSubsetOf(set *Set) bool {
|
||||
return s.SubsetOf(set) && s.Len() < set.Len()
|
||||
}
|
||||
|
||||
// Remove an element from the set
|
||||
func (s *Set) Remove(element interface{}) {
|
||||
func (s *Set) Remove(element any) {
|
||||
delete(s.hash, element)
|
||||
}
|
||||
|
||||
func (s *Set) Minus(set *Set) *Set {
|
||||
n := make(map[interface{}]nothing)
|
||||
n := make(map[any]nothing)
|
||||
for k := range s.hash {
|
||||
n[k] = nothing{}
|
||||
}
|
||||
@@ -115,7 +101,6 @@ func (s *Set) Minus(set *Set) *Set {
|
||||
return &Set{n}
|
||||
}
|
||||
|
||||
// Test whether or not this set is a subset of "set"
|
||||
func (s *Set) SubsetOf(set *Set) bool {
|
||||
if s.Len() > set.Len() {
|
||||
return false
|
||||
@@ -125,12 +110,12 @@ func (s *Set) SubsetOf(set *Set) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Find the union of two sets
|
||||
func (s *Set) Union(set *Set) *Set {
|
||||
n := make(map[interface{}]nothing)
|
||||
n := make(map[any]nothing)
|
||||
|
||||
for k := range s.hash {
|
||||
n[k] = nothing{}
|
||||
@@ -142,8 +127,8 @@ func (s *Set) Union(set *Set) *Set {
|
||||
return &Set{n}
|
||||
}
|
||||
|
||||
func (s *Set) Values() []interface{} {
|
||||
values := []interface{}{}
|
||||
func (s *Set) Values() []any {
|
||||
values := []any{}
|
||||
|
||||
for k := range s.hash {
|
||||
values = append(values, k)
|
||||
|
||||
@@ -27,7 +27,9 @@ import (
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
s := New()
|
||||
t.Parallel()
|
||||
|
||||
s := NewSet()
|
||||
|
||||
s.Insert(5)
|
||||
|
||||
@@ -50,8 +52,8 @@ func Test(t *testing.T) {
|
||||
}
|
||||
|
||||
// Difference
|
||||
s1 := New(1, 2, 3, 4, 5, 6)
|
||||
s2 := New(4, 5, 6)
|
||||
s1 := NewSet(1, 2, 3, 4, 5, 6)
|
||||
s2 := NewSet(4, 5, 6)
|
||||
s3 := s1.Difference(s2)
|
||||
|
||||
if s3.Len() != 3 {
|
||||
@@ -73,7 +75,7 @@ func Test(t *testing.T) {
|
||||
}
|
||||
|
||||
// Union
|
||||
s4 := New(7, 8, 9)
|
||||
s4 := NewSet(7, 8, 9)
|
||||
s3 = s2.Union(s4)
|
||||
|
||||
if s3.Len() != 6 {
|
||||
@@ -92,5 +94,4 @@ func Test(t *testing.T) {
|
||||
if s1.ProperSubsetOf(s1) {
|
||||
t.Errorf("set should not be a subset of itself")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@ func unquote(s string) (string, error) {
|
||||
buf = append(buf, s[i])
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
if quote != '"' && quote != '\'' {
|
||||
@@ -75,5 +77,6 @@ func unquote(s string) (string, error) {
|
||||
buf = append(buf, runeTmp[:n]...)
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
@@ -8,26 +8,25 @@
|
||||
package caql
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type quoteTest struct {
|
||||
in string
|
||||
out string
|
||||
ascii string
|
||||
graphic string
|
||||
in string
|
||||
out string
|
||||
}
|
||||
|
||||
var quotetests = []quoteTest{
|
||||
{in: "\a\b\f\r\n\t\v", out: `"\a\b\f\r\n\t\v"`, ascii: `"\a\b\f\r\n\t\v"`, graphic: `"\a\b\f\r\n\t\v"`},
|
||||
{"\\", `"\\"`, `"\\"`, `"\\"`},
|
||||
{"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`, `"abc\xffdef"`},
|
||||
{"\u263a", `"☺"`, `"\u263a"`, `"☺"`},
|
||||
{"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`, `"\U0010ffff"`},
|
||||
{"\x04", `"\x04"`, `"\x04"`, `"\x04"`},
|
||||
{in: "\a\b\f\r\n\t\v", out: `"\a\b\f\r\n\t\v"`},
|
||||
{"\\", `"\\"`},
|
||||
{"abc\xffdef", `"abc\xffdef"`},
|
||||
{"\u263a", `"☺"`},
|
||||
{"\U0010ffff", `"\U0010ffff"`},
|
||||
{"\x04", `"\x04"`},
|
||||
// Some non-printable but graphic runes. Final column is double-quoted.
|
||||
{"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`, `"!\u00a0!\u2000!\u3000!"`, "\"!\u00a0!\u2000!\u3000!\""},
|
||||
{"!\u00a0!\u2000!\u3000!", `"!\u00a0!\u2000!\u3000!"`},
|
||||
}
|
||||
|
||||
type unQuoteTest struct {
|
||||
@@ -104,6 +103,8 @@ var misquoted = []string{
|
||||
}
|
||||
|
||||
func TestUnquote(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tt := range unquotetests {
|
||||
if out, err := unquote(tt.in); err != nil || out != tt.out {
|
||||
t.Errorf("unquote(%#q) = %q, %v want %q, nil", tt.in, out, err, tt.out)
|
||||
@@ -118,7 +119,7 @@ func TestUnquote(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, s := range misquoted {
|
||||
if out, err := unquote(s); out != "" || err != strconv.ErrSyntax {
|
||||
if out, err := unquote(s); out != "" || !errors.Is(err, strconv.ErrSyntax) {
|
||||
t.Errorf("unquote(%#q) = %q, %v want %q, %v", s, out, err, "", strconv.ErrSyntax)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ var ErrBadPattern = errors.New("syntax error in pattern")
|
||||
// match requires pattern to match all of name, not just a substring.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
// is malformed.
|
||||
//
|
||||
func match(pattern, name string) (matched bool, err error) {
|
||||
Pattern:
|
||||
for len(pattern) > 0 {
|
||||
@@ -48,6 +47,7 @@ Pattern:
|
||||
// using the star
|
||||
if ok && (len(t) == 0 || len(pattern) > 0) {
|
||||
name = t
|
||||
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
@@ -64,6 +64,7 @@ Pattern:
|
||||
continue
|
||||
}
|
||||
name = t
|
||||
|
||||
continue Pattern
|
||||
}
|
||||
if err != nil {
|
||||
@@ -79,8 +80,10 @@ Pattern:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return len(name) == 0, nil
|
||||
}
|
||||
|
||||
@@ -104,6 +107,7 @@ Scan:
|
||||
break Scan
|
||||
}
|
||||
}
|
||||
|
||||
return star, pattern[0:i], pattern[i:]
|
||||
}
|
||||
|
||||
@@ -120,7 +124,6 @@ func matchChunk(chunk, s string) (rest string, ok bool, err error) {
|
||||
failed = true
|
||||
}
|
||||
switch chunk[0] {
|
||||
|
||||
case '_':
|
||||
if !failed {
|
||||
if s[0] == '/' {
|
||||
@@ -130,14 +133,13 @@ func matchChunk(chunk, s string) (rest string, ok bool, err error) {
|
||||
s = s[n:]
|
||||
}
|
||||
chunk = chunk[1:]
|
||||
|
||||
case '\\':
|
||||
chunk = chunk[1:]
|
||||
if len(chunk) == 0 {
|
||||
return "", false, ErrBadPattern
|
||||
}
|
||||
fallthrough
|
||||
|
||||
fallthrough
|
||||
default:
|
||||
if !failed {
|
||||
if chunk[0] != s[0] {
|
||||
@@ -151,5 +153,6 @@ func matchChunk(chunk, s string) (rest string, ok bool, err error) {
|
||||
if failed {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return s, true, nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
|
||||
package caql
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type MatchTest struct {
|
||||
pattern, s string
|
||||
@@ -41,9 +44,11 @@ var matchTests = []MatchTest{
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tt := range matchTests {
|
||||
ok, err := match(tt.pattern, tt.s)
|
||||
if ok != tt.match || err != tt.err {
|
||||
if ok != tt.match || !errors.Is(err, tt.err) {
|
||||
t.Errorf("match(%#q, %#q) = %v, %v want %v, %v", tt.pattern, tt.s, ok, err, tt.match, tt.err)
|
||||
}
|
||||
}
|
||||
|
||||
6
cmd/catalyst-dev/images.go
Normal file
6
cmd/catalyst-dev/images.go
Normal file
File diff suppressed because one or more lines are too long
@@ -2,18 +2,20 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst"
|
||||
"github.com/SecurityBrewery/catalyst/cmd"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/api"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/generated/pointer"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
"github.com/SecurityBrewery/catalyst/test"
|
||||
)
|
||||
|
||||
@@ -33,20 +35,45 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
demoUser := &model.UserResponse{ID: "demo", Roles: []string{role.Admin}}
|
||||
ctx := busdb.UserContext(context.Background(), demoUser)
|
||||
demoUser := &maut.User{ID: "demo", Roles: []string{maut.AdminRole}}
|
||||
ctx := maut.UserContext(context.Background(), demoUser, catalyst.Admin.Permissions)
|
||||
if err := test.SetupTestData(ctx, theCatalyst.DB); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// proxy static requests
|
||||
middlewares := []func(next http.Handler) http.Handler{
|
||||
catalyst.Authenticate(theCatalyst.DB, config.Auth),
|
||||
catalyst.AuthorizeBlockedUser(),
|
||||
}
|
||||
theCatalyst.Server.With(middlewares...).NotFound(api.Proxy("http://localhost:8080"))
|
||||
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "eve", Roles: []string{"admin"}, Password: pointer.String("eve")})
|
||||
_ = theCatalyst.DB.UserDataCreate(context.Background(), "eve", &model.UserData{
|
||||
Name: pointer.String("Eve"),
|
||||
Email: pointer.String("eve@example.com"),
|
||||
Image: &avatarEve,
|
||||
})
|
||||
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "kevin", Roles: []string{"admin"}, Password: pointer.String("kevin")})
|
||||
_ = theCatalyst.DB.UserDataCreate(context.Background(), "kevin", &model.UserData{
|
||||
Name: pointer.String("Kevin"),
|
||||
Email: pointer.String("kevin@example.com"),
|
||||
Image: &avatarKevin,
|
||||
})
|
||||
|
||||
if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil {
|
||||
_, _ = theCatalyst.DB.UserCreate(context.Background(), &model.UserForm{ID: "tom", Roles: []string{"admin"}, Password: pointer.String("tom")})
|
||||
_ = theCatalyst.DB.UserDataCreate(context.Background(), "tom", &model.UserData{
|
||||
Name: pointer.String("tom"),
|
||||
Email: pointer.String("tom@example.com"),
|
||||
Image: &avatarKevin,
|
||||
})
|
||||
|
||||
// proxy static requests
|
||||
theCatalyst.Server.Get("/ui/*", func(writer http.ResponseWriter, request *http.Request) {
|
||||
log.Println("proxy request", request.URL.Path)
|
||||
|
||||
api.Proxy("http://localhost:8080/")(writer, request)
|
||||
})
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", config.Port),
|
||||
ReadHeaderTimeout: 3 * time.Second,
|
||||
Handler: theCatalyst.Server,
|
||||
}
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst"
|
||||
"github.com/SecurityBrewery/catalyst/cmd"
|
||||
"github.com/SecurityBrewery/catalyst/generated/api"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/ui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -22,7 +27,16 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil {
|
||||
fsys, _ := fs.Sub(ui.UI, "dist")
|
||||
staticHandlerFunc := http.HandlerFunc(api.VueStatic(fsys))
|
||||
theCatalyst.Server.Get("/ui/*", http.StripPrefix("/ui", staticHandlerFunc).ServeHTTP)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", config.Port),
|
||||
ReadHeaderTimeout: 3 * time.Second,
|
||||
Handler: theCatalyst.Server,
|
||||
}
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
109
cmd/cmd.go
109
cmd/cmd.go
@@ -1,15 +1,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
kongyaml "github.com/alecthomas/kong-yaml"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst"
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
"github.com/SecurityBrewery/catalyst/storage"
|
||||
)
|
||||
|
||||
@@ -18,17 +20,25 @@ type CLI struct {
|
||||
ExternalAddress string `env:"EXTERNAL_ADDRESS" required:""`
|
||||
CatalystAddress string `env:"CATALYST_ADDRESS" default:"http://catalyst:8000"`
|
||||
Network string `env:"CATALYST_NETWORK" default:"catalyst"`
|
||||
Port int `env:"PORT" default:"8000"`
|
||||
|
||||
OIDCIssuer string `env:"OIDC_ISSUER" required:""`
|
||||
AuthBlockNew bool `env:"AUTH_BLOCK_NEW" default:"true" help:"Block newly created users"`
|
||||
AuthDefaultRoles []string `env:"AUTH_DEFAULT_ROLES" help:"Default roles for new users"`
|
||||
AuthAdminUsers []string `env:"AUTH_ADMIN_USERS" help:"Username of admins"`
|
||||
InitialAPIKey string `env:"INITIAL_API_KEY"`
|
||||
|
||||
// SimpleAuthEnable bool `env:"SIMPLE_AUTH_ENABLE" default:"true"`
|
||||
APIKeyAuthEnable bool `env:"API_KEY_AUTH_ENABLE" default:"true"`
|
||||
|
||||
OIDCEnable bool `env:"OIDC_ENABLE" default:"true"`
|
||||
OIDCIssuer string `env:"OIDC_ISSUER"`
|
||||
AuthURL string `env:"OIDC_AUTH_URL"`
|
||||
OIDCClientID string `env:"OIDC_CLIENT_ID" default:"catalyst"`
|
||||
OIDCClientSecret string `env:"OIDC_CLIENT_SECRET" required:""`
|
||||
OIDCClientSecret string `env:"OIDC_CLIENT_SECRET"`
|
||||
OIDCScopes []string `env:"OIDC_SCOPES" help:"Additional scopes, ['oidc', 'profile', 'email'] are always added." placeholder:"customscopes"`
|
||||
OIDCClaimUsername string `env:"OIDC_CLAIM_USERNAME" default:"preferred_username" help:"username field in the OIDC claim"`
|
||||
OIDCClaimEmail string `env:"OIDC_CLAIM_EMAIL" default:"email" help:"email field in the OIDC claim"`
|
||||
OIDCClaimName string `env:"OIDC_CLAIM_NAME" default:"name" help:"name field in the OIDC claim"`
|
||||
AuthBlockNew bool `env:"AUTH_BLOCK_NEW" default:"true" help:"Block newly created users"`
|
||||
AuthDefaultRoles []string `env:"AUTH_DEFAULT_ROLES" help:"Default roles for new users"`
|
||||
AuthAdminUsers []string `env:"AUTH_ADMIN_USERS" help:"Username of admins"`
|
||||
|
||||
IndexPath string `env:"INDEX_PATH" default:"index.bleve" help:"Path for the bleve index"`
|
||||
|
||||
@@ -39,11 +49,6 @@ type CLI struct {
|
||||
S3Host string `env:"S3_HOST" default:"http://minio:9000" name:"s3-host"`
|
||||
S3User string `env:"S3_USER" default:"minio" name:"s3-user"`
|
||||
S3Password string `env:"S3_PASSWORD" required:"" name:"s3-password"`
|
||||
|
||||
EmitterIOHost string `env:"EMITTER_IO_HOST" default:"tcp://emitter:8080"`
|
||||
EmitterIORKey string `env:"EMITTER_IO_KEY" required:""`
|
||||
|
||||
InitialAPIKey string `env:"INITIAL_API_KEY"`
|
||||
}
|
||||
|
||||
func ParseCatalystConfig() (*catalyst.Config, error) {
|
||||
@@ -54,46 +59,58 @@ func ParseCatalystConfig() (*catalyst.Config, error) {
|
||||
kong.Configuration(kongyaml.Loader, "/etc/catalyst.yaml", ".catalyst.yaml"),
|
||||
)
|
||||
|
||||
if cli.OIDCEnable {
|
||||
if cli.OIDCIssuer == "" {
|
||||
return nil, errors.New("OIDC issuer not set")
|
||||
}
|
||||
if cli.OIDCClientSecret == "" {
|
||||
return nil, errors.New("OIDC client secret is required")
|
||||
}
|
||||
}
|
||||
|
||||
return MapConfig(cli)
|
||||
}
|
||||
|
||||
func MapConfig(cli CLI) (*catalyst.Config, error) {
|
||||
roles := role.Explode(role.Analyst)
|
||||
roles = append(roles, role.Explodes(cli.AuthDefaultRoles)...)
|
||||
roles = role.Explodes(role.Strings(roles))
|
||||
|
||||
scopes := unique(append([]string{oidc.ScopeOpenID, "profile", "email"}, cli.OIDCScopes...))
|
||||
scopes := slices.Compact(append([]string{oidc.ScopeOpenID, "profile", "email"}, cli.OIDCScopes...))
|
||||
config := &catalyst.Config{
|
||||
IndexPath: cli.IndexPath,
|
||||
Network: cli.Network,
|
||||
DB: &database.Config{Host: cli.ArangoDBHost, User: cli.ArangoDBUser, Password: cli.ArangoDBPassword},
|
||||
Storage: &storage.Config{Host: cli.S3Host, User: cli.S3User, Password: cli.S3Password},
|
||||
Secret: []byte(cli.Secret),
|
||||
ExternalAddress: cli.ExternalAddress,
|
||||
Auth: &catalyst.AuthConfig{
|
||||
OIDCIssuer: cli.OIDCIssuer,
|
||||
OAuth2: &oauth2.Config{ClientID: cli.OIDCClientID, ClientSecret: cli.OIDCClientSecret, RedirectURL: cli.ExternalAddress + "/callback", Scopes: scopes},
|
||||
OIDCClaimUsername: cli.OIDCClaimUsername,
|
||||
OIDCClaimEmail: cli.OIDCClaimEmail,
|
||||
OIDCClaimName: cli.OIDCClaimName,
|
||||
AuthBlockNew: cli.AuthBlockNew,
|
||||
AuthDefaultRoles: roles,
|
||||
AuthAdminUsers: cli.AuthAdminUsers,
|
||||
IndexPath: cli.IndexPath,
|
||||
Network: cli.Network,
|
||||
DB: &database.Config{
|
||||
Host: cli.ArangoDBHost,
|
||||
User: cli.ArangoDBUser,
|
||||
Password: cli.ArangoDBPassword,
|
||||
},
|
||||
Storage: &storage.Config{Host: cli.S3Host, User: cli.S3User, Password: cli.S3Password},
|
||||
ExternalAddress: cli.ExternalAddress,
|
||||
InternalAddress: cli.CatalystAddress,
|
||||
Port: cli.Port,
|
||||
Auth: &maut.Config{
|
||||
CookieSecret: []byte(cli.Secret),
|
||||
SimpleAuthEnable: false, // cli.SimpleAuthEnable,
|
||||
APIKeyAuthEnable: cli.APIKeyAuthEnable,
|
||||
OIDCAuthEnable: cli.OIDCEnable,
|
||||
// InitialUser: "",
|
||||
// InitialPassword: "",
|
||||
InitialAPIKey: cli.InitialAPIKey,
|
||||
OIDCIssuer: cli.OIDCIssuer,
|
||||
AuthURL: cli.AuthURL,
|
||||
OAuth2: &oauth2.Config{
|
||||
ClientID: cli.OIDCClientID,
|
||||
ClientSecret: cli.OIDCClientSecret,
|
||||
RedirectURL: cli.ExternalAddress + "/auth/callback",
|
||||
Scopes: scopes,
|
||||
},
|
||||
UserCreateConfig: &maut.UserCreateConfig{
|
||||
AuthBlockNew: cli.AuthBlockNew,
|
||||
AuthDefaultRoles: cli.AuthDefaultRoles,
|
||||
AuthAdminUsers: cli.AuthAdminUsers,
|
||||
OIDCClaimUsername: cli.OIDCClaimUsername,
|
||||
OIDCClaimEmail: cli.OIDCClaimEmail,
|
||||
OIDCClaimName: cli.OIDCClaimName,
|
||||
},
|
||||
},
|
||||
Bus: &bus.Config{Host: cli.EmitterIOHost, Key: cli.EmitterIORKey, APIUrl: cli.CatalystAddress + "/api"},
|
||||
InitialAPIKey: cli.InitialAPIKey,
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func unique(l []string) []string {
|
||||
keys := make(map[string]bool)
|
||||
var list []string
|
||||
for _, entry := range l {
|
||||
if _, value := keys[entry]; !value {
|
||||
keys[entry] = true
|
||||
list = append(list, entry)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
49
cookie.go
49
cookie.go
@@ -1,49 +0,0 @@
|
||||
package catalyst
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
stateSessionCookie = "state"
|
||||
userSessionCookie = "user"
|
||||
)
|
||||
|
||||
func setStateCookie(w http.ResponseWriter, state string) {
|
||||
http.SetCookie(w, &http.Cookie{Name: stateSessionCookie, Value: state})
|
||||
}
|
||||
|
||||
func stateCookie(r *http.Request) (string, error) {
|
||||
stateCookie, err := r.Cookie(stateSessionCookie)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return stateCookie.Value, nil
|
||||
}
|
||||
|
||||
func setClaimsCookie(w http.ResponseWriter, claims map[string]interface{}) {
|
||||
b, _ := json.Marshal(claims)
|
||||
http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: base64.StdEncoding.EncodeToString(b)})
|
||||
}
|
||||
|
||||
func claimsCookie(r *http.Request) (map[string]interface{}, bool, error) {
|
||||
userCookie, err := r.Cookie(userSessionCookie)
|
||||
if err != nil {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(userCookie.Value)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("could not decode cookie: %w", err)
|
||||
}
|
||||
|
||||
var claims map[string]interface{}
|
||||
if err := json.Unmarshal(b, &claims); err != nil {
|
||||
return nil, false, errors.New("claims not in session")
|
||||
}
|
||||
return claims, false, err
|
||||
}
|
||||
20
dag/dag.go
20
dag/dag.go
@@ -25,6 +25,9 @@ package dag
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type Graph struct {
|
||||
@@ -52,6 +55,7 @@ func (g *Graph) AddNode(name string) error {
|
||||
}
|
||||
g.outputs[name] = make(map[string]struct{})
|
||||
g.inputs[name] = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,6 +65,7 @@ func (g *Graph) AddNodes(names ...string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -101,7 +106,9 @@ func (g *Graph) Toposort() ([]string, error) {
|
||||
L = append(L, n)
|
||||
|
||||
ms := make([]string, len(outputs[n]))
|
||||
for _, k := range keys(outputs[n]) {
|
||||
keys := maps.Keys(outputs[n])
|
||||
slices.Sort(keys)
|
||||
for _, k := range keys {
|
||||
m := k
|
||||
// i := outputs[n][m]
|
||||
// ms[i-1] = m
|
||||
@@ -130,15 +137,6 @@ func (g *Graph) Toposort() ([]string, error) {
|
||||
return L, nil
|
||||
}
|
||||
|
||||
func keys(m map[string]struct{}) []string {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (g *Graph) GetParents(id string) []string {
|
||||
var parents []string
|
||||
for node, targets := range g.outputs {
|
||||
@@ -147,6 +145,7 @@ func (g *Graph) GetParents(id string) []string {
|
||||
}
|
||||
}
|
||||
sort.Strings(parents)
|
||||
|
||||
return parents
|
||||
}
|
||||
|
||||
@@ -160,5 +159,6 @@ func (g *Graph) GetRoot() (string, error) {
|
||||
if len(roots) != 1 {
|
||||
return "", errors.New("more than one root")
|
||||
}
|
||||
|
||||
return roots[0], nil
|
||||
}
|
||||
|
||||
@@ -20,23 +20,17 @@
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
package dag
|
||||
package dag_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
func index(s []string, v string) int {
|
||||
for i, s := range s {
|
||||
if s == v {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
"github.com/SecurityBrewery/catalyst/dag"
|
||||
)
|
||||
|
||||
type Edge struct {
|
||||
From string
|
||||
@@ -44,13 +38,17 @@ type Edge struct {
|
||||
}
|
||||
|
||||
func TestDuplicatedNode(t *testing.T) {
|
||||
graph := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
graph := dag.NewGraph()
|
||||
assert.NoError(t, graph.AddNode("a"))
|
||||
assert.Error(t, graph.AddNode("a"))
|
||||
}
|
||||
|
||||
func TestWikipedia(t *testing.T) {
|
||||
graph := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
graph := dag.NewGraph()
|
||||
assert.NoError(t, graph.AddNodes("2", "3", "5", "7", "8", "9", "10", "11"))
|
||||
|
||||
edges := []Edge{
|
||||
@@ -79,27 +77,30 @@ func TestWikipedia(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, e := range edges {
|
||||
if i, j := index(result, e.From), index(result, e.To); i > j {
|
||||
if i, j := slices.Index(result, e.From), slices.Index(result, e.To); i > j {
|
||||
t.Errorf("dependency failed: not satisfy %v(%v) > %v(%v)", e.From, i, e.To, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCycle(t *testing.T) {
|
||||
graph := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
graph := dag.NewGraph()
|
||||
assert.NoError(t, graph.AddNodes("1", "2", "3"))
|
||||
|
||||
assert.NoError(t, graph.AddEdge("1", "2"))
|
||||
assert.NoError(t, graph.AddEdge("2", "3"))
|
||||
assert.NoError(t, graph.AddEdge("3", "1"))
|
||||
|
||||
_, err := graph.Toposort()
|
||||
if err == nil {
|
||||
if _, err := graph.Toposort(); err == nil {
|
||||
t.Errorf("closed path not detected in closed pathed graph")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraph_GetParents(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type fields struct {
|
||||
nodes []string
|
||||
edges map[string]string
|
||||
@@ -117,8 +118,11 @@ func TestGraph_GetParents(t *testing.T) {
|
||||
{"parents 3", fields{nodes: []string{"1", "2", "3"}, edges: map[string]string{"1": "3", "2": "3"}}, args{id: "3"}, []string{"1", "2"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
g := dag.NewGraph()
|
||||
for _, node := range tt.fields.nodes {
|
||||
assert.NoError(t, g.AddNode(node))
|
||||
}
|
||||
@@ -134,7 +138,9 @@ func TestGraph_GetParents(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_AddNode(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
|
||||
v := "1"
|
||||
assert.NoError(t, dag.AddNode(v))
|
||||
@@ -143,7 +149,9 @@ func TestDAG_AddNode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_AddEdge(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
assert.NoError(t, dag.AddNode("0"))
|
||||
assert.NoError(t, dag.AddNode("1"))
|
||||
assert.NoError(t, dag.AddNode("2"))
|
||||
@@ -162,7 +170,9 @@ func TestDAG_AddEdge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_GetParents(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
assert.NoError(t, dag.AddNode("1"))
|
||||
assert.NoError(t, dag.AddNode("2"))
|
||||
assert.NoError(t, dag.AddNode("3"))
|
||||
@@ -176,7 +186,9 @@ func TestDAG_GetParents(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_GetDescendants(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
assert.NoError(t, dag.AddNode("1"))
|
||||
assert.NoError(t, dag.AddNode("2"))
|
||||
assert.NoError(t, dag.AddNode("3"))
|
||||
@@ -188,7 +200,9 @@ func TestDAG_GetDescendants(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_Topsort(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
assert.NoError(t, dag.AddNode("1"))
|
||||
assert.NoError(t, dag.AddNode("2"))
|
||||
assert.NoError(t, dag.AddNode("3"))
|
||||
@@ -203,7 +217,9 @@ func TestDAG_Topsort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_TopsortStable(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
assert.NoError(t, dag.AddNode("1"))
|
||||
assert.NoError(t, dag.AddNode("2"))
|
||||
assert.NoError(t, dag.AddNode("3"))
|
||||
@@ -216,7 +232,9 @@ func TestDAG_TopsortStable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDAG_TopsortStable2(t *testing.T) {
|
||||
dag := NewGraph()
|
||||
t.Parallel()
|
||||
|
||||
dag := dag.NewGraph()
|
||||
|
||||
assert.NoError(t, dag.AddNodes("block-ioc", "block-iocs", "block-sender", "board", "fetch-iocs", "escalate", "extract-iocs", "mail-available", "search-email-gateway"))
|
||||
assert.NoError(t, dag.AddEdge("block-iocs", "block-ioc"))
|
||||
|
||||
@@ -23,7 +23,7 @@ func (db *Database) ArtifactGet(ctx context.Context, id int64, name string) (*mo
|
||||
FOR a in NOT_NULL(d.artifacts, [])
|
||||
FILTER a.name == @name
|
||||
RETURN a`
|
||||
cursor, _, err := db.Query(ctx, query, mergeMaps(ticketFilterVars, map[string]interface{}{
|
||||
cursor, _, err := db.Query(ctx, query, mergeMaps(ticketFilterVars, map[string]any{
|
||||
"@collection": TicketCollectionName,
|
||||
"ID": fmt.Sprint(id),
|
||||
"name": name,
|
||||
@@ -55,7 +55,8 @@ func (db *Database) ArtifactUpdate(ctx context.Context, id int64, name string, a
|
||||
LET newartifacts = APPEND(REMOVE_VALUE(d.artifacts, a), @artifact)
|
||||
UPDATE d WITH { "artifacts": newartifacts } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"@collection": TicketCollectionName,
|
||||
"ID": id,
|
||||
"name": name,
|
||||
@@ -69,7 +70,7 @@ func (db *Database) ArtifactUpdate(ctx context.Context, id int64, name string, a
|
||||
}
|
||||
|
||||
func (db *Database) EnrichArtifact(ctx context.Context, id int64, name string, enrichmentForm *model.EnrichmentForm) (*model.TicketWithTickets, error) {
|
||||
enrichment := model.Enrichment{time.Now().UTC(), enrichmentForm.Data, enrichmentForm.Name}
|
||||
enrichment := model.Enrichment{Created: time.Now().UTC(), Data: enrichmentForm.Data, Name: enrichmentForm.Name}
|
||||
|
||||
ticketFilterQuery, ticketFilterVars, err := db.Hooks.TicketWriteFilter(ctx)
|
||||
if err != nil {
|
||||
@@ -85,7 +86,8 @@ func (db *Database) EnrichArtifact(ctx context.Context, id int64, name string, e
|
||||
LET newartifacts = APPEND(REMOVE_VALUE(d.artifacts, a), MERGE(a, { "enrichments": newenrichments }))
|
||||
UPDATE d WITH { "artifacts": newartifacts } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"@collection": TicketCollectionName,
|
||||
"ID": id,
|
||||
"name": name,
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
|
||||
func toAutomation(doc *model.AutomationForm) interface{} {
|
||||
func toAutomation(doc *model.AutomationForm) *model.Automation {
|
||||
return &model.Automation{
|
||||
Image: doc.Image,
|
||||
Script: doc.Script,
|
||||
@@ -72,12 +72,13 @@ func (db *Database) AutomationUpdate(ctx context.Context, id string, automation
|
||||
|
||||
func (db *Database) AutomationDelete(ctx context.Context, id string) error {
|
||||
_, err := db.automationCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) AutomationList(ctx context.Context) ([]*model.AutomationResponse, error) {
|
||||
query := "FOR d IN @@collection SORT d._key ASC RETURN UNSET(d, 'script')"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": AutomationCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": AutomationCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -40,10 +40,12 @@ type Operation struct {
|
||||
Ids []driver.DocumentID
|
||||
}
|
||||
|
||||
var CreateOperation = &Operation{Type: bus.DatabaseEntryCreated}
|
||||
var ReadOperation = &Operation{Type: bus.DatabaseEntryRead}
|
||||
var (
|
||||
CreateOperation = &Operation{Type: bus.DatabaseEntryCreated}
|
||||
ReadOperation = &Operation{Type: bus.DatabaseEntryRead}
|
||||
)
|
||||
|
||||
func (db BusDatabase) Query(ctx context.Context, query string, vars map[string]interface{}, operation *Operation) (cur driver.Cursor, logs *model.LogEntry, err error) {
|
||||
func (db *BusDatabase) Query(ctx context.Context, query string, vars map[string]any, operation *Operation) (cur driver.Cursor, logs *model.LogEntry, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
cur, err = db.internal.Query(ctx, query, vars)
|
||||
@@ -53,51 +55,47 @@ func (db BusDatabase) Query(ctx context.Context, query string, vars map[string]i
|
||||
|
||||
switch {
|
||||
case operation.Type == bus.DatabaseEntryCreated, operation.Type == bus.DatabaseEntryUpdated:
|
||||
if err := db.bus.PublishDatabaseUpdate(operation.Ids, operation.Type); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{IDs: operation.Ids, Type: operation.Type})
|
||||
}
|
||||
|
||||
return cur, logs, err
|
||||
}
|
||||
|
||||
func (db BusDatabase) Remove(ctx context.Context) (err error) {
|
||||
func (db *BusDatabase) Remove(ctx context.Context) (err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
return db.internal.Remove(ctx)
|
||||
}
|
||||
|
||||
func (db BusDatabase) Collection(ctx context.Context, name string) (col driver.Collection, err error) {
|
||||
func (db *BusDatabase) Collection(ctx context.Context, name string) (col driver.Collection, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
return db.internal.Collection(ctx, name)
|
||||
}
|
||||
|
||||
type Collection struct {
|
||||
type Collection[T any] struct {
|
||||
internal driver.Collection
|
||||
db *BusDatabase
|
||||
}
|
||||
|
||||
func NewCollection(internal driver.Collection, db *BusDatabase) *Collection {
|
||||
return &Collection{internal: internal, db: db}
|
||||
func NewCollection[T any](internal driver.Collection, db *BusDatabase) *Collection[T] {
|
||||
return &Collection[T]{internal: internal, db: db}
|
||||
}
|
||||
|
||||
func (c Collection) CreateDocument(ctx, newctx context.Context, key string, document interface{}) (meta driver.DocumentMeta, err error) {
|
||||
func (c *Collection[T]) CreateDocument(ctx, newctx context.Context, key string, document *T) (meta driver.DocumentMeta, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
meta, err = c.internal.CreateDocument(newctx, &Keyed{Key: key, Doc: document})
|
||||
meta, err = c.internal.CreateDocument(newctx, &Keyed[T]{Key: key, Doc: document})
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
|
||||
err = c.db.bus.PublishDatabaseUpdate([]driver.DocumentID{meta.ID}, bus.DatabaseEntryCreated)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
c.db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{IDs: []driver.DocumentID{meta.ID}, Type: bus.DatabaseEntryCreated})
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (c Collection) CreateEdge(ctx, newctx context.Context, edge *driver.EdgeDocument) (meta driver.DocumentMeta, err error) {
|
||||
func (c *Collection[T]) CreateEdge(ctx, newctx context.Context, edge *driver.EdgeDocument) (meta driver.DocumentMeta, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
meta, err = c.internal.CreateDocument(newctx, edge)
|
||||
@@ -105,14 +103,12 @@ func (c Collection) CreateEdge(ctx, newctx context.Context, edge *driver.EdgeDoc
|
||||
return meta, err
|
||||
}
|
||||
|
||||
err = c.db.bus.PublishDatabaseUpdate([]driver.DocumentID{meta.ID}, bus.DatabaseEntryCreated)
|
||||
if err != nil {
|
||||
return meta, err
|
||||
}
|
||||
c.db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{IDs: []driver.DocumentID{meta.ID}, Type: bus.DatabaseEntryCreated})
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (c Collection) CreateEdges(ctx context.Context, edges []*driver.EdgeDocument) (meta driver.DocumentMetaSlice, err error) {
|
||||
func (c *Collection[T]) CreateEdges(ctx context.Context, edges []*driver.EdgeDocument) (meta driver.DocumentMetaSlice, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
metas, errs, err := c.internal.CreateDocuments(ctx, edges)
|
||||
@@ -128,21 +124,18 @@ func (c Collection) CreateEdges(ctx context.Context, edges []*driver.EdgeDocumen
|
||||
ids = append(ids, meta.ID)
|
||||
}
|
||||
|
||||
err = c.db.bus.PublishDatabaseUpdate(ids, bus.DatabaseEntryCreated)
|
||||
if err != nil {
|
||||
return metas, err
|
||||
}
|
||||
c.db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{IDs: ids, Type: bus.DatabaseEntryCreated})
|
||||
|
||||
return metas, nil
|
||||
}
|
||||
|
||||
func (c Collection) DocumentExists(ctx context.Context, id string) (exists bool, err error) {
|
||||
func (c *Collection[T]) DocumentExists(ctx context.Context, id string) (exists bool, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
return c.internal.DocumentExists(ctx, id)
|
||||
}
|
||||
|
||||
func (c Collection) ReadDocument(ctx context.Context, key string, result interface{}) (meta driver.DocumentMeta, err error) {
|
||||
func (c *Collection[T]) ReadDocument(ctx context.Context, key string, result *T) (meta driver.DocumentMeta, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
meta, err = c.internal.ReadDocument(ctx, key, result)
|
||||
@@ -150,7 +143,7 @@ func (c Collection) ReadDocument(ctx context.Context, key string, result interfa
|
||||
return
|
||||
}
|
||||
|
||||
func (c Collection) UpdateDocument(ctx context.Context, key string, update interface{}) (meta driver.DocumentMeta, err error) {
|
||||
func (c *Collection[T]) UpdateDocument(ctx context.Context, key string, update any) (meta driver.DocumentMeta, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
meta, err = c.internal.UpdateDocument(ctx, key, update)
|
||||
@@ -158,10 +151,12 @@ func (c Collection) UpdateDocument(ctx context.Context, key string, update inter
|
||||
return meta, err
|
||||
}
|
||||
|
||||
return meta, c.db.bus.PublishDatabaseUpdate([]driver.DocumentID{meta.ID}, bus.DatabaseEntryUpdated)
|
||||
c.db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{IDs: []driver.DocumentID{meta.ID}, Type: bus.DatabaseEntryUpdated})
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (c Collection) ReplaceDocument(ctx context.Context, key string, document interface{}) (meta driver.DocumentMeta, err error) {
|
||||
func (c *Collection[T]) ReplaceDocument(ctx context.Context, key string, document *T) (meta driver.DocumentMeta, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
meta, err = c.internal.ReplaceDocument(ctx, key, document)
|
||||
@@ -169,16 +164,18 @@ func (c Collection) ReplaceDocument(ctx context.Context, key string, document in
|
||||
return meta, err
|
||||
}
|
||||
|
||||
return meta, c.db.bus.PublishDatabaseUpdate([]driver.DocumentID{meta.ID}, bus.DatabaseEntryUpdated)
|
||||
c.db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{IDs: []driver.DocumentID{meta.ID}, Type: bus.DatabaseEntryUpdated})
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (c Collection) RemoveDocument(ctx context.Context, formatInt string) (meta driver.DocumentMeta, err error) {
|
||||
func (c *Collection[T]) RemoveDocument(ctx context.Context, formatInt string) (meta driver.DocumentMeta, err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
return c.internal.RemoveDocument(ctx, formatInt)
|
||||
}
|
||||
|
||||
func (c Collection) Truncate(ctx context.Context) (err error) {
|
||||
func (c *Collection[T]) Truncate(ctx context.Context) (err error) {
|
||||
defer func() { err = toHTTPErr(err) }()
|
||||
|
||||
return c.internal.Truncate(ctx)
|
||||
@@ -190,7 +187,9 @@ func toHTTPErr(err error) error {
|
||||
if errors.As(err, &ae) {
|
||||
return &api.HTTPError{Status: ae.Code, Internal: err}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package busdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
)
|
||||
|
||||
const (
|
||||
userContextKey = "user"
|
||||
groupContextKey = "groups"
|
||||
)
|
||||
|
||||
func SetContext(r *http.Request, user *model.UserResponse) *http.Request {
|
||||
user.Roles = role.Strings(role.Explodes(user.Roles))
|
||||
|
||||
return r.WithContext(context.WithValue(r.Context(), userContextKey, user))
|
||||
}
|
||||
|
||||
func SetGroupContext(r *http.Request, groups []string) *http.Request {
|
||||
return r.WithContext(context.WithValue(r.Context(), groupContextKey, groups))
|
||||
}
|
||||
|
||||
func UserContext(ctx context.Context, user *model.UserResponse) context.Context {
|
||||
user.Roles = role.Strings(role.Explodes(user.Roles))
|
||||
return context.WithValue(ctx, userContextKey, user)
|
||||
}
|
||||
|
||||
func UserFromContext(ctx context.Context) (*model.UserResponse, bool) {
|
||||
u, ok := ctx.Value(userContextKey).(*model.UserResponse)
|
||||
return u, ok
|
||||
}
|
||||
@@ -2,18 +2,18 @@ package busdb
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Keyed struct {
|
||||
type Keyed[T any] struct {
|
||||
Key string
|
||||
Doc interface{}
|
||||
Doc *T
|
||||
}
|
||||
|
||||
func (p Keyed) MarshalJSON() ([]byte, error) {
|
||||
func (p *Keyed[T]) MarshalJSON() ([]byte, error) {
|
||||
b, err := json.Marshal(p.Doc)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
var m map[string]any
|
||||
err = json.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
const LogCollectionName = "logs"
|
||||
|
||||
func (db *BusDatabase) LogCreate(ctx context.Context, logType, reference, message string) (*model.LogEntry, error) {
|
||||
user, ok := UserFromContext(ctx)
|
||||
user, _, ok := maut.UserFromContext(ctx)
|
||||
if !ok {
|
||||
return nil, errors.New("no user in context")
|
||||
}
|
||||
@@ -45,7 +46,10 @@ func (db *BusDatabase) LogBatchCreate(ctx context.Context, logentries []*model.L
|
||||
}
|
||||
}
|
||||
if ids != nil {
|
||||
go db.bus.PublishDatabaseUpdate(ids, bus.DatabaseEntryCreated)
|
||||
go db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{
|
||||
IDs: ids,
|
||||
Type: bus.DatabaseEntryCreated,
|
||||
})
|
||||
}
|
||||
|
||||
_, errs, err := db.logCollection.CreateDocuments(ctx, logentries)
|
||||
@@ -62,7 +66,7 @@ func (db *BusDatabase) LogBatchCreate(ctx context.Context, logentries []*model.L
|
||||
|
||||
func (db *BusDatabase) LogList(ctx context.Context, reference string) ([]*model.LogEntry, error) {
|
||||
query := "FOR d IN @@collection FILTER d.reference == @reference SORT d.created DESC RETURN d"
|
||||
cursor, err := db.internal.Query(ctx, query, map[string]interface{}{
|
||||
cursor, err := db.internal.Query(ctx, query, map[string]any{
|
||||
"@collection": LogCollectionName,
|
||||
"reference": reference,
|
||||
})
|
||||
|
||||
@@ -72,12 +72,13 @@ func (db *Database) DashboardUpdate(ctx context.Context, id string, dashboard *m
|
||||
|
||||
func (db *Database) DashboardDelete(ctx context.Context, id string) error {
|
||||
_, err := db.dashboardCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) DashboardList(ctx context.Context) ([]*model.DashboardResponse, error) {
|
||||
query := "FOR d IN @@collection RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": DashboardCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": DashboardCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,15 +104,16 @@ func (db *Database) parseWidgets(dashboard *model.Dashboard) error {
|
||||
|
||||
_, err := parser.Parse(widget.Aggregation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid aggregation query (%s): syntax error\n", widget.Aggregation)
|
||||
return fmt.Errorf("invalid aggregation query (%s): syntax error", widget.Aggregation)
|
||||
}
|
||||
|
||||
if widget.Filter != nil {
|
||||
_, err := parser.Parse(*widget.Filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid filter query (%s): syntax error\n", *widget.Filter)
|
||||
return fmt.Errorf("invalid filter query (%s): syntax error", *widget.Filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
124
database/db.go
124
database/db.go
@@ -2,8 +2,10 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/arangodb/go-driver/http"
|
||||
@@ -11,6 +13,7 @@ import (
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/database/migrations"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/index"
|
||||
)
|
||||
@@ -38,18 +41,18 @@ type Database struct {
|
||||
bus *bus.Bus
|
||||
Hooks *hooks.Hooks
|
||||
|
||||
templateCollection *busdb.Collection
|
||||
ticketCollection *busdb.Collection
|
||||
playbookCollection *busdb.Collection
|
||||
automationCollection *busdb.Collection
|
||||
userdataCollection *busdb.Collection
|
||||
userCollection *busdb.Collection
|
||||
tickettypeCollection *busdb.Collection
|
||||
jobCollection *busdb.Collection
|
||||
settingsCollection *busdb.Collection
|
||||
dashboardCollection *busdb.Collection
|
||||
templateCollection *busdb.Collection[model.TicketTemplate]
|
||||
ticketCollection *busdb.Collection[model.Ticket]
|
||||
playbookCollection *busdb.Collection[model.PlaybookTemplate]
|
||||
automationCollection *busdb.Collection[model.Automation]
|
||||
userdataCollection *busdb.Collection[model.UserData]
|
||||
userCollection *busdb.Collection[model.User]
|
||||
tickettypeCollection *busdb.Collection[model.TicketType]
|
||||
jobCollection *busdb.Collection[model.Job]
|
||||
settingsCollection *busdb.Collection[model.Settings]
|
||||
dashboardCollection *busdb.Collection[model.Dashboard]
|
||||
|
||||
relatedCollection *busdb.Collection
|
||||
relatedCollection *busdb.Collection[driver.EdgeDocument]
|
||||
// containsCollection *busdb.Collection
|
||||
}
|
||||
|
||||
@@ -66,17 +69,25 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
|
||||
name = Name
|
||||
}
|
||||
|
||||
conn, err := http.NewConnection(http.ConnectionConfig{Endpoints: []string{config.Host}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
var client driver.Client
|
||||
for {
|
||||
deadline, ok := ctx.Deadline()
|
||||
if ok && time.Until(deadline) < 0 {
|
||||
return nil, context.DeadlineExceeded
|
||||
}
|
||||
|
||||
client, err := driver.NewClient(driver.ClientConfig{
|
||||
Connection: conn,
|
||||
Authentication: driver.BasicAuthentication(config.User, config.Password),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
client, err = getClient(ctx, config)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, errors.New("could not load database, connection timed out")
|
||||
}
|
||||
|
||||
log.Printf("could not connect to database: %s, retrying in 10 seconds\n", err)
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
|
||||
hooks.DatabaseAfterConnect(ctx, client, name)
|
||||
@@ -145,26 +156,47 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
|
||||
bus: bus,
|
||||
Index: index,
|
||||
Hooks: hooks,
|
||||
templateCollection: busdb.NewCollection(templateCollection, hookedDB),
|
||||
ticketCollection: busdb.NewCollection(ticketCollection, hookedDB),
|
||||
playbookCollection: busdb.NewCollection(playbookCollection, hookedDB),
|
||||
automationCollection: busdb.NewCollection(automationCollection, hookedDB),
|
||||
relatedCollection: busdb.NewCollection(relatedCollection, hookedDB),
|
||||
userdataCollection: busdb.NewCollection(userdataCollection, hookedDB),
|
||||
userCollection: busdb.NewCollection(userCollection, hookedDB),
|
||||
tickettypeCollection: busdb.NewCollection(tickettypeCollection, hookedDB),
|
||||
jobCollection: busdb.NewCollection(jobCollection, hookedDB),
|
||||
settingsCollection: busdb.NewCollection(settingsCollection, hookedDB),
|
||||
dashboardCollection: busdb.NewCollection(dashboardCollection, hookedDB),
|
||||
templateCollection: busdb.NewCollection[model.TicketTemplate](templateCollection, hookedDB),
|
||||
ticketCollection: busdb.NewCollection[model.Ticket](ticketCollection, hookedDB),
|
||||
playbookCollection: busdb.NewCollection[model.PlaybookTemplate](playbookCollection, hookedDB),
|
||||
automationCollection: busdb.NewCollection[model.Automation](automationCollection, hookedDB),
|
||||
userdataCollection: busdb.NewCollection[model.UserData](userdataCollection, hookedDB),
|
||||
userCollection: busdb.NewCollection[model.User](userCollection, hookedDB),
|
||||
tickettypeCollection: busdb.NewCollection[model.TicketType](tickettypeCollection, hookedDB),
|
||||
jobCollection: busdb.NewCollection[model.Job](jobCollection, hookedDB),
|
||||
settingsCollection: busdb.NewCollection[model.Settings](settingsCollection, hookedDB),
|
||||
dashboardCollection: busdb.NewCollection[model.Dashboard](dashboardCollection, hookedDB),
|
||||
relatedCollection: busdb.NewCollection[driver.EdgeDocument](relatedCollection, hookedDB),
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func getClient(ctx context.Context, config *Config) (driver.Client, error) {
|
||||
conn, err := http.NewConnection(http.ConnectionConfig{Endpoints: []string{config.Host}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := driver.NewClient(driver.ClientConfig{
|
||||
Connection: conn,
|
||||
Authentication: driver.BasicAuthentication(config.User, config.Password),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := client.Version(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func SetupDB(ctx context.Context, client driver.Client, dbName string) (driver.Database, error) {
|
||||
databaseExists, err := client.DatabaseExists(ctx, dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not check if database exists: %w", err)
|
||||
}
|
||||
|
||||
var db driver.Database
|
||||
@@ -174,12 +206,12 @@ func SetupDB(ctx context.Context, client driver.Client, dbName string) (driver.D
|
||||
db, err = client.Database(ctx, dbName)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not create database: %w", err)
|
||||
}
|
||||
|
||||
collectionExists, err := db.CollectionExists(ctx, migrations.MigrationCollection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not check if collection exists: %w", err)
|
||||
}
|
||||
|
||||
if !collectionExists {
|
||||
@@ -194,16 +226,16 @@ func SetupDB(ctx context.Context, client driver.Client, dbName string) (driver.D
|
||||
}
|
||||
|
||||
func (db *Database) Truncate(ctx context.Context) {
|
||||
db.templateCollection.Truncate(ctx)
|
||||
db.ticketCollection.Truncate(ctx)
|
||||
db.playbookCollection.Truncate(ctx)
|
||||
db.automationCollection.Truncate(ctx)
|
||||
db.userdataCollection.Truncate(ctx)
|
||||
db.userCollection.Truncate(ctx)
|
||||
db.tickettypeCollection.Truncate(ctx)
|
||||
db.jobCollection.Truncate(ctx)
|
||||
db.relatedCollection.Truncate(ctx)
|
||||
db.settingsCollection.Truncate(ctx)
|
||||
db.dashboardCollection.Truncate(ctx)
|
||||
_ = db.templateCollection.Truncate(ctx)
|
||||
_ = db.ticketCollection.Truncate(ctx)
|
||||
_ = db.playbookCollection.Truncate(ctx)
|
||||
_ = db.automationCollection.Truncate(ctx)
|
||||
_ = db.userdataCollection.Truncate(ctx)
|
||||
_ = db.userCollection.Truncate(ctx)
|
||||
_ = db.tickettypeCollection.Truncate(ctx)
|
||||
_ = db.jobCollection.Truncate(ctx)
|
||||
_ = db.relatedCollection.Truncate(ctx)
|
||||
_ = db.settingsCollection.Truncate(ctx)
|
||||
_ = db.dashboardCollection.Truncate(ctx)
|
||||
// db.containsCollection.Truncate(ctx)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func (db *Database) toJobResponse(ctx context.Context, key string, doc *model.Jo
|
||||
inspect, err := cli.ContainerInspect(ctx, key)
|
||||
if err != nil || inspect.State == nil {
|
||||
if update {
|
||||
db.JobUpdate(ctx, key, &model.JobUpdate{
|
||||
_, _ = db.JobUpdate(ctx, key, &model.JobUpdate{
|
||||
Status: doc.Status,
|
||||
Running: false,
|
||||
})
|
||||
@@ -46,7 +46,7 @@ func (db *Database) toJobResponse(ctx context.Context, key string, doc *model.Jo
|
||||
} else if doc.Status != inspect.State.Status {
|
||||
status = inspect.State.Status
|
||||
if update {
|
||||
db.JobUpdate(ctx, key, &model.JobUpdate{
|
||||
_, _ = db.JobUpdate(ctx, key, &model.JobUpdate{
|
||||
Status: status,
|
||||
Running: doc.Running,
|
||||
})
|
||||
@@ -107,7 +107,7 @@ func (db *Database) JobUpdate(ctx context.Context, id string, job *model.JobUpda
|
||||
func (db *Database) JobLogAppend(ctx context.Context, id string, logLine string) error {
|
||||
query := `LET d = DOCUMENT(@@collection, @ID)
|
||||
UPDATE d WITH { "log": CONCAT(NOT_NULL(d.log, ""), @logline) } IN @@collection`
|
||||
cur, _, err := db.Query(ctx, query, map[string]interface{}{
|
||||
cur, _, err := db.Query(ctx, query, map[string]any{
|
||||
"@collection": JobCollectionName,
|
||||
"ID": id,
|
||||
"logline": logLine,
|
||||
@@ -125,10 +125,10 @@ func (db *Database) JobLogAppend(ctx context.Context, id string, logLine string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Database) JobComplete(ctx context.Context, id string, out interface{}) error {
|
||||
func (db *Database) JobComplete(ctx context.Context, id string, out any) error {
|
||||
query := `LET d = DOCUMENT(@@collection, @ID)
|
||||
UPDATE d WITH { "output": @out, "status": "completed", "running": false } IN @@collection`
|
||||
cur, _, err := db.Query(ctx, query, map[string]interface{}{
|
||||
cur, _, err := db.Query(ctx, query, map[string]any{
|
||||
"@collection": JobCollectionName,
|
||||
"ID": id,
|
||||
"out": out,
|
||||
@@ -148,12 +148,13 @@ func (db *Database) JobComplete(ctx context.Context, id string, out interface{})
|
||||
|
||||
func (db *Database) JobDelete(ctx context.Context, id string) error {
|
||||
_, err := db.jobCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) JobList(ctx context.Context) ([]*model.JobResponse, error) {
|
||||
query := "FOR d IN @@collection RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": JobCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": JobCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -185,27 +186,33 @@ func publishJobMapping(id, automation string, contextStructs *model.Context, ori
|
||||
return fmt.Errorf("message generation failed: %w", err)
|
||||
}
|
||||
|
||||
return publishJob(id, automation, contextStructs, origin, msg, db)
|
||||
db.bus.JobChannel.Publish(&bus.JobMsg{
|
||||
ID: id,
|
||||
Automation: automation,
|
||||
Origin: origin,
|
||||
Message: &model.Message{
|
||||
Context: contextStructs,
|
||||
Payload: msg,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishJob(id, automation string, contextStructs *model.Context, origin *model.Origin, payload map[string]interface{}, db *Database) error {
|
||||
return db.bus.PublishJob(id, automation, payload, contextStructs, origin)
|
||||
}
|
||||
|
||||
func generatePayload(msgMapping map[string]string, contextStructs *model.Context) (map[string]interface{}, error) {
|
||||
contextJson, err := json.Marshal(contextStructs)
|
||||
func generatePayload(msgMapping map[string]string, contextStructs *model.Context) (map[string]any, error) {
|
||||
contextJSON, err := json.Marshal(contextStructs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
automationContext := map[string]interface{}{}
|
||||
err = json.Unmarshal(contextJson, &automationContext)
|
||||
automationContext := map[string]any{}
|
||||
err = json.Unmarshal(contextJSON, &automationContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := caql.Parser{}
|
||||
msg := map[string]interface{}{}
|
||||
msg := map[string]any{}
|
||||
for arg, expr := range msgMapping {
|
||||
tree, err := parser.Parse(expr)
|
||||
if err != nil {
|
||||
@@ -218,5 +225,6 @@ func generatePayload(msgMapping map[string]string, contextStructs *model.Context
|
||||
}
|
||||
msg[arg] = v
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
@@ -32,34 +32,38 @@ func generateMigrations() ([]Migration, error) {
|
||||
|
||||
&createGraph{ID: "create-ticket-graph", Name: "Graph", EdgeDefinitions: []driver.EdgeDefinition{{Collection: "related", From: []string{"tickets"}, To: []string{"tickets"}}}},
|
||||
|
||||
&createDocument{ID: "create-template-default", Collection: "templates", Document: &busdb.Keyed{Key: "default", Doc: model.TicketTemplate{Schema: DefaultTemplateSchema, Name: "Default"}}},
|
||||
&createDocument{ID: "create-automation-vt.hash", Collection: "automations", Document: &busdb.Keyed{Key: "vt.hash", Doc: model.Automation{Image: "docker.io/python:3", Script: VTHashAutomation}}},
|
||||
&createDocument{ID: "create-automation-comment", Collection: "automations", Document: &busdb.Keyed{Key: "comment", Doc: model.Automation{Image: "docker.io/python:3", Script: CommentAutomation}}},
|
||||
&createDocument{ID: "create-automation-hash.sha1", Collection: "automations", Document: &busdb.Keyed{Key: "hash.sha1", Doc: model.Automation{Image: "docker.io/python:3", Script: SHA1HashAutomation}}},
|
||||
&createDocument{ID: "create-playbook-malware", Collection: "playbooks", Document: &busdb.Keyed{Key: "malware", Doc: model.PlaybookTemplate{Name: "Malware", Yaml: MalwarePlaybook}}},
|
||||
&createDocument{ID: "create-playbook-phishing", Collection: "playbooks", Document: &busdb.Keyed{Key: "phishing", Doc: model.PlaybookTemplate{Name: "Phishing", Yaml: PhishingPlaybook}}},
|
||||
&createDocument{ID: "create-tickettype-alert", Collection: "tickettypes", Document: &busdb.Keyed{Key: "alert", Doc: model.TicketType{Name: "Alerts", Icon: "mdi-alert", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument{ID: "create-tickettype-incident", Collection: "tickettypes", Document: &busdb.Keyed{Key: "incident", Doc: model.TicketType{Name: "Incidents", Icon: "mdi-radioactive", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument{ID: "create-tickettype-investigation", Collection: "tickettypes", Document: &busdb.Keyed{Key: "investigation", Doc: model.TicketType{Name: "Forensic Investigations", Icon: "mdi-fingerprint", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument{ID: "create-tickettype-hunt", Collection: "tickettypes", Document: &busdb.Keyed{Key: "hunt", Doc: model.TicketType{Name: "Threat Hunting", Icon: "mdi-target", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument[busdb.Keyed[model.TicketTemplate]]{ID: "create-template-default", Collection: "templates", Document: &busdb.Keyed[model.TicketTemplate]{Key: "default", Doc: &model.TicketTemplate{Schema: DefaultTemplateSchema, Name: "Default"}}},
|
||||
&createDocument[busdb.Keyed[model.Automation]]{ID: "create-automation-vt.hash", Collection: "automations", Document: &busdb.Keyed[model.Automation]{Key: "vt.hash", Doc: &model.Automation{Image: "docker.io/python:3", Script: VTHashAutomation}}},
|
||||
&createDocument[busdb.Keyed[model.Automation]]{ID: "create-automation-comment", Collection: "automations", Document: &busdb.Keyed[model.Automation]{Key: "comment", Doc: &model.Automation{Image: "docker.io/python:3", Script: CommentAutomation}}},
|
||||
&createDocument[busdb.Keyed[model.Automation]]{ID: "create-automation-hash.sha1", Collection: "automations", Document: &busdb.Keyed[model.Automation]{Key: "hash.sha1", Doc: &model.Automation{Image: "docker.io/python:3", Script: SHA1HashAutomation}}},
|
||||
&createDocument[busdb.Keyed[model.PlaybookTemplate]]{ID: "create-playbook-malware", Collection: "playbooks", Document: &busdb.Keyed[model.PlaybookTemplate]{Key: "malware", Doc: &model.PlaybookTemplate{Name: "Malware", Yaml: MalwarePlaybook}}},
|
||||
&createDocument[busdb.Keyed[model.PlaybookTemplate]]{ID: "create-playbook-phishing", Collection: "playbooks", Document: &busdb.Keyed[model.PlaybookTemplate]{Key: "phishing", Doc: &model.PlaybookTemplate{Name: "Phishing", Yaml: PhishingPlaybook}}},
|
||||
&createDocument[busdb.Keyed[model.TicketType]]{ID: "create-tickettype-alert", Collection: "tickettypes", Document: &busdb.Keyed[model.TicketType]{Key: "alert", Doc: &model.TicketType{Name: "Alerts", Icon: "mdi-alert", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument[busdb.Keyed[model.TicketType]]{ID: "create-tickettype-incident", Collection: "tickettypes", Document: &busdb.Keyed[model.TicketType]{Key: "incident", Doc: &model.TicketType{Name: "Incidents", Icon: "mdi-radioactive", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument[busdb.Keyed[model.TicketType]]{ID: "create-tickettype-investigation", Collection: "tickettypes", Document: &busdb.Keyed[model.TicketType]{Key: "investigation", Doc: &model.TicketType{Name: "Forensic Investigations", Icon: "mdi-fingerprint", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
&createDocument[busdb.Keyed[model.TicketType]]{ID: "create-tickettype-hunt", Collection: "tickettypes", Document: &busdb.Keyed[model.TicketType]{Key: "hunt", Doc: &model.TicketType{Name: "Threat Hunting", Icon: "mdi-target", DefaultTemplate: "default", DefaultPlaybooks: []string{}, DefaultGroups: nil}}},
|
||||
|
||||
&updateSchema{ID: "update-automation-collection-1", Name: "automations", DataType: "automation", Schema: `{"properties":{"image":{"type":"string"},"script":{"type":"string"}},"required":["image","script"],"type":"object"}`},
|
||||
&updateDocument{ID: "update-automation-vt.hash-1", Collection: "automations", Key: "vt.hash", Document: model.Automation{Image: "docker.io/python:3", Script: VTHashAutomation, Schema: pointer.String(`{"title":"Input","type":"object","properties":{"default":{"type":"string","title":"Value"}},"required":["default"]}`), Type: []string{"global", "artifact", "playbook"}}},
|
||||
&updateDocument{ID: "update-automation-comment-1", Collection: "automations", Key: "comment", Document: model.Automation{Image: "docker.io/python:3", Script: CommentAutomation, Type: []string{"playbook"}}},
|
||||
&updateDocument{ID: "update-automation-hash.sha1-1", Collection: "automations", Key: "hash.sha1", Document: model.Automation{Image: "docker.io/python:3", Script: SHA1HashAutomation, Schema: pointer.String(`{"title":"Input","type":"object","properties":{"default":{"type":"string","title":"Value"}},"required":["default"]}`), Type: []string{"global", "artifact", "playbook"}}},
|
||||
&updateDocument[model.Automation]{ID: "update-automation-vt.hash-1", Collection: "automations", Key: "vt.hash", Document: &model.Automation{Image: "docker.io/python:3", Script: VTHashAutomation, Schema: pointer.String(`{"title":"Input","type":"object","properties":{"default":{"type":"string","title":"Value"}},"required":["default"]}`), Type: []string{"global", "artifact", "playbook"}}},
|
||||
&updateDocument[model.Automation]{ID: "update-automation-comment-1", Collection: "automations", Key: "comment", Document: &model.Automation{Image: "docker.io/python:3", Script: CommentAutomation, Type: []string{"playbook"}}},
|
||||
&updateDocument[model.Automation]{ID: "update-automation-hash.sha1-1", Collection: "automations", Key: "hash.sha1", Document: &model.Automation{Image: "docker.io/python:3", Script: SHA1HashAutomation, Schema: pointer.String(`{"title":"Input","type":"object","properties":{"default":{"type":"string","title":"Value"}},"required":["default"]}`), Type: []string{"global", "artifact", "playbook"}}},
|
||||
|
||||
&createCollection{ID: "create-job-collection", Name: "jobs", DataType: "job", Schema: `{"properties":{"automation":{"type":"string"},"log":{"type":"string"},"payload":{},"origin":{"properties":{"artifact_origin":{"properties":{"artifact":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["artifact","ticket_id"],"type":"object"},"task_origin":{"properties":{"playbook_id":{"type":"string"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["playbook_id","task_id","ticket_id"],"type":"object"}},"type":"object"},"output":{"properties":{},"type":"object"},"running":{"type":"boolean"},"status":{"type":"string"}},"required":["automation","running","status"],"type":"object"}`},
|
||||
|
||||
&createDocument{ID: "create-playbook-simple", Collection: "playbooks", Document: &busdb.Keyed{Key: "simple", Doc: model.PlaybookTemplate{Name: "Simple", Yaml: SimplePlaybook}}},
|
||||
&createDocument[busdb.Keyed[model.PlaybookTemplate]]{ID: "create-playbook-simple", Collection: "playbooks", Document: &busdb.Keyed[model.PlaybookTemplate]{Key: "simple", Doc: &model.PlaybookTemplate{Name: "Simple", Yaml: SimplePlaybook}}},
|
||||
|
||||
&createCollection{ID: "create-settings-collection", Name: "settings", DataType: "settings", Schema: `{"type":"object","properties":{"artifactStates":{"title":"Artifact States","items":{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"]},"type":"array"},"artifactKinds":{"title":"Artifact Kinds","items":{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"]},"type":"array"},"timeformat":{"title":"Time Format","type":"string"}},"required":["timeformat","artifactKinds","artifactStates"]}`},
|
||||
&createDocument{ID: "create-settings-global", Collection: "settings", Document: &busdb.Keyed{Key: "global", Doc: model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "YYYY-MM-DDThh:mm:ss"}}},
|
||||
&createDocument[busdb.Keyed[model.Settings]]{ID: "create-settings-global", Collection: "settings", Document: &busdb.Keyed[model.Settings]{Key: "global", Doc: &model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "YYYY-MM-DDThh:mm:ss"}}},
|
||||
|
||||
&updateSchema{ID: "update-ticket-collection", Name: "tickets", DataType: "ticket", Schema: `{"properties":{"artifacts":{"items":{"properties":{"enrichments":{"additionalProperties":{"properties":{"created":{"format":"date-time","type":"string"},"data":{"example":{"hash":"b7a067a742c20d07a7456646de89bc2d408a1153"},"properties":{},"type":"object"},"name":{"example":"hash.sha1","type":"string"}},"required":["created","data","name"],"type":"object"},"type":"object"},"name":{"example":"2.2.2.2","type":"string"},"status":{"example":"Unknown","type":"string"},"type":{"type":"string"},"kind":{"type":"string"}},"required":["name"],"type":"object"},"type":"array"},"comments":{"items":{"properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["created","creator","message"],"type":"object"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"example":{"description":"my little incident"},"properties":{},"type":"object"},"files":{"items":{"properties":{"key":{"example":"myfile","type":"string"},"name":{"example":"notes.docx","type":"string"}},"required":["key","name"],"type":"object"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"example":"WannyCry","type":"string"},"owner":{"example":"bob","type":"string"},"playbooks":{"additionalProperties":{"properties":{"name":{"example":"Phishing","type":"string"},"tasks":{"additionalProperties":{"properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"properties":{},"type":"object"},"done":{"type":"boolean"},"join":{"example":false,"type":"boolean"},"payload":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"example":"Inform user","type":"string"},"next":{"additionalProperties":{"type":"string"},"type":"object"},"owner":{"type":"string"},"schema":{"properties":{},"type":"object"},"type":{"enum":["task","input","automation"],"example":"task","type":"string"}},"required":["created","done","name","type"],"type":"object"},"type":"object"}},"required":["name","tasks"],"type":"object"},"type":"object"},"read":{"example":["bob"],"items":{"type":"string"},"type":"array"},"references":{"items":{"properties":{"href":{"example":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-0144","type":"string"},"name":{"example":"CVE-2017-0144","type":"string"}},"required":["href","name"],"type":"object"},"type":"array"},"schema":{"example":"{}","type":"string"},"status":{"example":"open","type":"string"},"type":{"example":"incident","type":"string"},"write":{"example":["alice"],"items":{"type":"string"},"type":"array"}},"required":["created","modified","name","schema","status","type"],"type":"object"}`},
|
||||
|
||||
&createCollection{ID: "create-dashboard-collection", Name: "dashboards", DataType: "dashboards", Schema: `{"type":"object","properties":{"name":{"type":"string"},"widgets":{"items":{"type":"object","properties":{"aggregation":{"type":"string"},"filter":{"type":"string"},"name":{"type":"string"},"type":{"enum":[ "bar", "line", "pie" ]},"width": { "type": "integer", "minimum": 1, "maximum": 12 }},"required":["name","aggregation", "type", "width"]},"type":"array"}},"required":["name","widgets"]}`},
|
||||
|
||||
&updateDocument{ID: "update-settings-global-1", Collection: "settings", Key: "global", Document: &model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "yyyy-MM-dd hh:mm:ss"}},
|
||||
&updateDocument[model.Settings]{ID: "update-settings-global-1", Collection: "settings", Key: "global", Document: &model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "yyyy-MM-dd hh:mm:ss"}},
|
||||
|
||||
&updateSchema{ID: "update-user-simple-login", Name: "users", DataType: "user", Schema: `{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"salt":{"type":"string"},"sha256":{"type":"string"},"sha512":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`},
|
||||
|
||||
&mapRoles{ID: "simplify-roles"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -67,6 +71,7 @@ func loadSchema(dataType, jsonschema string) (*driver.CollectionSchemaOptions, e
|
||||
ticketCollectionSchema := &driver.CollectionSchemaOptions{Level: driver.CollectionSchemaLevelStrict, Message: fmt.Sprintf("Validation of %s failed", dataType)}
|
||||
|
||||
err := ticketCollectionSchema.LoadRule([]byte(jsonschema))
|
||||
|
||||
return ticketCollectionSchema, err
|
||||
}
|
||||
|
||||
@@ -101,6 +106,7 @@ func PerformMigrations(ctx context.Context, db driver.Database) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -171,41 +177,43 @@ func (m *createGraph) Migrate(ctx context.Context, db driver.Database) error {
|
||||
_, err := db.CreateGraph(ctx, m.Name, &driver.CreateGraphOptions{
|
||||
EdgeDefinitions: m.EdgeDefinitions,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type createDocument struct {
|
||||
type createDocument[T any] struct {
|
||||
ID string
|
||||
Collection string
|
||||
Document interface{}
|
||||
Document *T
|
||||
}
|
||||
|
||||
func (m *createDocument) MID() string {
|
||||
func (m *createDocument[T]) MID() string {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
func (m *createDocument) Migrate(ctx context.Context, driver driver.Database) error {
|
||||
func (m *createDocument[T]) Migrate(ctx context.Context, driver driver.Database) error {
|
||||
collection, err := driver.Collection(ctx, m.Collection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = collection.CreateDocument(ctx, m.Document)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type updateDocument struct {
|
||||
type updateDocument[T any] struct {
|
||||
ID string
|
||||
Collection string
|
||||
Key string
|
||||
Document interface{}
|
||||
Document *T
|
||||
}
|
||||
|
||||
func (m *updateDocument) MID() string {
|
||||
func (m *updateDocument[T]) MID() string {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
func (m *updateDocument) Migrate(ctx context.Context, driver driver.Database) error {
|
||||
func (m *updateDocument[T]) Migrate(ctx context.Context, driver driver.Database) error {
|
||||
collection, err := driver.Collection(ctx, m.Collection)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -218,9 +226,25 @@ func (m *updateDocument) Migrate(ctx context.Context, driver driver.Database) er
|
||||
|
||||
if !exists {
|
||||
_, err = collection.CreateDocument(ctx, m.Document)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = collection.ReplaceDocument(ctx, m.Key, m.Document)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type mapRoles struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (m mapRoles) MID() string {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
func (m mapRoles) Migrate(ctx context.Context, driver driver.Database) error {
|
||||
_, err := driver.Query(ctx, "FOR u IN users UPDATE u WITH {roles: u.roles[*].name} IN users", nil)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type PlaybookYAML struct {
|
||||
type TaskYAML struct {
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Schema interface{} `yaml:"schema"`
|
||||
Schema any `yaml:"schema"`
|
||||
Automation string `yaml:"automation"`
|
||||
Payload map[string]string `yaml:"payload"`
|
||||
Next map[string]string `yaml:"next"`
|
||||
@@ -42,6 +42,7 @@ func toPlaybooks(docs []*model.PlaybookTemplateForm) (map[string]*model.Playbook
|
||||
playbooks[strcase.ToKebab(playbook.Name)] = playbook
|
||||
}
|
||||
}
|
||||
|
||||
return playbooks, nil
|
||||
}
|
||||
|
||||
@@ -53,11 +54,17 @@ func toPlaybook(doc *model.PlaybookTemplateForm) (*model.Playbook, error) {
|
||||
}
|
||||
for idx, task := range ticketPlaybook.Tasks {
|
||||
if task.Schema != nil {
|
||||
task.Schema = dyno.ConvertMapI2MapS(task.Schema).(map[string]interface{})
|
||||
schema, ok := dyno.ConvertMapI2MapS(task.Schema).(map[string]any)
|
||||
if ok {
|
||||
task.Schema = schema
|
||||
} else {
|
||||
return nil, errors.New("could not convert schema")
|
||||
}
|
||||
}
|
||||
task.Created = time.Now().UTC()
|
||||
ticketPlaybook.Tasks[idx] = task
|
||||
}
|
||||
|
||||
return ticketPlaybook, nil
|
||||
}
|
||||
|
||||
@@ -84,7 +91,7 @@ func (db *Database) PlaybookCreate(ctx context.Context, playbook *model.Playbook
|
||||
var doc model.PlaybookTemplate
|
||||
newctx := driver.WithReturnNew(ctx, &doc)
|
||||
|
||||
meta, err := db.playbookCollection.CreateDocument(ctx, newctx, strcase.ToKebab(playbookYAML.Name), p)
|
||||
meta, err := db.playbookCollection.CreateDocument(ctx, newctx, strcase.ToKebab(playbookYAML.Name), &p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -104,6 +111,7 @@ func (db *Database) PlaybookGet(ctx context.Context, id string) (*model.Playbook
|
||||
|
||||
func (db *Database) PlaybookDelete(ctx context.Context, id string) error {
|
||||
_, err := db.playbookCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -121,7 +129,7 @@ func (db *Database) PlaybookUpdate(ctx context.Context, id string, playbook *mod
|
||||
var doc model.PlaybookTemplate
|
||||
ctx = driver.WithReturnNew(ctx, &doc)
|
||||
|
||||
meta, err := db.playbookCollection.ReplaceDocument(ctx, id, model.PlaybookTemplate{Name: pb.Name, Yaml: playbook.Yaml})
|
||||
meta, err := db.playbookCollection.ReplaceDocument(ctx, id, &model.PlaybookTemplate{Name: pb.Name, Yaml: playbook.Yaml})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -131,7 +139,7 @@ func (db *Database) PlaybookUpdate(ctx context.Context, id string, playbook *mod
|
||||
|
||||
func (db *Database) PlaybookList(ctx context.Context) ([]*model.PlaybookTemplateResponse, error) {
|
||||
query := "FOR d IN @@collection RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": PlaybookCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": PlaybookCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func playbookGraph(playbook *model.Playbook) (*dag.Graph, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
@@ -109,6 +110,7 @@ func active(playbook *model.Playbook, taskID string, d *dag.Graph, task *model.T
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -129,10 +131,11 @@ func active(playbook *model.Playbook, taskID string, d *dag.Graph, task *model.T
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func evalRequirement(aql string, data interface{}) (bool, error) {
|
||||
func evalRequirement(aql string, data any) (bool, error) {
|
||||
if aql == "" {
|
||||
return true, nil
|
||||
}
|
||||
@@ -143,9 +146,9 @@ func evalRequirement(aql string, data interface{}) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var dataMap map[string]interface{}
|
||||
var dataMap map[string]any
|
||||
if data != nil {
|
||||
if dataMapX, ok := data.(map[string]interface{}); ok {
|
||||
if dataMapX, ok := data.(map[string]any); ok {
|
||||
dataMap = dataMapX
|
||||
} else {
|
||||
log.Println("wrong data type for task data")
|
||||
@@ -160,6 +163,7 @@ func evalRequirement(aql string, data interface{}) (bool, error) {
|
||||
if b, ok := v.(bool); ok {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ var playbook2 = &model.Playbook{
|
||||
Name: "Phishing",
|
||||
Tasks: map[string]*model.Task{
|
||||
"board": {Next: map[string]string{
|
||||
"escalate": "boardInvolved == true",
|
||||
"aquire-mail": "boardInvolved == false",
|
||||
"escalate": "boardInvolved == true",
|
||||
"acquire-mail": "boardInvolved == false",
|
||||
}},
|
||||
"escalate": {},
|
||||
"aquire-mail": {Next: map[string]string{
|
||||
"acquire-mail": {Next: map[string]string{
|
||||
"extract-iocs": "schemaKey == 'yes'",
|
||||
"block-sender": "schemaKey == 'yes'",
|
||||
"search-email-gateway": "schemaKey == 'no'",
|
||||
@@ -34,11 +34,11 @@ var playbook3 = &model.Playbook{
|
||||
Name: "Phishing",
|
||||
Tasks: map[string]*model.Task{
|
||||
"board": {Next: map[string]string{
|
||||
"escalate": "boardInvolved == true",
|
||||
"aquire-mail": "boardInvolved == false",
|
||||
}, Data: map[string]interface{}{"boardInvolved": true}, Done: true},
|
||||
"escalate": "boardInvolved == true",
|
||||
"acquire-mail": "boardInvolved == false",
|
||||
}, Data: map[string]any{"boardInvolved": true}, Done: true},
|
||||
"escalate": {},
|
||||
"aquire-mail": {Next: map[string]string{
|
||||
"acquire-mail": {Next: map[string]string{
|
||||
"extract-iocs": "schemaKey == 'yes'",
|
||||
"block-sender": "schemaKey == 'yes'",
|
||||
"search-email-gateway": "schemaKey == 'no'",
|
||||
@@ -71,6 +71,8 @@ var playbook4 = &model.Playbook{
|
||||
}
|
||||
|
||||
func Test_canBeCompleted(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
playbook *model.Playbook
|
||||
taskID string
|
||||
@@ -83,18 +85,22 @@ func Test_canBeCompleted(t *testing.T) {
|
||||
}{
|
||||
{"playbook2 board", args{playbook: playbook2, taskID: "board"}, true, false},
|
||||
{"playbook2 escalate", args{playbook: playbook2, taskID: "escalate"}, false, false},
|
||||
{"playbook2 aquire-mail", args{playbook: playbook2, taskID: "aquire-mail"}, false, false},
|
||||
{"playbook2 acquire-mail", args{playbook: playbook2, taskID: "acquire-mail"}, false, false},
|
||||
{"playbook2 block-ioc", args{playbook: playbook2, taskID: "block-ioc"}, false, false},
|
||||
{"playbook3 board", args{playbook: playbook3, taskID: "board"}, false, false},
|
||||
{"playbook3 escalate", args{playbook: playbook3, taskID: "escalate"}, true, false},
|
||||
{"playbook3 aquire-mail", args{playbook: playbook3, taskID: "aquire-mail"}, false, false},
|
||||
{"playbook3 acquire-mail", args{playbook: playbook3, taskID: "acquire-mail"}, false, false},
|
||||
{"playbook3 block-ioc", args{playbook: playbook3, taskID: "block-ioc"}, false, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := activePlaybook(tt.args.playbook, tt.args.taskID)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("activePlaybook() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
@@ -105,6 +111,8 @@ func Test_canBeCompleted(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_playbookOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
playbook *model.Playbook
|
||||
}
|
||||
@@ -117,10 +125,14 @@ func Test_playbookOrder(t *testing.T) {
|
||||
{"playbook4", args{playbook: playbook4}, []string{"file-or-hash", "enter-hash", "upload", "hash", "virustotal"}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := toPlaybookResponse(tt.args.playbook)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("activePlaybook() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,13 @@ func (db *Database) RelatedCreate(ctx context.Context, id, id2 int64) error {
|
||||
From: driver.DocumentID(TicketCollectionName + "/" + strconv.Itoa(int(id))),
|
||||
To: driver.DocumentID(TicketCollectionName + "/" + strconv.Itoa(int(id2))),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) RelatedBatchCreate(ctx context.Context, edges []*driver.EdgeDocument) error {
|
||||
_, err := db.relatedCollection.CreateEdges(ctx, edges)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -33,7 +35,7 @@ func (db *Database) RelatedRemove(ctx context.Context, id, id2 int64) error {
|
||||
FOR d in @@collection
|
||||
FILTER (d._from == @id && d._to == @id2) || (d._to == @id && d._from == @id2)
|
||||
REMOVE d in @@collection`
|
||||
_, _, err := db.Query(ctx, q, map[string]interface{}{
|
||||
_, _, err := db.Query(ctx, q, map[string]any{
|
||||
"@collection": RelatedTicketsCollectionName,
|
||||
"id": driver.DocumentID(TicketCollectionName + "/" + strconv.Itoa(int(id))),
|
||||
"id2": driver.DocumentID(TicketCollectionName + "/" + strconv.Itoa(int(id2))),
|
||||
@@ -44,5 +46,6 @@ func (db *Database) RelatedRemove(ctx context.Context, id, id2 int64) error {
|
||||
driver.DocumentID(TicketCollectionName + "/" + strconv.Itoa(int(id2))),
|
||||
},
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ func (db *Database) Statistics(ctx context.Context) (*model.Statistics, error) {
|
||||
return &statistics, nil
|
||||
}
|
||||
|
||||
func (db *Database) WidgetData(ctx context.Context, aggregation string, filter *string) (map[string]interface{}, error) {
|
||||
func (db *Database) WidgetData(ctx context.Context, aggregation string, filter *string) (map[string]any, error) {
|
||||
parser := &caql.Parser{Searcher: db.Index, Prefix: "d."}
|
||||
|
||||
queryTree, err := parser.Parse(aggregation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid aggregation query (%s): syntax error\n", aggregation)
|
||||
return nil, fmt.Errorf("invalid aggregation query (%s): syntax error", aggregation)
|
||||
}
|
||||
aggregationString, err := queryTree.String()
|
||||
if err != nil {
|
||||
@@ -61,7 +61,7 @@ func (db *Database) WidgetData(ctx context.Context, aggregation string, filter *
|
||||
if filter != nil && *filter != "" {
|
||||
queryTree, err := parser.Parse(*filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter query (%s): syntax error\n", *filter)
|
||||
return nil, fmt.Errorf("invalid filter query (%s): syntax error", *filter)
|
||||
}
|
||||
filterString, err := queryTree.String()
|
||||
if err != nil {
|
||||
@@ -82,7 +82,7 @@ func (db *Database) WidgetData(ctx context.Context, aggregation string, filter *
|
||||
}
|
||||
defer cur.Close()
|
||||
|
||||
statistics := map[string]interface{}{}
|
||||
statistics := map[string]any{}
|
||||
if _, err := cur.ReadDocument(ctx, &statistics); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
)
|
||||
|
||||
type playbookResponse struct {
|
||||
PlaybookId string `json:"playbook_id"`
|
||||
PlaybookID string `json:"playbook_id"`
|
||||
PlaybookName string `json:"playbook_name"`
|
||||
Playbook model.Playbook `json:"playbook"`
|
||||
TicketId int64 `json:"ticket_id"`
|
||||
TicketID int64 `json:"ticket_id"`
|
||||
TicketName string `json:"ticket_name"`
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func (db *Database) TaskList(ctx context.Context) ([]*model.TaskWithContext, err
|
||||
FILTER d.status == 'open'
|
||||
FOR playbook IN NOT_NULL(VALUES(d.playbooks), [])
|
||||
RETURN { ticket_id: TO_NUMBER(d._key), ticket_name: d.name, playbook_id: POSITION(d.playbooks, playbook, true), playbook_name: playbook.name, playbook: playbook }`
|
||||
cursor, _, err := db.Query(ctx, query, mergeMaps(ticketFilterVars, map[string]interface{}{
|
||||
cursor, _, err := db.Query(ctx, query, mergeMaps(ticketFilterVars, map[string]any{
|
||||
"@collection": TicketCollectionName,
|
||||
}), busdb.ReadOperation)
|
||||
if err != nil {
|
||||
@@ -53,10 +53,10 @@ func (db *Database) TaskList(ctx context.Context) ([]*model.TaskWithContext, err
|
||||
for _, task := range playbook.Tasks {
|
||||
if task.Active {
|
||||
docs = append(docs, &model.TaskWithContext{
|
||||
PlaybookId: doc.PlaybookId,
|
||||
PlaybookId: doc.PlaybookID,
|
||||
PlaybookName: doc.PlaybookName,
|
||||
Task: task,
|
||||
TicketId: doc.TicketId,
|
||||
TicketId: doc.TicketID,
|
||||
TicketName: doc.TicketName,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,12 +62,13 @@ func (db *Database) TemplateUpdate(ctx context.Context, id string, template *mod
|
||||
|
||||
func (db *Database) TemplateDelete(ctx context.Context, id string) error {
|
||||
_, err := db.templateCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) TemplateList(ctx context.Context) ([]*model.TicketTemplateResponse, error) {
|
||||
query := "FOR d IN @@collection RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": TemplateCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": TemplateCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,16 +10,20 @@ import (
|
||||
"github.com/SecurityBrewery/catalyst/test"
|
||||
)
|
||||
|
||||
var template1 = &model.TicketTemplateForm{
|
||||
Schema: migrations.DefaultTemplateSchema,
|
||||
Name: "Template 1",
|
||||
}
|
||||
var default1 = &model.TicketTemplateForm{
|
||||
Schema: migrations.DefaultTemplateSchema,
|
||||
Name: "Default",
|
||||
}
|
||||
var (
|
||||
template1 = &model.TicketTemplateForm{
|
||||
Schema: migrations.DefaultTemplateSchema,
|
||||
Name: "Template 1",
|
||||
}
|
||||
default1 = &model.TicketTemplateForm{
|
||||
Schema: migrations.DefaultTemplateSchema,
|
||||
Name: "Default",
|
||||
}
|
||||
)
|
||||
|
||||
func TestDatabase_TemplateCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
template *model.TicketTemplateForm
|
||||
}
|
||||
@@ -35,7 +39,10 @@ func TestDatabase_TemplateCreate(t *testing.T) {
|
||||
{name: "Only name", args: args{template: &model.TicketTemplateForm{Name: "name"}}, wantErr: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -50,6 +57,8 @@ func TestDatabase_TemplateCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_TemplateDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
id string
|
||||
}
|
||||
@@ -62,7 +71,10 @@ func TestDatabase_TemplateDelete(t *testing.T) {
|
||||
{name: "Not existing", args: args{"foobar"}, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -81,6 +93,8 @@ func TestDatabase_TemplateDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_TemplateGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
id string
|
||||
}
|
||||
@@ -94,7 +108,10 @@ func TestDatabase_TemplateGet(t *testing.T) {
|
||||
{name: "Not existing", args: args{id: "foobar"}, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -108,6 +125,7 @@ func TestDatabase_TemplateGet(t *testing.T) {
|
||||
got, err := db.TemplateGet(test.Context(), tt.args.id)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("TemplateGet() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -120,6 +138,8 @@ func TestDatabase_TemplateGet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_TemplateList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
want []*model.TicketTemplateResponse
|
||||
@@ -128,7 +148,10 @@ func TestDatabase_TemplateList(t *testing.T) {
|
||||
{name: "Normal", want: []*model.TicketTemplateResponse{{ID: "default", Name: "Default", Schema: migrations.DefaultTemplateSchema}, {ID: "template-1", Name: template1.Name, Schema: template1.Schema}}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -142,6 +165,7 @@ func TestDatabase_TemplateList(t *testing.T) {
|
||||
got, err := db.TemplateList(test.Context())
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("TemplateList() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
assert.Equal(t, got, tt.want)
|
||||
@@ -150,6 +174,8 @@ func TestDatabase_TemplateList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_TemplateUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
id string
|
||||
template *model.TicketTemplateForm
|
||||
@@ -163,7 +189,10 @@ func TestDatabase_TemplateUpdate(t *testing.T) {
|
||||
{name: "Not existing", args: args{"foobar", template1}, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/SecurityBrewery/catalyst/index"
|
||||
)
|
||||
|
||||
func toTicket(ticketForm *model.TicketForm) (interface{}, error) {
|
||||
func toTicket(ticketForm *model.TicketForm) (any, error) {
|
||||
playbooks, err := toPlaybooks(ticketForm.Playbooks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -65,8 +65,9 @@ func toTicket(ticketForm *model.TicketForm) (interface{}, error) {
|
||||
ticket.Status = "open"
|
||||
}
|
||||
if ticketForm.ID != nil {
|
||||
return &busdb.Keyed{Key: strconv.FormatInt(*ticketForm.ID, 10), Doc: ticket}, nil
|
||||
return &busdb.Keyed[model.Ticket]{Key: strconv.FormatInt(*ticketForm.ID, 10), Doc: ticket}, nil
|
||||
}
|
||||
|
||||
return ticket, nil
|
||||
}
|
||||
|
||||
@@ -79,6 +80,7 @@ func toTicketResponses(tickets []*model.TicketSimpleResponse) ([]*model.TicketRe
|
||||
}
|
||||
extendedTickets = append(extendedTickets, tr)
|
||||
}
|
||||
|
||||
return extendedTickets, nil
|
||||
}
|
||||
|
||||
@@ -167,6 +169,7 @@ func toPlaybookResponses(playbooks map[string]*model.Playbook) (map[string]*mode
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
@@ -195,6 +198,7 @@ func toPlaybookResponse(playbook *model.Playbook) (*model.PlaybookResponse, erro
|
||||
re.Tasks[taskID] = rootTask
|
||||
i++
|
||||
}
|
||||
|
||||
return re, nil
|
||||
}
|
||||
|
||||
@@ -204,7 +208,7 @@ func (db *Database) TicketBatchCreate(ctx context.Context, ticketForms []*model.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dbTickets []interface{}
|
||||
var dbTickets []any
|
||||
for _, ticketForm := range ticketForms {
|
||||
ticket, err := toTicket(ticketForm)
|
||||
if err != nil {
|
||||
@@ -231,7 +235,7 @@ func (db *Database) TicketBatchCreate(ctx context.Context, ticketForms []*model.
|
||||
LET noiddoc = UNSET(keyeddoc, "id")
|
||||
INSERT noiddoc INTO @@collection
|
||||
RETURN NEW`
|
||||
apiTickets, _, err := db.ticketListQuery(ctx, query, mergeMaps(map[string]interface{}{
|
||||
apiTickets, _, err := db.ticketListQuery(ctx, query, mergeMaps(map[string]any{
|
||||
"tickets": dbTickets,
|
||||
}, ticketFilterVars), busdb.CreateOperation)
|
||||
if err != nil {
|
||||
@@ -247,7 +251,10 @@ func (db *Database) TicketBatchCreate(ctx context.Context, ticketForms []*model.
|
||||
ids = append(ids, driver.NewDocumentID(TicketCollectionName, fmt.Sprint(apiTicket.ID)))
|
||||
}
|
||||
|
||||
go db.bus.PublishDatabaseUpdate(ids, bus.DatabaseEntryUpdated)
|
||||
db.bus.DatabaseChannel.Publish(&bus.DatabaseUpdateMsg{
|
||||
IDs: ids,
|
||||
Type: bus.DatabaseEntryCreated,
|
||||
})
|
||||
|
||||
ticketResponses, err := toTicketResponses(apiTickets)
|
||||
if err != nil {
|
||||
@@ -294,6 +301,7 @@ func batchIndex(index *index.Index, tickets []*model.TicketSimpleResponse) error
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -306,9 +314,9 @@ func (db *Database) TicketGet(ctx context.Context, ticketID int64) (*model.Ticke
|
||||
return db.ticketGetQuery(ctx, ticketID, `LET d = DOCUMENT(@@collection, @ID) `+ticketFilterQuery+` RETURN d`, ticketFilterVars, busdb.ReadOperation)
|
||||
}
|
||||
|
||||
func (db *Database) ticketGetQuery(ctx context.Context, ticketID int64, query string, bindVars map[string]interface{}, operation *busdb.Operation) (*model.TicketWithTickets, error) {
|
||||
func (db *Database) ticketGetQuery(ctx context.Context, ticketID int64, query string, bindVars map[string]any, operation *busdb.Operation) (*model.TicketWithTickets, error) {
|
||||
if bindVars == nil {
|
||||
bindVars = map[string]interface{}{}
|
||||
bindVars = map[string]any{}
|
||||
}
|
||||
bindVars["@collection"] = TicketCollectionName
|
||||
if ticketID != 0 {
|
||||
@@ -350,7 +358,7 @@ func (db *Database) ticketGetQuery(ctx context.Context, ticketID int64, query st
|
||||
` + ticketFilterQuery + `
|
||||
RETURN d`
|
||||
|
||||
outTickets, _, err := db.ticketListQuery(ctx, ticketsQuery, mergeMaps(map[string]interface{}{
|
||||
outTickets, _, err := db.ticketListQuery(ctx, ticketsQuery, mergeMaps(map[string]any{
|
||||
"ID": fmt.Sprint(ticketID),
|
||||
"graph": TicketArtifactsGraphName,
|
||||
"@tickets": TicketCollectionName,
|
||||
@@ -368,7 +376,7 @@ func (db *Database) ticketGetQuery(ctx context.Context, ticketID int64, query st
|
||||
` + ticketFilterQuery + `
|
||||
RETURN d`
|
||||
|
||||
inTickets, _, err := db.ticketListQuery(ctx, ticketsQuery, mergeMaps(map[string]interface{}{
|
||||
inTickets, _, err := db.ticketListQuery(ctx, ticketsQuery, mergeMaps(map[string]any{
|
||||
"ID": fmt.Sprint(ticketID),
|
||||
"graph": TicketArtifactsGraphName,
|
||||
"@tickets": TicketCollectionName,
|
||||
@@ -387,7 +395,7 @@ func (db *Database) ticketGetQuery(ctx context.Context, ticketID int64, query st
|
||||
FOR a IN NOT_NULL(d.artifacts, [])
|
||||
FILTER POSITION(@artifacts, a.name)
|
||||
RETURN d`
|
||||
sameArtifactTickets, _, err := db.ticketListQuery(ctx, ticketsQuery, mergeMaps(map[string]interface{}{
|
||||
sameArtifactTickets, _, err := db.ticketListQuery(ctx, ticketsQuery, mergeMaps(map[string]any{
|
||||
"ID": fmt.Sprint(ticketID),
|
||||
"artifacts": artifactNames,
|
||||
}, ticketFilterVars), busdb.ReadOperation)
|
||||
@@ -395,7 +403,8 @@ func (db *Database) ticketGetQuery(ctx context.Context, ticketID int64, query st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tickets := append(outTickets, inTickets...)
|
||||
tickets := outTickets
|
||||
tickets = append(tickets, inTickets...)
|
||||
tickets = append(tickets, sameArtifactTickets...)
|
||||
sort.Slice(tickets, func(i, j int) bool {
|
||||
return tickets[i].ID < tickets[j].ID
|
||||
@@ -425,7 +434,8 @@ func (db *Database) TicketUpdate(ctx context.Context, ticketID int64, ticket *mo
|
||||
REPLACE d WITH @ticket IN @@collection
|
||||
RETURN NEW`
|
||||
ticket.Modified = time.Now().UTC() // TODO make setable?
|
||||
return db.ticketGetQuery(ctx, ticketID, query, mergeMaps(map[string]interface{}{"ticket": ticket}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, ticketID, query, mergeMaps(map[string]any{"ticket": ticket}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated, Ids: []driver.DocumentID{
|
||||
driver.NewDocumentID(TicketCollectionName, strconv.FormatInt(ticketID, 10)),
|
||||
},
|
||||
@@ -447,15 +457,15 @@ func (db *Database) TicketDelete(ctx context.Context, ticketID int64) error {
|
||||
}
|
||||
|
||||
func (db *Database) TicketList(ctx context.Context, ticketType string, query string, sorts []string, desc []bool, offset, count int64) (*model.TicketList, error) {
|
||||
binVars := map[string]interface{}{}
|
||||
binVars := map[string]any{}
|
||||
|
||||
var typeString = ""
|
||||
typeString := ""
|
||||
if ticketType != "" {
|
||||
typeString = "FILTER d.type == @type "
|
||||
binVars["type"] = ticketType
|
||||
}
|
||||
|
||||
var filterString = ""
|
||||
filterString := ""
|
||||
if query != "" {
|
||||
parser := &caql.Parser{Searcher: db.Index, Prefix: "d."}
|
||||
queryTree, err := parser.Parse(query)
|
||||
@@ -493,6 +503,7 @@ func (db *Database) TicketList(ctx context.Context, ticketType string, query str
|
||||
RETURN d`
|
||||
// RETURN KEEP(d, "_key", "id", "name", "type", "created")`
|
||||
ticketList, _, err := db.ticketListQuery(ctx, q, mergeMaps(binVars, ticketFilterVars), busdb.ReadOperation)
|
||||
|
||||
return &model.TicketList{
|
||||
Count: documentCount,
|
||||
Tickets: ticketList,
|
||||
@@ -500,9 +511,9 @@ func (db *Database) TicketList(ctx context.Context, ticketType string, query str
|
||||
// return map[string]interface{}{"tickets": ticketList, "count": documentCount}, err
|
||||
}
|
||||
|
||||
func (db *Database) ticketListQuery(ctx context.Context, query string, bindVars map[string]interface{}, operation *busdb.Operation) ([]*model.TicketSimpleResponse, *model.LogEntry, error) {
|
||||
func (db *Database) ticketListQuery(ctx context.Context, query string, bindVars map[string]any, operation *busdb.Operation) ([]*model.TicketSimpleResponse, *model.LogEntry, error) {
|
||||
if bindVars == nil {
|
||||
bindVars = map[string]interface{}{}
|
||||
bindVars = map[string]any{}
|
||||
}
|
||||
bindVars["@collection"] = TicketCollectionName
|
||||
|
||||
@@ -533,9 +544,9 @@ func (db *Database) ticketListQuery(ctx context.Context, query string, bindVars
|
||||
return docs, logEntry, nil
|
||||
}
|
||||
|
||||
func (db *Database) TicketCount(ctx context.Context, typequery, filterquery string, bindVars map[string]interface{}) (int, error) {
|
||||
func (db *Database) TicketCount(ctx context.Context, typequery, filterquery string, bindVars map[string]any) (int, error) {
|
||||
if bindVars == nil {
|
||||
bindVars = map[string]interface{}{}
|
||||
bindVars = map[string]any{}
|
||||
}
|
||||
bindVars["@collection"] = TicketCollectionName
|
||||
|
||||
@@ -555,11 +566,12 @@ func (db *Database) TicketCount(ctx context.Context, typequery, filterquery stri
|
||||
return 0, err
|
||||
}
|
||||
cursor.Close()
|
||||
|
||||
return documentCount, nil
|
||||
}
|
||||
|
||||
func sortQuery(paramsSort []string, paramsDesc []bool, bindVars map[string]interface{}) string {
|
||||
sort := ""
|
||||
func sortQuery(paramsSort []string, paramsDesc []bool, bindVars map[string]any) string {
|
||||
sortQuery := ""
|
||||
if len(paramsSort) > 0 {
|
||||
var sorts []string
|
||||
for i, column := range paramsSort {
|
||||
@@ -570,23 +582,25 @@ func sortQuery(paramsSort []string, paramsDesc []bool, bindVars map[string]inter
|
||||
sorts = append(sorts, colsort)
|
||||
bindVars[fmt.Sprintf("column%d", i)] = column
|
||||
}
|
||||
sort = "SORT " + strings.Join(sorts, ", ")
|
||||
sortQuery = "SORT " + strings.Join(sorts, ", ")
|
||||
}
|
||||
return sort
|
||||
|
||||
return sortQuery
|
||||
}
|
||||
|
||||
func mergeMaps(a map[string]interface{}, b map[string]interface{}) map[string]interface{} {
|
||||
merged := map[string]interface{}{}
|
||||
func mergeMaps(a map[string]any, b map[string]any) map[string]any {
|
||||
merged := map[string]any{}
|
||||
for k, v := range a {
|
||||
merged[k] = v
|
||||
}
|
||||
for k, v := range b {
|
||||
merged[k] = v
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
|
||||
func validate(e interface{}, schema *gojsonschema.Schema) error {
|
||||
func validate(e any, schema *gojsonschema.Schema) error {
|
||||
b, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -602,7 +616,9 @@ func validate(e interface{}, schema *gojsonschema.Schema) error {
|
||||
for _, e := range res.Errors() {
|
||||
l = append(l, e.String())
|
||||
}
|
||||
|
||||
return fmt.Errorf("validation failed: %v", strings.Join(l, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/iancoleman/strcase"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
"github.com/mingrammer/commonregex"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
@@ -34,7 +35,8 @@ func (db *Database) AddArtifact(ctx context.Context, id int64, artifact *model.A
|
||||
` + ticketFilterQuery + `
|
||||
UPDATE d WITH { "modified": @now, "artifacts": PUSH(NOT_NULL(d.artifacts, []), @artifact) } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"artifact": artifact, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"artifact": artifact, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -57,6 +59,7 @@ func inferType(name string) string {
|
||||
case commonregex.SHA256HexRegex.MatchString(name):
|
||||
return "sha256"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -73,7 +76,8 @@ func (db *Database) RemoveArtifact(ctx context.Context, id int64, name string) (
|
||||
LET newartifacts = REMOVE_VALUE(d.artifacts, a)
|
||||
UPDATE d WITH { "modified": @now, "artifacts": newartifacts } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"name": name, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"name": name, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -91,7 +95,8 @@ func (db *Database) SetTemplate(ctx context.Context, id int64, schema string) (*
|
||||
` + ticketFilterQuery + `
|
||||
UPDATE d WITH { "schema": @schema } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"schema": schema}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"schema": schema}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -106,7 +111,7 @@ func (db *Database) AddComment(ctx context.Context, id int64, comment *model.Com
|
||||
}
|
||||
|
||||
if comment.Creator == nil || *comment.Creator == "" {
|
||||
user, exists := busdb.UserFromContext(ctx)
|
||||
user, _, exists := maut.UserFromContext(ctx)
|
||||
if !exists {
|
||||
return nil, errors.New("no user in context")
|
||||
}
|
||||
@@ -122,7 +127,8 @@ func (db *Database) AddComment(ctx context.Context, id int64, comment *model.Com
|
||||
` + ticketFilterQuery + `
|
||||
UPDATE d WITH { "modified": @now, "comments": PUSH(NOT_NULL(d.comments, []), @comment) } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"comment": comment, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"comment": comment, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -140,7 +146,8 @@ func (db *Database) RemoveComment(ctx context.Context, id int64, commentID int64
|
||||
` + ticketFilterQuery + `
|
||||
UPDATE d WITH { "modified": @now, "comments": REMOVE_NTH(d.comments, @commentID) } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"commentID": commentID, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"commentID": commentID, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -158,7 +165,8 @@ func (db *Database) SetReferences(ctx context.Context, id int64, references []*m
|
||||
` + ticketFilterQuery + `
|
||||
UPDATE d WITH { "modified": @now, "references": @references } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"references": references, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"references": references, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -176,7 +184,8 @@ func (db *Database) AddFile(ctx context.Context, id int64, file *model.File) (*m
|
||||
` + ticketFilterQuery + `
|
||||
UPDATE d WITH { "modified": @now, "files": APPEND(NOT_NULL(d.files, []), [@file]) } IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{"file": file, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{"file": file, "now": time.Now().UTC()}, ticketFilterVars), &busdb.Operation{
|
||||
Type: bus.DatabaseEntryUpdated,
|
||||
Ids: []driver.DocumentID{
|
||||
driver.DocumentID(fmt.Sprintf("%s/%d", TicketCollectionName, id)),
|
||||
@@ -213,7 +222,7 @@ func (db *Database) AddTicketPlaybook(ctx context.Context, id int64, playbookTem
|
||||
LET newticket = MERGE(d, { "modified": @now, "playbooks": newplaybooks })
|
||||
REPLACE d WITH newticket IN @@collection
|
||||
RETURN NEW`
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"playbook": pb,
|
||||
"playbookID": findName(parentTicket.Playbooks, playbookID),
|
||||
"now": time.Now().UTC(),
|
||||
@@ -258,6 +267,7 @@ func runRootTask(ticket *model.TicketResponse, playbookID string, db *Database)
|
||||
}
|
||||
|
||||
runNextTasks(ticket.ID, playbookID, root.Next, root.Data, ticket, db)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -273,7 +283,8 @@ func (db *Database) RemoveTicketPlaybook(ctx context.Context, id int64, playbook
|
||||
LET newplaybooks = UNSET(d.playbooks, @playbookID)
|
||||
REPLACE d WITH MERGE(d, { "modified": @now, "playbooks": newplaybooks }) IN @@collection
|
||||
RETURN NEW`
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
|
||||
return db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"playbookID": playbookID,
|
||||
"now": time.Now().UTC(),
|
||||
}, ticketFilterVars), &busdb.Operation{
|
||||
|
||||
@@ -41,7 +41,7 @@ func (db *Database) TaskGet(ctx context.Context, id int64, playbookID string, ta
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *Database) TaskComplete(ctx context.Context, id int64, playbookID string, taskID string, data interface{}) (*model.TicketWithTickets, error) {
|
||||
func (db *Database) TaskComplete(ctx context.Context, id int64, playbookID string, taskID string, data any) (*model.TicketWithTickets, error) {
|
||||
inc, err := db.TicketGet(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -68,7 +68,7 @@ func (db *Database) TaskComplete(ctx context.Context, id int64, playbookID strin
|
||||
|
||||
UPDATE d WITH { "modified": @now, "playbooks": newplaybooks } IN @@collection
|
||||
RETURN NEW`
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"playbookID": playbookID,
|
||||
"taskID": taskID,
|
||||
"data": data,
|
||||
@@ -130,7 +130,7 @@ func (db *Database) TaskUpdateOwner(ctx context.Context, id int64, playbookID st
|
||||
|
||||
UPDATE d WITH { "modified": @now, "playbooks": newplaybooks } IN @@collection
|
||||
RETURN NEW`
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"playbookID": playbookID,
|
||||
"taskID": taskID,
|
||||
"owner": owner,
|
||||
@@ -148,7 +148,7 @@ func (db *Database) TaskUpdateOwner(ctx context.Context, id int64, playbookID st
|
||||
return ticket, nil
|
||||
}
|
||||
|
||||
func (db *Database) TaskUpdateData(ctx context.Context, id int64, playbookID string, taskID string, data map[string]interface{}) (*model.TicketWithTickets, error) {
|
||||
func (db *Database) TaskUpdateData(ctx context.Context, id int64, playbookID string, taskID string, data map[string]any) (*model.TicketWithTickets, error) {
|
||||
ticketFilterQuery, ticketFilterVars, err := db.Hooks.TicketWriteFilter(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -165,7 +165,7 @@ func (db *Database) TaskUpdateData(ctx context.Context, id int64, playbookID str
|
||||
|
||||
UPDATE d WITH { "modified": @now, "playbooks": newplaybooks } IN @@collection
|
||||
RETURN NEW`
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]interface{}{
|
||||
ticket, err := db.ticketGetQuery(ctx, id, query, mergeMaps(map[string]any{
|
||||
"playbookID": playbookID,
|
||||
"taskID": taskID,
|
||||
"data": data,
|
||||
@@ -198,7 +198,7 @@ func (db *Database) TaskRun(ctx context.Context, id int64, playbookID string, ta
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNextTasks(id int64, playbookID string, next map[string]string, data interface{}, ticket *model.TicketResponse, db *Database) {
|
||||
func runNextTasks(id int64, playbookID string, next map[string]string, data any, ticket *model.TicketResponse, db *Database) {
|
||||
for nextTaskID, requirement := range next {
|
||||
nextTask := ticket.Playbooks[playbookID].Tasks[nextTaskID]
|
||||
if nextTask.Type == model.TaskTypeAutomation {
|
||||
@@ -220,5 +220,6 @@ func runTask(ticketID int64, playbookID string, taskID string, task *model.TaskR
|
||||
msgContext := &model.Context{Playbook: playbook, Task: task, Ticket: ticket}
|
||||
origin := &model.Origin{TaskOrigin: &model.TaskOrigin{TaskId: taskID, PlaybookId: playbookID, TicketId: ticketID}}
|
||||
jobID := uuid.NewString()
|
||||
|
||||
return publishJobMapping(jobID, *task.Automation, msgContext, origin, task.Payload, db)
|
||||
}
|
||||
|
||||
@@ -75,12 +75,13 @@ func (db *Database) TicketTypeUpdate(ctx context.Context, id string, tickettype
|
||||
|
||||
func (db *Database) TicketTypeDelete(ctx context.Context, id string) error {
|
||||
_, err := db.tickettypeCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) TicketTypeList(ctx context.Context) ([]*model.TicketTypeResponse, error) {
|
||||
query := "FOR d IN @@collection RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": TicketTypeCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": TicketTypeCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
122
database/user.go
122
database/user.go
@@ -3,18 +3,20 @@ package database
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/iancoleman/strcase"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/generated/pointer"
|
||||
"github.com/SecurityBrewery/catalyst/generated/time"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
)
|
||||
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_")
|
||||
@@ -28,16 +30,17 @@ func generateKey() string {
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func toUser(user *model.UserForm, sha256 *string) *model.User {
|
||||
roles := []string{}
|
||||
roles = append(roles, role.Strings(role.Explodes(user.Roles))...)
|
||||
func toUser(user *model.UserForm, salt, sha256, sha512 *string) *model.User {
|
||||
u := &model.User{
|
||||
Blocked: user.Blocked,
|
||||
Roles: roles,
|
||||
Roles: user.Roles,
|
||||
Salt: salt,
|
||||
Sha256: sha256,
|
||||
Sha512: sha512,
|
||||
Apikey: user.Apikey,
|
||||
}
|
||||
|
||||
@@ -78,41 +81,74 @@ func (db *Database) UserGetOrCreate(ctx context.Context, newUser *model.UserForm
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.UserResponse{ID: newUser.ID, Roles: newUser.Roles, Blocked: newUser.Blocked}, nil
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (db *Database) UserCreate(ctx context.Context, newUser *model.UserForm) (*model.NewUserResponse, error) {
|
||||
var key string
|
||||
var hash *string
|
||||
var key, salt, sha256Hash, sha512Hash *string
|
||||
if newUser.Apikey {
|
||||
key = generateKey()
|
||||
hash = pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
|
||||
key, sha256Hash = generateAPIKey()
|
||||
} else if newUser.Password != nil {
|
||||
salt, sha512Hash = hashUserPassword(newUser)
|
||||
}
|
||||
|
||||
var doc model.User
|
||||
newctx := driver.WithReturnNew(ctx, &doc)
|
||||
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, hash))
|
||||
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, salt, sha256Hash, sha512Hash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toNewUserResponse(meta.Key, &doc, pointer.String(key)), nil
|
||||
return toNewUserResponse(meta.Key, &doc, key), nil
|
||||
}
|
||||
|
||||
func (db *Database) UserCreateSetupAPIKey(ctx context.Context, key string) (*model.UserResponse, error) {
|
||||
newUser := &model.UserForm{
|
||||
ID: "setup",
|
||||
Roles: []string{role.Admin},
|
||||
Roles: []string{maut.AdminRole},
|
||||
Apikey: true,
|
||||
Blocked: false,
|
||||
}
|
||||
hash := pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
|
||||
sha256Hash := pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
|
||||
|
||||
var doc model.User
|
||||
newctx := driver.WithReturnNew(ctx, &doc)
|
||||
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, hash))
|
||||
meta, err := db.userCollection.CreateDocument(ctx, newctx, strcase.ToKebab(newUser.ID), toUser(newUser, nil, sha256Hash, nil))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toUserResponse(meta.Key, &doc), nil
|
||||
}
|
||||
|
||||
func (db *Database) UserUpdate(ctx context.Context, id string, user *model.UserForm) (*model.UserResponse, error) {
|
||||
var doc model.User
|
||||
_, err := db.userCollection.ReadDocument(ctx, id, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if doc.Apikey {
|
||||
return nil, errors.New("cannot update an API key")
|
||||
}
|
||||
|
||||
var salt, sha512Hash *string
|
||||
if user.Password != nil {
|
||||
salt, sha512Hash = hashUserPassword(user)
|
||||
} else {
|
||||
salt = doc.Salt
|
||||
sha512Hash = doc.Sha512
|
||||
}
|
||||
|
||||
ctx = driver.WithReturnNew(ctx, &doc)
|
||||
|
||||
user.ID = id
|
||||
|
||||
meta, err := db.userCollection.ReplaceDocument(ctx, id, toUser(user, salt, nil, sha512Hash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -132,12 +168,13 @@ func (db *Database) UserGet(ctx context.Context, id string) (*model.UserResponse
|
||||
|
||||
func (db *Database) UserDelete(ctx context.Context, id string) error {
|
||||
_, err := db.userCollection.RemoveDocument(ctx, id)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) UserList(ctx context.Context) ([]*model.UserResponse, error) {
|
||||
query := "FOR d IN @@collection RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": UserCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": UserCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -158,12 +195,13 @@ func (db *Database) UserList(ctx context.Context) ([]*model.UserResponse, error)
|
||||
return docs, err
|
||||
}
|
||||
|
||||
func (db *Database) UserByHash(ctx context.Context, sha256 string) (*model.UserResponse, error) {
|
||||
func (db *Database) UserAPIKeyByHash(ctx context.Context, sha256 string) (*model.UserResponse, error) {
|
||||
query := `FOR d in @@collection
|
||||
FILTER d.sha256 == @sha256
|
||||
FILTER d.apikey && d.sha256 == @sha256
|
||||
RETURN d`
|
||||
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": UserCollectionName, "sha256": sha256}, busdb.ReadOperation)
|
||||
vars := map[string]any{"@collection": UserCollectionName, "sha256": sha256}
|
||||
cursor, _, err := db.Query(ctx, query, vars, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -178,25 +216,41 @@ func (db *Database) UserByHash(ctx context.Context, sha256 string) (*model.UserR
|
||||
return toUserResponse(meta.Key, &doc), err
|
||||
}
|
||||
|
||||
func (db *Database) UserUpdate(ctx context.Context, id string, user *model.UserForm) (*model.UserResponse, error) {
|
||||
func (db *Database) UserByIDAndPassword(ctx context.Context, id, password string) (*model.UserResponse, error) {
|
||||
log.Println("UserByIDAndPassword", id, password)
|
||||
query := `FOR d in @@collection
|
||||
FILTER d._key == @id && !d.apikey && d.sha512 == SHA512(CONCAT(d.salt, @password))
|
||||
RETURN d`
|
||||
|
||||
vars := map[string]any{"@collection": UserCollectionName, "id": id, "password": password}
|
||||
cursor, _, err := db.Query(ctx, query, vars, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
var doc model.User
|
||||
_, err := db.userCollection.ReadDocument(ctx, id, &doc)
|
||||
meta, err := cursor.ReadDocument(ctx, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if doc.Sha256 != nil {
|
||||
return nil, errors.New("cannot update an API key")
|
||||
}
|
||||
|
||||
ctx = driver.WithReturnNew(ctx, &doc)
|
||||
|
||||
user.ID = id
|
||||
|
||||
meta, err := db.userCollection.ReplaceDocument(ctx, id, toUser(user, nil))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toUserResponse(meta.Key, &doc), nil
|
||||
return toUserResponse(meta.Key, &doc), err
|
||||
}
|
||||
|
||||
func generateAPIKey() (key, sha256Hash *string) {
|
||||
newKey := generateKey()
|
||||
sha256Hash = pointer.String(fmt.Sprintf("%x", sha256.Sum256([]byte(newKey))))
|
||||
|
||||
return &newKey, sha256Hash
|
||||
}
|
||||
|
||||
func hashUserPassword(newUser *model.UserForm) (salt, sha512Hash *string) {
|
||||
if newUser.Password != nil {
|
||||
saltKey := generateKey()
|
||||
salt = &saltKey
|
||||
sha512Hash = pointer.String(fmt.Sprintf("%x", sha512.Sum512([]byte(saltKey+*newUser.Password))))
|
||||
}
|
||||
|
||||
return salt, sha512Hash
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ func (db *Database) UserDataCreate(ctx context.Context, id string, userdata *mod
|
||||
}
|
||||
|
||||
_, err := db.userdataCollection.CreateDocument(ctx, ctx, id, userdata)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -37,6 +38,7 @@ func (db *Database) UserDataGetOrCreate(ctx context.Context, id string, newUserD
|
||||
if err != nil {
|
||||
return toUserDataResponse(id, newUserData), db.UserDataCreate(ctx, id, newUserData)
|
||||
}
|
||||
|
||||
return setting, nil
|
||||
}
|
||||
|
||||
@@ -52,7 +54,7 @@ func (db *Database) UserDataGet(ctx context.Context, id string) (*model.UserData
|
||||
|
||||
func (db *Database) UserDataList(ctx context.Context) ([]*model.UserDataResponse, error) {
|
||||
query := "FOR d IN @@collection SORT d.username ASC RETURN d"
|
||||
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": UserDataCollectionName}, busdb.ReadOperation)
|
||||
cursor, _, err := db.Query(ctx, query, map[string]any{"@collection": UserDataCollectionName}, busdb.ReadOperation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ var bobResponse = &model.UserDataResponse{
|
||||
}
|
||||
|
||||
func TestDatabase_UserDataCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
id string
|
||||
setting *model.UserData
|
||||
@@ -37,7 +39,10 @@ func TestDatabase_UserDataCreate(t *testing.T) {
|
||||
{name: "Only settingname", args: args{id: "bob"}, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -52,6 +57,8 @@ func TestDatabase_UserDataCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_UserDataGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
id string
|
||||
}
|
||||
@@ -65,7 +72,10 @@ func TestDatabase_UserDataGet(t *testing.T) {
|
||||
{name: "Not existing", args: args{id: "foo"}, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -79,6 +89,7 @@ func TestDatabase_UserDataGet(t *testing.T) {
|
||||
got, err := db.UserDataGet(test.Context(), tt.args.id)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("UserDataGet() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
@@ -91,6 +102,8 @@ func TestDatabase_UserDataGet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_UserDataList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
want []*model.UserDataResponse
|
||||
@@ -99,7 +112,10 @@ func TestDatabase_UserDataList(t *testing.T) {
|
||||
{name: "Normal list", want: []*model.UserDataResponse{bobResponse}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -113,6 +129,7 @@ func TestDatabase_UserDataList(t *testing.T) {
|
||||
got, err := db.UserDataList(test.Context())
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("UserDataList() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -122,6 +139,8 @@ func TestDatabase_UserDataList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDatabase_UserDataUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
id string
|
||||
setting *model.UserData
|
||||
@@ -135,7 +154,10 @@ func TestDatabase_UserDataUpdate(t *testing.T) {
|
||||
{name: "Not existing", args: args{id: "foo"}, wantErr: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, _, _, _, _, db, cleanup, err := test.DB(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -28,16 +28,6 @@ paths:
|
||||
- { icon: "mdi-help-circle-outline", id: "unknown", name: "Unknown", color: "info" }
|
||||
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
|
||||
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
|
||||
roles: [
|
||||
"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write",
|
||||
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
|
||||
"admin:userdata:write", "analyst:automation:read",
|
||||
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read",
|
||||
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
|
||||
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
|
||||
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
|
||||
"engineer:playbook:write", "engineer:rule:write", "engineer:template:write",
|
||||
"engineer:tickettype:write" ]
|
||||
security: [ { roles: [ "settings:read" ] } ]
|
||||
post:
|
||||
tags: [ "settings" ]
|
||||
@@ -66,16 +56,6 @@ paths:
|
||||
- { icon: "mdi-help-circle-outline", id: "unknown", name: "Unknown", color: "info" }
|
||||
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
|
||||
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
|
||||
roles: [
|
||||
"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write",
|
||||
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
|
||||
"admin:userdata:write", "analyst:automation:read",
|
||||
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read",
|
||||
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
|
||||
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
|
||||
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
|
||||
"engineer:playbook:write", "engineer:rule:write", "engineer:template:write",
|
||||
"engineer:tickettype:write" ]
|
||||
security: [ { roles: [ "settings:write" ] } ]
|
||||
|
||||
definitions:
|
||||
|
||||
@@ -12,7 +12,7 @@ paths:
|
||||
description: "successful operation"
|
||||
schema: { $ref: "#/definitions/UserResponse" }
|
||||
examples:
|
||||
test: { id: bob, roles: [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read","analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: false }
|
||||
test: { id: bob, roles: [ "admin" ], blocked: false, apikey: false }
|
||||
security: [ { roles: [ "currentuser:read" ] } ]
|
||||
|
||||
/users:
|
||||
@@ -26,8 +26,8 @@ paths:
|
||||
schema: { type: array, items: { $ref: "#/definitions/UserResponse" } }
|
||||
examples:
|
||||
test:
|
||||
- { id: bob, blocked: false, roles: [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], apikey: false }
|
||||
- { id: script, roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
|
||||
- { id: bob, blocked: false, roles: [ "admin" ], apikey: false }
|
||||
- { id: script, roles: [ "engineer" ], blocked: false, apikey: true }
|
||||
security: [ { roles: [ "user:read" ] } ]
|
||||
post:
|
||||
tags: [ "users" ]
|
||||
@@ -40,7 +40,7 @@ paths:
|
||||
description: "successful operation"
|
||||
schema: { $ref: "#/definitions/NewUserResponse" }
|
||||
examples:
|
||||
test: { id: "syncscript", roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read" ], secret: "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH", blocked: false }
|
||||
test: { id: "syncscript", roles: [ "analyst" ], secret: "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH", blocked: false }
|
||||
security: [ { roles: [ "user:write" ] } ]
|
||||
/users/{id}:
|
||||
get:
|
||||
@@ -54,7 +54,7 @@ paths:
|
||||
description: "successful operation"
|
||||
schema: { $ref: "#/definitions/UserResponse" }
|
||||
examples:
|
||||
test: { id: "script", roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
|
||||
test: { id: "script", roles: [ "engineer" ], blocked: false, apikey: true }
|
||||
security: [ { roles: [ "user:read" ] } ]
|
||||
put:
|
||||
tags: [ "users" ]
|
||||
@@ -70,7 +70,7 @@ paths:
|
||||
examples:
|
||||
test:
|
||||
id: bob
|
||||
roles: [ "admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
|
||||
roles: [ "analyst", "admin" ]
|
||||
apikey: false
|
||||
blocked: false
|
||||
security: [ { roles: [ "user:write" ] } ]
|
||||
@@ -90,6 +90,7 @@ definitions:
|
||||
required: [ id, blocked, roles, apikey ]
|
||||
properties:
|
||||
id: { type: string }
|
||||
password: { type: string }
|
||||
blocked: { type: boolean }
|
||||
apikey: { type: boolean }
|
||||
roles: { type: array, items: { type: string } }
|
||||
@@ -101,7 +102,9 @@ definitions:
|
||||
blocked: { type: boolean }
|
||||
apikey: { type: boolean }
|
||||
roles: { type: array, items: { type: string } }
|
||||
salt: { type: string }
|
||||
sha256: { type: string } # for api keys
|
||||
sha512: { type: string } # for users
|
||||
|
||||
UserResponse:
|
||||
type: object
|
||||
|
||||
52
dev/docker-compose-with-keycloak.yml
Normal file
52
dev/docker-compose-with-keycloak.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
version: '2.4'
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:1.23
|
||||
volumes:
|
||||
- ./nginx-with-keycloak.conf:/etc/nginx/nginx.conf:ro
|
||||
ports: [ "80:80", "8529:8529", "9000:9000", "9002:9002", "9003:9003" ]
|
||||
networks: [ catalyst ]
|
||||
|
||||
arangodb:
|
||||
image: arangodb/arangodb:3.8.1
|
||||
environment:
|
||||
ARANGO_ROOT_PASSWORD: foobar
|
||||
networks: [ catalyst ]
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2021-12-10T23-03-39Z
|
||||
environment:
|
||||
MINIO_ROOT_USER: minio
|
||||
MINIO_ROOT_PASSWORD: minio123
|
||||
command: server /data -console-address ":9003"
|
||||
networks: [ catalyst ]
|
||||
|
||||
postgres:
|
||||
image: postgres:13
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: password
|
||||
networks: [ catalyst ]
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:14.0.0
|
||||
environment:
|
||||
DB_VENDOR: POSTGRES
|
||||
DB_ADDR: postgres
|
||||
DB_DATABASE: keycloak
|
||||
DB_USER: keycloak
|
||||
DB_SCHEMA: public
|
||||
DB_PASSWORD: password
|
||||
KEYCLOAK_USER: admin
|
||||
KEYCLOAK_PASSWORD: admin
|
||||
KEYCLOAK_IMPORT: /tmp/realm.json
|
||||
PROXY_ADDRESS_FORWARDING: "true"
|
||||
volumes:
|
||||
- ./keycloak/realm.json:/tmp/realm.json
|
||||
depends_on: [ postgres ]
|
||||
networks: [ catalyst ]
|
||||
|
||||
networks:
|
||||
catalyst:
|
||||
name: catalyst
|
||||
@@ -1,10 +1,10 @@
|
||||
version: '2.4'
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:1.21
|
||||
image: nginx:1.23
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
ports: [ "80:80", "8529:8529", "9000:9000", "9001:9001", "9002:9002", "9003:9003" ]
|
||||
ports: [ "80:80", "8529:8529", "9000:9000", "9003:9003" ]
|
||||
networks: [ catalyst ]
|
||||
|
||||
arangodb:
|
||||
@@ -13,13 +13,6 @@ services:
|
||||
ARANGO_ROOT_PASSWORD: foobar
|
||||
networks: [ catalyst ]
|
||||
|
||||
emitter:
|
||||
image: emitter/server
|
||||
environment:
|
||||
- EMITTER_LICENSE=PfA8ID8izeSlDUlNZgNXo77DQV9QzlNtxTk64WreCXKfDZsREAVXUXwh20UKOZdkALbLTmOytO_iC6mc_twKAQ:3
|
||||
# A9RysEsPJni8RaHeg_K0FKXQNfBrUyw-
|
||||
networks: [ catalyst ]
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2021-12-10T23-03-39Z
|
||||
environment:
|
||||
@@ -28,32 +21,6 @@ services:
|
||||
command: server /data -console-address ":9003"
|
||||
networks: [ catalyst ]
|
||||
|
||||
postgres:
|
||||
image: postgres:13
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: password
|
||||
networks: [ catalyst ]
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:14.0.0
|
||||
environment:
|
||||
DB_VENDOR: POSTGRES
|
||||
DB_ADDR: postgres
|
||||
DB_DATABASE: keycloak
|
||||
DB_USER: keycloak
|
||||
DB_SCHEMA: public
|
||||
DB_PASSWORD: password
|
||||
KEYCLOAK_USER: admin
|
||||
KEYCLOAK_PASSWORD: admin
|
||||
KEYCLOAK_IMPORT: /tmp/realm.json
|
||||
PROXY_ADDRESS_FORWARDING: "true"
|
||||
volumes:
|
||||
- ./keycloak/realm.json:/tmp/realm.json
|
||||
depends_on: [ postgres ]
|
||||
networks: [ catalyst ]
|
||||
|
||||
networks:
|
||||
catalyst:
|
||||
name: catalyst
|
||||
|
||||
@@ -455,8 +455,8 @@
|
||||
"secret": "d3ec0d91-b6ea-482d-8a4e-2f5a7ca0b4cb",
|
||||
"redirectUris": [
|
||||
"http://catalyst.internal.com/*",
|
||||
"http://localhost:8000/callback",
|
||||
"http://localhost/callback"
|
||||
"http://localhost:8000/auth/callback",
|
||||
"http://localhost/auth/callback"
|
||||
],
|
||||
"webOrigins": [
|
||||
"http://catalyst.internal.com",
|
||||
|
||||
112
dev/nginx-with-keycloak.conf
Normal file
112
dev/nginx-with-keycloak.conf
Normal file
@@ -0,0 +1,112 @@
|
||||
user www-data;
|
||||
worker_processes 5;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
index index.html index.htm;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] $status '
|
||||
'"$request" $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_catalyst host.docker.internal;
|
||||
proxy_pass http://$upstream_catalyst:8000;
|
||||
}
|
||||
|
||||
location /wss {
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_catalyst host.docker.internal;
|
||||
proxy_pass http://$upstream_catalyst:8000;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8529 default_server;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_arangodb arangodb;
|
||||
proxy_pass http://$upstream_arangodb:8529;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9000 default_server;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_minio minio;
|
||||
proxy_pass http://$upstream_minio:9000;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9002 default_server;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_keycloak keycloak;
|
||||
proxy_pass http://$upstream_keycloak:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9003 default_server;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_connect_timeout 300;
|
||||
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
chunked_transfer_encoding off;
|
||||
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_minio minio;
|
||||
proxy_pass http://$upstream_minio:9003;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,24 +70,6 @@ http {
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9002 default_server;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_keycloak keycloak;
|
||||
proxy_pass http://$upstream_keycloak:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Server $host;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9003 default_server;
|
||||
server_name _;
|
||||
@@ -110,13 +92,3 @@ http {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream {
|
||||
server {
|
||||
listen 9001;
|
||||
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
set $upstream_emitter emitter;
|
||||
proxy_pass $upstream_emitter:8080;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export SECRET=4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd
|
||||
|
||||
# export OIDC_ENABLE=true
|
||||
export OIDC_ISSUER=http://localhost:9002/auth/realms/catalyst
|
||||
export OIDC_CLIENT_SECRET=d3ec0d91-b6ea-482d-8a4e-2f5a7ca0b4cb
|
||||
|
||||
@@ -7,8 +8,6 @@ export ARANGO_DB_HOST=http://localhost:8529
|
||||
export ARANGO_DB_PASSWORD=foobar
|
||||
export S3_HOST=http://localhost:9000
|
||||
export S3_PASSWORD=minio123
|
||||
export EMITTER_IO_HOST=tcp://localhost:9001
|
||||
export EMITTER_IO_KEY=A9RysEsPJni8RaHeg_K0FKXQNfBrUyw-
|
||||
|
||||
export AUTH_BLOCK_NEW=false
|
||||
export AUTH_DEFAULT_ROLES=analyst,admin
|
||||
@@ -17,4 +16,4 @@ export EXTERNAL_ADDRESS=http://localhost
|
||||
export CATALYST_ADDRESS=http://host.docker.internal
|
||||
export INITIAL_API_KEY=d0169af94c40981eb4452a42fae536b6caa9be3a
|
||||
|
||||
go run cmd/catalyst-dev/*.go
|
||||
go run ../cmd/catalyst-dev/*.go
|
||||
20
dev/start_dev_with_keycloak.sh
Normal file
20
dev/start_dev_with_keycloak.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
export SECRET=4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd
|
||||
|
||||
export SIMPLE_AUTH_ENABLE=false
|
||||
export OIDC_ENABLE=true
|
||||
export OIDC_ISSUER=http://localhost:9002/auth/realms/catalyst
|
||||
export OIDC_CLIENT_SECRET=d3ec0d91-b6ea-482d-8a4e-2f5a7ca0b4cb
|
||||
|
||||
export ARANGO_DB_HOST=http://localhost:8529
|
||||
export ARANGO_DB_PASSWORD=foobar
|
||||
export S3_HOST=http://localhost:9000
|
||||
export S3_PASSWORD=minio123
|
||||
|
||||
export AUTH_BLOCK_NEW=false
|
||||
export AUTH_DEFAULT_ROLES=analyst,admin
|
||||
|
||||
export EXTERNAL_ADDRESS=http://localhost
|
||||
export CATALYST_ADDRESS=http://host.docker.internal
|
||||
export INITIAL_API_KEY=d0169af94c40981eb4452a42fae536b6caa9be3a
|
||||
|
||||
go run ../cmd/catalyst-dev/*.go
|
||||
BIN
docs/screenshots/dashboard.png
Normal file
BIN
docs/screenshots/dashboard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 218 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 191 KiB |
30
file.go
30
file.go
@@ -13,28 +13,30 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
tusd "github.com/tus/tusd/pkg/handler"
|
||||
"github.com/tus/tusd/pkg/s3store"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/api"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/storage"
|
||||
)
|
||||
|
||||
func tusdUpload(db *database.Database, bus *bus.Bus, client *s3.S3, external string) http.HandlerFunc {
|
||||
func tusdUpload(db *database.Database, catalystBus *bus.Bus, client *s3.S3, external string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ticketID := chi.URLParam(r, "ticketID")
|
||||
if ticketID == "" {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, errors.New("ticketID not given"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := storage.CreateBucket(client, ticketID); err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, fmt.Errorf("could not create bucket: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -50,11 +52,12 @@ func tusdUpload(db *database.Database, bus *bus.Bus, client *s3.S3, external str
|
||||
})
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, fmt.Errorf("could not create tusd handler: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
userID := "unknown"
|
||||
user, ok := busdb.UserFromContext(r.Context())
|
||||
user, _, ok := maut.UserFromContext(r.Context())
|
||||
if ok {
|
||||
userID = user.ID
|
||||
}
|
||||
@@ -73,13 +76,15 @@ func tusdUpload(db *database.Database, bus *bus.Bus, client *s3.S3, external str
|
||||
doc, err := db.AddFile(ctx, id, file)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = bus.PublishRequest(userID, "LinkFiles", []driver.DocumentID{driver.DocumentID(fmt.Sprintf("tickets/%d", doc.ID))})
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
catalystBus.RequestChannel.Publish(&bus.RequestMsg{
|
||||
User: userID,
|
||||
Function: "LinkFiles",
|
||||
IDs: []driver.DocumentID{driver.DocumentID(fmt.Sprintf("tickets/%d", doc.ID))},
|
||||
})
|
||||
}()
|
||||
|
||||
switch r.Method {
|
||||
@@ -92,7 +97,6 @@ func tusdUpload(db *database.Database, bus *bus.Bus, client *s3.S3, external str
|
||||
default:
|
||||
api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("unknown method"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,18 +105,21 @@ func upload(db *database.Database, client *s3.S3, uploader *s3manager.Uploader)
|
||||
ticketID := chi.URLParam(r, "ticketID")
|
||||
if ticketID == "" {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, errors.New("ticketID not given"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, err)
|
||||
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := storage.CreateBucket(client, ticketID); err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, fmt.Errorf("could not create bucket: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -123,12 +130,14 @@ func upload(db *database.Database, client *s3.S3, uploader *s3manager.Uploader)
|
||||
})
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseInt(ticketID, 10, 64)
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -138,6 +147,7 @@ func upload(db *database.Database, client *s3.S3, uploader *s3manager.Uploader)
|
||||
})
|
||||
if err != nil {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -148,12 +158,14 @@ func download(downloader *s3manager.Downloader) http.HandlerFunc {
|
||||
ticketID := chi.URLParam(r, "ticketID")
|
||||
if ticketID == "" {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, errors.New("ticketID not given"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
key := chi.URLParam(r, "key")
|
||||
if key == "" {
|
||||
api.JSONErrorStatus(w, http.StatusBadRequest, errors.New("key not given"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ spruce merge definition/*.yaml definition/enterprise/*.yaml >generated/catalyst.
|
||||
|
||||
echo generate caql parser and lexer
|
||||
cd definition || exit
|
||||
# antlr 4.10.1
|
||||
antlr -Dlanguage=Go -o ../generated/caql/parser CAQLParser.g4 CAQLLexer.g4
|
||||
antlr -Dlanguage=JavaScript -o ../ui/src/suggestions/grammar CAQLParser.g4 CAQLLexer.g4
|
||||
cd ..
|
||||
|
||||
echo generate json
|
||||
# openapi-generator 6.0.0
|
||||
openapi-generator generate -i generated/community.yml -o generated -g openapi
|
||||
mv generated/openapi.json generated/community.json
|
||||
openapi-generator generate -i generated/catalyst.yml -o generated -g openapi
|
||||
@@ -21,6 +23,7 @@ mv generated/openapi.json generated/catalyst.json
|
||||
echo generate server and tests
|
||||
swagger-go-chi generated/community.yml generated
|
||||
rm -rf generated/auth generated/cli
|
||||
find generated -type f -name "*.go" -print0 | xargs -0 sed -i '' -e 's#"github.com/go-chi/chi"#"github.com/go-chi/chi/v5"#g'
|
||||
|
||||
echo generate typescript client
|
||||
openapi-generator generate -i generated/catalyst.yml -o ui/src/client -g typescript-axios --artifact-version 1.0.0-SNAPSHOT
|
||||
@@ -32,3 +35,6 @@ rm -rf ui/src/client/.openapi-generator ui/src/client/git_push.sh ui/src/client/
|
||||
|
||||
go mod tidy
|
||||
gci write --Section Standard --Section Default --Section "Prefix(github.com/SecurityBrewery/catalyst)" .
|
||||
cd internal/maut
|
||||
gci write --Section Standard --Section Default --Section "Prefix(github.com/jonas-plum/maut)" .
|
||||
cd ../..
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
|
||||
@@ -68,7 +68,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Get", URL: "/currentuser"},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
||||
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin"}},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -239,7 +239,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Get", URL: "/settings"},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
|
||||
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -248,7 +248,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Post", URL: "/settings", Data: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "timeformat": "yyyy-MM-dd hh:mm:ss"}},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
|
||||
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "yyyy-MM-dd hh:mm:ss", "version": "0.0.0-test"},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -608,7 +608,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Get", URL: "/users"},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
|
||||
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"engineer"}}},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -617,7 +617,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Post", URL: "/users", Data: map[string]interface{}{"apikey": true, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst"}}},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: map[string]interface{}{"blocked": false, "id": "syncscript", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read"}, "secret": "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"},
|
||||
Body: map[string]interface{}{"blocked": false, "id": "syncscript", "roles": []interface{}{"analyst"}, "secret": "v39bOuobnlEljfWzjAgoKzhmnh1xSMxH"},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -626,7 +626,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Get", URL: "/users/script"},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
||||
Body: map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"engineer"}},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -635,7 +635,7 @@ var Tests = []struct {
|
||||
Args: Args{Method: "Put", URL: "/users/bob", Data: map[string]interface{}{"apikey": false, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst", "admin"}}},
|
||||
Want: Want{
|
||||
Status: 200,
|
||||
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:dashboard:write", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:dashboard:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
|
||||
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"analyst", "admin"}},
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Code generated from CAQLLexer.g4 by ANTLR 4.9.3. DO NOT EDIT.
|
||||
// Code generated from CAQLLexer.g4 by ANTLR 4.10.1. DO NOT EDIT.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
@@ -11,391 +12,9 @@ import (
|
||||
|
||||
// Suppress unused import error
|
||||
var _ = fmt.Printf
|
||||
var _ = sync.Once{}
|
||||
var _ = unicode.IsLetter
|
||||
|
||||
var serializedLexerAtn = []uint16{
|
||||
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 80, 739,
|
||||
8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7,
|
||||
9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12,
|
||||
4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4,
|
||||
18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23,
|
||||
9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9,
|
||||
28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33,
|
||||
4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4,
|
||||
39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44,
|
||||
9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9,
|
||||
49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54,
|
||||
4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4,
|
||||
60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65,
|
||||
9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9,
|
||||
70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75,
|
||||
4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4,
|
||||
81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86,
|
||||
9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9,
|
||||
91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96,
|
||||
4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101,
|
||||
4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106,
|
||||
9, 106, 4, 107, 9, 107, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4,
|
||||
3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 9, 3, 9,
|
||||
3, 9, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3,
|
||||
14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18,
|
||||
3, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3,
|
||||
23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27,
|
||||
3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3,
|
||||
29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 5, 29, 294, 10, 29, 3, 30, 3, 30,
|
||||
3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 3,
|
||||
32, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34,
|
||||
3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3,
|
||||
35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37,
|
||||
3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3,
|
||||
39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41,
|
||||
3, 41, 3, 41, 3, 41, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3,
|
||||
42, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43,
|
||||
3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3,
|
||||
44, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46,
|
||||
3, 46, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3,
|
||||
48, 5, 48, 414, 10, 48, 3, 49, 3, 49, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50,
|
||||
3, 50, 3, 50, 3, 50, 5, 50, 426, 10, 50, 3, 51, 3, 51, 3, 51, 3, 51, 3,
|
||||
51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52,
|
||||
3, 52, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3,
|
||||
54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55,
|
||||
3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, 3,
|
||||
56, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58,
|
||||
3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 59, 3,
|
||||
59, 3, 59, 3, 60, 3, 60, 3, 60, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 61,
|
||||
3, 61, 3, 62, 3, 62, 3, 62, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3,
|
||||
63, 3, 63, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 64, 3, 64, 3, 64,
|
||||
3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 3,
|
||||
67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68,
|
||||
3, 68, 3, 69, 3, 69, 3, 69, 3, 69, 3, 70, 3, 70, 7, 70, 555, 10, 70, 12,
|
||||
70, 14, 70, 558, 11, 70, 3, 71, 3, 71, 7, 71, 562, 10, 71, 12, 71, 14,
|
||||
71, 565, 11, 71, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 6, 71, 572, 10, 71,
|
||||
13, 71, 14, 71, 573, 3, 71, 3, 71, 3, 71, 3, 71, 6, 71, 580, 10, 71, 13,
|
||||
71, 14, 71, 581, 5, 71, 584, 10, 71, 3, 72, 3, 72, 7, 72, 588, 10, 72,
|
||||
12, 72, 14, 72, 591, 11, 72, 3, 72, 5, 72, 594, 10, 72, 3, 72, 3, 72, 6,
|
||||
72, 598, 10, 72, 13, 72, 14, 72, 599, 3, 72, 3, 72, 5, 72, 604, 10, 72,
|
||||
3, 72, 6, 72, 607, 10, 72, 13, 72, 14, 72, 608, 5, 72, 611, 10, 72, 3,
|
||||
73, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 74, 3, 74, 3, 74, 7, 74, 622,
|
||||
10, 74, 12, 74, 14, 74, 625, 11, 74, 3, 74, 3, 74, 3, 74, 3, 74, 3, 74,
|
||||
3, 74, 3, 74, 7, 74, 634, 10, 74, 12, 74, 14, 74, 637, 11, 74, 3, 74, 5,
|
||||
74, 640, 10, 74, 3, 75, 3, 75, 3, 75, 3, 75, 7, 75, 646, 10, 75, 12, 75,
|
||||
14, 75, 649, 11, 75, 3, 75, 5, 75, 652, 10, 75, 3, 75, 3, 75, 5, 75, 656,
|
||||
10, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 76, 3, 76, 7, 76, 664, 10, 76, 12,
|
||||
76, 14, 76, 667, 11, 76, 3, 76, 3, 76, 3, 76, 3, 76, 3, 76, 3, 77, 3, 77,
|
||||
3, 77, 3, 77, 3, 78, 3, 78, 3, 79, 3, 79, 3, 80, 3, 80, 3, 81, 3, 81, 3,
|
||||
82, 3, 82, 3, 83, 3, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 86, 3, 86, 3, 87,
|
||||
3, 87, 3, 88, 3, 88, 3, 89, 3, 89, 3, 90, 3, 90, 3, 91, 3, 91, 3, 92, 3,
|
||||
92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 95, 3, 95, 3, 96, 3, 96, 3, 97, 3, 97,
|
||||
3, 98, 3, 98, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 102, 3,
|
||||
102, 3, 103, 3, 103, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3,
|
||||
107, 3, 107, 3, 107, 3, 107, 3, 665, 2, 108, 3, 3, 5, 4, 7, 5, 9, 6, 11,
|
||||
7, 13, 8, 15, 9, 17, 10, 19, 11, 21, 12, 23, 13, 25, 14, 27, 15, 29, 16,
|
||||
31, 17, 33, 18, 35, 19, 37, 20, 39, 21, 41, 22, 43, 23, 45, 24, 47, 25,
|
||||
49, 26, 51, 27, 53, 28, 55, 29, 57, 30, 59, 31, 61, 32, 63, 33, 65, 34,
|
||||
67, 35, 69, 36, 71, 37, 73, 38, 75, 39, 77, 40, 79, 41, 81, 42, 83, 43,
|
||||
85, 44, 87, 45, 89, 46, 91, 47, 93, 48, 95, 49, 97, 50, 99, 51, 101, 52,
|
||||
103, 53, 105, 54, 107, 55, 109, 56, 111, 57, 113, 58, 115, 59, 117, 60,
|
||||
119, 61, 121, 62, 123, 63, 125, 64, 127, 65, 129, 66, 131, 67, 133, 68,
|
||||
135, 69, 137, 70, 139, 71, 141, 72, 143, 73, 145, 74, 147, 75, 149, 76,
|
||||
151, 77, 153, 78, 155, 79, 157, 2, 159, 2, 161, 2, 163, 2, 165, 2, 167,
|
||||
2, 169, 2, 171, 2, 173, 2, 175, 2, 177, 2, 179, 2, 181, 2, 183, 2, 185,
|
||||
2, 187, 2, 189, 2, 191, 2, 193, 2, 195, 2, 197, 2, 199, 2, 201, 2, 203,
|
||||
2, 205, 2, 207, 2, 209, 2, 211, 2, 213, 80, 3, 2, 39, 5, 2, 67, 92, 97,
|
||||
97, 99, 124, 6, 2, 50, 59, 67, 92, 97, 97, 99, 124, 3, 2, 51, 59, 3, 2,
|
||||
50, 51, 4, 2, 45, 45, 47, 47, 4, 2, 41, 41, 94, 94, 4, 2, 36, 36, 94, 94,
|
||||
4, 2, 12, 12, 15, 15, 5, 2, 11, 13, 15, 15, 34, 34, 5, 2, 50, 59, 67, 72,
|
||||
99, 104, 3, 2, 50, 59, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4,
|
||||
2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 71, 71, 103, 103, 4,
|
||||
2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4,
|
||||
2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4,
|
||||
2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4,
|
||||
2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4,
|
||||
2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4,
|
||||
2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4,
|
||||
2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 2,
|
||||
738, 2, 3, 3, 2, 2, 2, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2,
|
||||
2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3,
|
||||
2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25,
|
||||
3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2,
|
||||
33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2,
|
||||
2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 2, 47, 3, 2, 2,
|
||||
2, 2, 49, 3, 2, 2, 2, 2, 51, 3, 2, 2, 2, 2, 53, 3, 2, 2, 2, 2, 55, 3, 2,
|
||||
2, 2, 2, 57, 3, 2, 2, 2, 2, 59, 3, 2, 2, 2, 2, 61, 3, 2, 2, 2, 2, 63, 3,
|
||||
2, 2, 2, 2, 65, 3, 2, 2, 2, 2, 67, 3, 2, 2, 2, 2, 69, 3, 2, 2, 2, 2, 71,
|
||||
3, 2, 2, 2, 2, 73, 3, 2, 2, 2, 2, 75, 3, 2, 2, 2, 2, 77, 3, 2, 2, 2, 2,
|
||||
79, 3, 2, 2, 2, 2, 81, 3, 2, 2, 2, 2, 83, 3, 2, 2, 2, 2, 85, 3, 2, 2, 2,
|
||||
2, 87, 3, 2, 2, 2, 2, 89, 3, 2, 2, 2, 2, 91, 3, 2, 2, 2, 2, 93, 3, 2, 2,
|
||||
2, 2, 95, 3, 2, 2, 2, 2, 97, 3, 2, 2, 2, 2, 99, 3, 2, 2, 2, 2, 101, 3,
|
||||
2, 2, 2, 2, 103, 3, 2, 2, 2, 2, 105, 3, 2, 2, 2, 2, 107, 3, 2, 2, 2, 2,
|
||||
109, 3, 2, 2, 2, 2, 111, 3, 2, 2, 2, 2, 113, 3, 2, 2, 2, 2, 115, 3, 2,
|
||||
2, 2, 2, 117, 3, 2, 2, 2, 2, 119, 3, 2, 2, 2, 2, 121, 3, 2, 2, 2, 2, 123,
|
||||
3, 2, 2, 2, 2, 125, 3, 2, 2, 2, 2, 127, 3, 2, 2, 2, 2, 129, 3, 2, 2, 2,
|
||||
2, 131, 3, 2, 2, 2, 2, 133, 3, 2, 2, 2, 2, 135, 3, 2, 2, 2, 2, 137, 3,
|
||||
2, 2, 2, 2, 139, 3, 2, 2, 2, 2, 141, 3, 2, 2, 2, 2, 143, 3, 2, 2, 2, 2,
|
||||
145, 3, 2, 2, 2, 2, 147, 3, 2, 2, 2, 2, 149, 3, 2, 2, 2, 2, 151, 3, 2,
|
||||
2, 2, 2, 153, 3, 2, 2, 2, 2, 155, 3, 2, 2, 2, 2, 213, 3, 2, 2, 2, 3, 215,
|
||||
3, 2, 2, 2, 5, 217, 3, 2, 2, 2, 7, 220, 3, 2, 2, 2, 9, 223, 3, 2, 2, 2,
|
||||
11, 226, 3, 2, 2, 2, 13, 229, 3, 2, 2, 2, 15, 231, 3, 2, 2, 2, 17, 233,
|
||||
3, 2, 2, 2, 19, 236, 3, 2, 2, 2, 21, 239, 3, 2, 2, 2, 23, 241, 3, 2, 2,
|
||||
2, 25, 243, 3, 2, 2, 2, 27, 245, 3, 2, 2, 2, 29, 247, 3, 2, 2, 2, 31, 249,
|
||||
3, 2, 2, 2, 33, 251, 3, 2, 2, 2, 35, 253, 3, 2, 2, 2, 37, 256, 3, 2, 2,
|
||||
2, 39, 259, 3, 2, 2, 2, 41, 261, 3, 2, 2, 2, 43, 263, 3, 2, 2, 2, 45, 265,
|
||||
3, 2, 2, 2, 47, 267, 3, 2, 2, 2, 49, 269, 3, 2, 2, 2, 51, 271, 3, 2, 2,
|
||||
2, 53, 273, 3, 2, 2, 2, 55, 283, 3, 2, 2, 2, 57, 293, 3, 2, 2, 2, 59, 295,
|
||||
3, 2, 2, 2, 61, 299, 3, 2, 2, 2, 63, 303, 3, 2, 2, 2, 65, 311, 3, 2, 2,
|
||||
2, 67, 316, 3, 2, 2, 2, 69, 325, 3, 2, 2, 2, 71, 331, 3, 2, 2, 2, 73, 338,
|
||||
3, 2, 2, 2, 75, 342, 3, 2, 2, 2, 77, 348, 3, 2, 2, 2, 79, 351, 3, 2, 2,
|
||||
2, 81, 359, 3, 2, 2, 2, 83, 366, 3, 2, 2, 2, 85, 371, 3, 2, 2, 2, 87, 388,
|
||||
3, 2, 2, 2, 89, 392, 3, 2, 2, 2, 91, 397, 3, 2, 2, 2, 93, 403, 3, 2, 2,
|
||||
2, 95, 413, 3, 2, 2, 2, 97, 415, 3, 2, 2, 2, 99, 425, 3, 2, 2, 2, 101,
|
||||
427, 3, 2, 2, 2, 103, 436, 3, 2, 2, 2, 105, 443, 3, 2, 2, 2, 107, 451,
|
||||
3, 2, 2, 2, 109, 458, 3, 2, 2, 2, 111, 472, 3, 2, 2, 2, 113, 477, 3, 2,
|
||||
2, 2, 115, 482, 3, 2, 2, 2, 117, 489, 3, 2, 2, 2, 119, 496, 3, 2, 2, 2,
|
||||
121, 501, 3, 2, 2, 2, 123, 506, 3, 2, 2, 2, 125, 512, 3, 2, 2, 2, 127,
|
||||
520, 3, 2, 2, 2, 129, 526, 3, 2, 2, 2, 131, 533, 3, 2, 2, 2, 133, 536,
|
||||
3, 2, 2, 2, 135, 544, 3, 2, 2, 2, 137, 548, 3, 2, 2, 2, 139, 552, 3, 2,
|
||||
2, 2, 141, 583, 3, 2, 2, 2, 143, 593, 3, 2, 2, 2, 145, 612, 3, 2, 2, 2,
|
||||
147, 639, 3, 2, 2, 2, 149, 641, 3, 2, 2, 2, 151, 659, 3, 2, 2, 2, 153,
|
||||
673, 3, 2, 2, 2, 155, 677, 3, 2, 2, 2, 157, 679, 3, 2, 2, 2, 159, 681,
|
||||
3, 2, 2, 2, 161, 683, 3, 2, 2, 2, 163, 685, 3, 2, 2, 2, 165, 687, 3, 2,
|
||||
2, 2, 167, 689, 3, 2, 2, 2, 169, 691, 3, 2, 2, 2, 171, 693, 3, 2, 2, 2,
|
||||
173, 695, 3, 2, 2, 2, 175, 697, 3, 2, 2, 2, 177, 699, 3, 2, 2, 2, 179,
|
||||
701, 3, 2, 2, 2, 181, 703, 3, 2, 2, 2, 183, 705, 3, 2, 2, 2, 185, 707,
|
||||
3, 2, 2, 2, 187, 709, 3, 2, 2, 2, 189, 711, 3, 2, 2, 2, 191, 713, 3, 2,
|
||||
2, 2, 193, 715, 3, 2, 2, 2, 195, 717, 3, 2, 2, 2, 197, 719, 3, 2, 2, 2,
|
||||
199, 721, 3, 2, 2, 2, 201, 723, 3, 2, 2, 2, 203, 725, 3, 2, 2, 2, 205,
|
||||
727, 3, 2, 2, 2, 207, 729, 3, 2, 2, 2, 209, 731, 3, 2, 2, 2, 211, 733,
|
||||
3, 2, 2, 2, 213, 735, 3, 2, 2, 2, 215, 216, 7, 48, 2, 2, 216, 4, 3, 2,
|
||||
2, 2, 217, 218, 7, 63, 2, 2, 218, 219, 7, 128, 2, 2, 219, 6, 3, 2, 2, 2,
|
||||
220, 221, 7, 35, 2, 2, 221, 222, 7, 128, 2, 2, 222, 8, 3, 2, 2, 2, 223,
|
||||
224, 7, 63, 2, 2, 224, 225, 7, 63, 2, 2, 225, 10, 3, 2, 2, 2, 226, 227,
|
||||
7, 35, 2, 2, 227, 228, 7, 63, 2, 2, 228, 12, 3, 2, 2, 2, 229, 230, 7, 62,
|
||||
2, 2, 230, 14, 3, 2, 2, 2, 231, 232, 7, 64, 2, 2, 232, 16, 3, 2, 2, 2,
|
||||
233, 234, 7, 62, 2, 2, 234, 235, 7, 63, 2, 2, 235, 18, 3, 2, 2, 2, 236,
|
||||
237, 7, 64, 2, 2, 237, 238, 7, 63, 2, 2, 238, 20, 3, 2, 2, 2, 239, 240,
|
||||
7, 45, 2, 2, 240, 22, 3, 2, 2, 2, 241, 242, 7, 47, 2, 2, 242, 24, 3, 2,
|
||||
2, 2, 243, 244, 7, 44, 2, 2, 244, 26, 3, 2, 2, 2, 245, 246, 7, 49, 2, 2,
|
||||
246, 28, 3, 2, 2, 2, 247, 248, 7, 39, 2, 2, 248, 30, 3, 2, 2, 2, 249, 250,
|
||||
7, 65, 2, 2, 250, 32, 3, 2, 2, 2, 251, 252, 7, 60, 2, 2, 252, 34, 3, 2,
|
||||
2, 2, 253, 254, 7, 60, 2, 2, 254, 255, 7, 60, 2, 2, 255, 36, 3, 2, 2, 2,
|
||||
256, 257, 7, 48, 2, 2, 257, 258, 7, 48, 2, 2, 258, 38, 3, 2, 2, 2, 259,
|
||||
260, 7, 46, 2, 2, 260, 40, 3, 2, 2, 2, 261, 262, 7, 42, 2, 2, 262, 42,
|
||||
3, 2, 2, 2, 263, 264, 7, 43, 2, 2, 264, 44, 3, 2, 2, 2, 265, 266, 7, 125,
|
||||
2, 2, 266, 46, 3, 2, 2, 2, 267, 268, 7, 127, 2, 2, 268, 48, 3, 2, 2, 2,
|
||||
269, 270, 7, 93, 2, 2, 270, 50, 3, 2, 2, 2, 271, 272, 7, 95, 2, 2, 272,
|
||||
52, 3, 2, 2, 2, 273, 274, 5, 161, 81, 2, 274, 275, 5, 173, 87, 2, 275,
|
||||
276, 5, 173, 87, 2, 276, 277, 5, 195, 98, 2, 277, 278, 5, 169, 85, 2, 278,
|
||||
279, 5, 173, 87, 2, 279, 280, 5, 161, 81, 2, 280, 281, 5, 199, 100, 2,
|
||||
281, 282, 5, 169, 85, 2, 282, 54, 3, 2, 2, 2, 283, 284, 5, 161, 81, 2,
|
||||
284, 285, 5, 183, 92, 2, 285, 286, 5, 183, 92, 2, 286, 56, 3, 2, 2, 2,
|
||||
287, 288, 5, 161, 81, 2, 288, 289, 5, 187, 94, 2, 289, 290, 5, 167, 84,
|
||||
2, 290, 294, 3, 2, 2, 2, 291, 292, 7, 40, 2, 2, 292, 294, 7, 40, 2, 2,
|
||||
293, 287, 3, 2, 2, 2, 293, 291, 3, 2, 2, 2, 294, 58, 3, 2, 2, 2, 295, 296,
|
||||
5, 161, 81, 2, 296, 297, 5, 187, 94, 2, 297, 298, 5, 209, 105, 2, 298,
|
||||
60, 3, 2, 2, 2, 299, 300, 5, 161, 81, 2, 300, 301, 5, 197, 99, 2, 301,
|
||||
302, 5, 165, 83, 2, 302, 62, 3, 2, 2, 2, 303, 304, 5, 165, 83, 2, 304,
|
||||
305, 5, 189, 95, 2, 305, 306, 5, 183, 92, 2, 306, 307, 5, 183, 92, 2, 307,
|
||||
308, 5, 169, 85, 2, 308, 309, 5, 165, 83, 2, 309, 310, 5, 199, 100, 2,
|
||||
310, 64, 3, 2, 2, 2, 311, 312, 5, 167, 84, 2, 312, 313, 5, 169, 85, 2,
|
||||
313, 314, 5, 197, 99, 2, 314, 315, 5, 165, 83, 2, 315, 66, 3, 2, 2, 2,
|
||||
316, 317, 5, 167, 84, 2, 317, 318, 5, 177, 89, 2, 318, 319, 5, 197, 99,
|
||||
2, 319, 320, 5, 199, 100, 2, 320, 321, 5, 177, 89, 2, 321, 322, 5, 187,
|
||||
94, 2, 322, 323, 5, 165, 83, 2, 323, 324, 5, 199, 100, 2, 324, 68, 3, 2,
|
||||
2, 2, 325, 326, 5, 171, 86, 2, 326, 327, 5, 161, 81, 2, 327, 328, 5, 183,
|
||||
92, 2, 328, 329, 5, 197, 99, 2, 329, 330, 5, 169, 85, 2, 330, 70, 3, 2,
|
||||
2, 2, 331, 332, 5, 171, 86, 2, 332, 333, 5, 177, 89, 2, 333, 334, 5, 183,
|
||||
92, 2, 334, 335, 5, 199, 100, 2, 335, 336, 5, 169, 85, 2, 336, 337, 5,
|
||||
195, 98, 2, 337, 72, 3, 2, 2, 2, 338, 339, 5, 171, 86, 2, 339, 340, 5,
|
||||
189, 95, 2, 340, 341, 5, 195, 98, 2, 341, 74, 3, 2, 2, 2, 342, 343, 5,
|
||||
173, 87, 2, 343, 344, 5, 195, 98, 2, 344, 345, 5, 161, 81, 2, 345, 346,
|
||||
5, 191, 96, 2, 346, 347, 5, 175, 88, 2, 347, 76, 3, 2, 2, 2, 348, 349,
|
||||
5, 177, 89, 2, 349, 350, 5, 187, 94, 2, 350, 78, 3, 2, 2, 2, 351, 352,
|
||||
5, 177, 89, 2, 352, 353, 5, 187, 94, 2, 353, 354, 5, 163, 82, 2, 354, 355,
|
||||
5, 189, 95, 2, 355, 356, 5, 201, 101, 2, 356, 357, 5, 187, 94, 2, 357,
|
||||
358, 5, 167, 84, 2, 358, 80, 3, 2, 2, 2, 359, 360, 5, 177, 89, 2, 360,
|
||||
361, 5, 187, 94, 2, 361, 362, 5, 197, 99, 2, 362, 363, 5, 169, 85, 2, 363,
|
||||
364, 5, 195, 98, 2, 364, 365, 5, 199, 100, 2, 365, 82, 3, 2, 2, 2, 366,
|
||||
367, 5, 177, 89, 2, 367, 368, 5, 187, 94, 2, 368, 369, 5, 199, 100, 2,
|
||||
369, 370, 5, 189, 95, 2, 370, 84, 3, 2, 2, 2, 371, 372, 5, 181, 91, 2,
|
||||
372, 373, 7, 97, 2, 2, 373, 374, 5, 197, 99, 2, 374, 375, 5, 175, 88, 2,
|
||||
375, 376, 5, 189, 95, 2, 376, 377, 5, 195, 98, 2, 377, 378, 5, 199, 100,
|
||||
2, 378, 379, 5, 169, 85, 2, 379, 380, 5, 197, 99, 2, 380, 381, 5, 199,
|
||||
100, 2, 381, 382, 7, 97, 2, 2, 382, 383, 5, 191, 96, 2, 383, 384, 5, 161,
|
||||
81, 2, 384, 385, 5, 199, 100, 2, 385, 386, 5, 175, 88, 2, 386, 387, 5,
|
||||
197, 99, 2, 387, 86, 3, 2, 2, 2, 388, 389, 5, 183, 92, 2, 389, 390, 5,
|
||||
169, 85, 2, 390, 391, 5, 199, 100, 2, 391, 88, 3, 2, 2, 2, 392, 393, 5,
|
||||
183, 92, 2, 393, 394, 5, 177, 89, 2, 394, 395, 5, 181, 91, 2, 395, 396,
|
||||
5, 169, 85, 2, 396, 90, 3, 2, 2, 2, 397, 398, 5, 183, 92, 2, 398, 399,
|
||||
5, 177, 89, 2, 399, 400, 5, 185, 93, 2, 400, 401, 5, 177, 89, 2, 401, 402,
|
||||
5, 199, 100, 2, 402, 92, 3, 2, 2, 2, 403, 404, 5, 187, 94, 2, 404, 405,
|
||||
5, 189, 95, 2, 405, 406, 5, 187, 94, 2, 406, 407, 5, 169, 85, 2, 407, 94,
|
||||
3, 2, 2, 2, 408, 409, 5, 187, 94, 2, 409, 410, 5, 189, 95, 2, 410, 411,
|
||||
5, 199, 100, 2, 411, 414, 3, 2, 2, 2, 412, 414, 7, 35, 2, 2, 413, 408,
|
||||
3, 2, 2, 2, 413, 412, 3, 2, 2, 2, 414, 96, 3, 2, 2, 2, 415, 416, 5, 187,
|
||||
94, 2, 416, 417, 5, 201, 101, 2, 417, 418, 5, 183, 92, 2, 418, 419, 5,
|
||||
183, 92, 2, 419, 98, 3, 2, 2, 2, 420, 421, 5, 189, 95, 2, 421, 422, 5,
|
||||
195, 98, 2, 422, 426, 3, 2, 2, 2, 423, 424, 7, 126, 2, 2, 424, 426, 7,
|
||||
126, 2, 2, 425, 420, 3, 2, 2, 2, 425, 423, 3, 2, 2, 2, 426, 100, 3, 2,
|
||||
2, 2, 427, 428, 5, 189, 95, 2, 428, 429, 5, 201, 101, 2, 429, 430, 5, 199,
|
||||
100, 2, 430, 431, 5, 163, 82, 2, 431, 432, 5, 189, 95, 2, 432, 433, 5,
|
||||
201, 101, 2, 433, 434, 5, 187, 94, 2, 434, 435, 5, 167, 84, 2, 435, 102,
|
||||
3, 2, 2, 2, 436, 437, 5, 195, 98, 2, 437, 438, 5, 169, 85, 2, 438, 439,
|
||||
5, 185, 93, 2, 439, 440, 5, 189, 95, 2, 440, 441, 5, 203, 102, 2, 441,
|
||||
442, 5, 169, 85, 2, 442, 104, 3, 2, 2, 2, 443, 444, 5, 195, 98, 2, 444,
|
||||
445, 5, 169, 85, 2, 445, 446, 5, 191, 96, 2, 446, 447, 5, 183, 92, 2, 447,
|
||||
448, 5, 161, 81, 2, 448, 449, 5, 165, 83, 2, 449, 450, 5, 169, 85, 2, 450,
|
||||
106, 3, 2, 2, 2, 451, 452, 5, 195, 98, 2, 452, 453, 5, 169, 85, 2, 453,
|
||||
454, 5, 199, 100, 2, 454, 455, 5, 201, 101, 2, 455, 456, 5, 195, 98, 2,
|
||||
456, 457, 5, 187, 94, 2, 457, 108, 3, 2, 2, 2, 458, 459, 5, 197, 99, 2,
|
||||
459, 460, 5, 175, 88, 2, 460, 461, 5, 189, 95, 2, 461, 462, 5, 195, 98,
|
||||
2, 462, 463, 5, 199, 100, 2, 463, 464, 5, 169, 85, 2, 464, 465, 5, 197,
|
||||
99, 2, 465, 466, 5, 199, 100, 2, 466, 467, 7, 97, 2, 2, 467, 468, 5, 191,
|
||||
96, 2, 468, 469, 5, 161, 81, 2, 469, 470, 5, 199, 100, 2, 470, 471, 5,
|
||||
175, 88, 2, 471, 110, 3, 2, 2, 2, 472, 473, 5, 197, 99, 2, 473, 474, 5,
|
||||
189, 95, 2, 474, 475, 5, 195, 98, 2, 475, 476, 5, 199, 100, 2, 476, 112,
|
||||
3, 2, 2, 2, 477, 478, 5, 199, 100, 2, 478, 479, 5, 195, 98, 2, 479, 480,
|
||||
5, 201, 101, 2, 480, 481, 5, 169, 85, 2, 481, 114, 3, 2, 2, 2, 482, 483,
|
||||
5, 201, 101, 2, 483, 484, 5, 191, 96, 2, 484, 485, 5, 167, 84, 2, 485,
|
||||
486, 5, 161, 81, 2, 486, 487, 5, 199, 100, 2, 487, 488, 5, 169, 85, 2,
|
||||
488, 116, 3, 2, 2, 2, 489, 490, 5, 201, 101, 2, 490, 491, 5, 191, 96, 2,
|
||||
491, 492, 5, 197, 99, 2, 492, 493, 5, 169, 85, 2, 493, 494, 5, 195, 98,
|
||||
2, 494, 495, 5, 199, 100, 2, 495, 118, 3, 2, 2, 2, 496, 497, 5, 205, 103,
|
||||
2, 497, 498, 5, 177, 89, 2, 498, 499, 5, 199, 100, 2, 499, 500, 5, 175,
|
||||
88, 2, 500, 120, 3, 2, 2, 2, 501, 502, 5, 181, 91, 2, 502, 503, 5, 169,
|
||||
85, 2, 503, 504, 5, 169, 85, 2, 504, 505, 5, 191, 96, 2, 505, 122, 3, 2,
|
||||
2, 2, 506, 507, 5, 165, 83, 2, 507, 508, 5, 189, 95, 2, 508, 509, 5, 201,
|
||||
101, 2, 509, 510, 5, 187, 94, 2, 510, 511, 5, 199, 100, 2, 511, 124, 3,
|
||||
2, 2, 2, 512, 513, 5, 189, 95, 2, 513, 514, 5, 191, 96, 2, 514, 515, 5,
|
||||
199, 100, 2, 515, 516, 5, 177, 89, 2, 516, 517, 5, 189, 95, 2, 517, 518,
|
||||
5, 187, 94, 2, 518, 519, 5, 197, 99, 2, 519, 126, 3, 2, 2, 2, 520, 521,
|
||||
5, 191, 96, 2, 521, 522, 5, 195, 98, 2, 522, 523, 5, 201, 101, 2, 523,
|
||||
524, 5, 187, 94, 2, 524, 525, 5, 169, 85, 2, 525, 128, 3, 2, 2, 2, 526,
|
||||
527, 5, 197, 99, 2, 527, 528, 5, 169, 85, 2, 528, 529, 5, 161, 81, 2, 529,
|
||||
530, 5, 195, 98, 2, 530, 531, 5, 165, 83, 2, 531, 532, 5, 175, 88, 2, 532,
|
||||
130, 3, 2, 2, 2, 533, 534, 5, 199, 100, 2, 534, 535, 5, 189, 95, 2, 535,
|
||||
132, 3, 2, 2, 2, 536, 537, 5, 165, 83, 2, 537, 538, 5, 201, 101, 2, 538,
|
||||
539, 5, 195, 98, 2, 539, 540, 5, 195, 98, 2, 540, 541, 5, 169, 85, 2, 541,
|
||||
542, 5, 187, 94, 2, 542, 543, 5, 199, 100, 2, 543, 134, 3, 2, 2, 2, 544,
|
||||
545, 5, 187, 94, 2, 545, 546, 5, 169, 85, 2, 546, 547, 5, 205, 103, 2,
|
||||
547, 136, 3, 2, 2, 2, 548, 549, 5, 189, 95, 2, 549, 550, 5, 183, 92, 2,
|
||||
550, 551, 5, 167, 84, 2, 551, 138, 3, 2, 2, 2, 552, 556, 9, 2, 2, 2, 553,
|
||||
555, 9, 3, 2, 2, 554, 553, 3, 2, 2, 2, 555, 558, 3, 2, 2, 2, 556, 554,
|
||||
3, 2, 2, 2, 556, 557, 3, 2, 2, 2, 557, 140, 3, 2, 2, 2, 558, 556, 3, 2,
|
||||
2, 2, 559, 563, 9, 4, 2, 2, 560, 562, 5, 159, 80, 2, 561, 560, 3, 2, 2,
|
||||
2, 562, 565, 3, 2, 2, 2, 563, 561, 3, 2, 2, 2, 563, 564, 3, 2, 2, 2, 564,
|
||||
584, 3, 2, 2, 2, 565, 563, 3, 2, 2, 2, 566, 584, 7, 50, 2, 2, 567, 568,
|
||||
7, 50, 2, 2, 568, 569, 7, 122, 2, 2, 569, 571, 3, 2, 2, 2, 570, 572, 5,
|
||||
157, 79, 2, 571, 570, 3, 2, 2, 2, 572, 573, 3, 2, 2, 2, 573, 571, 3, 2,
|
||||
2, 2, 573, 574, 3, 2, 2, 2, 574, 584, 3, 2, 2, 2, 575, 576, 7, 50, 2, 2,
|
||||
576, 577, 7, 100, 2, 2, 577, 579, 3, 2, 2, 2, 578, 580, 9, 5, 2, 2, 579,
|
||||
578, 3, 2, 2, 2, 580, 581, 3, 2, 2, 2, 581, 579, 3, 2, 2, 2, 581, 582,
|
||||
3, 2, 2, 2, 582, 584, 3, 2, 2, 2, 583, 559, 3, 2, 2, 2, 583, 566, 3, 2,
|
||||
2, 2, 583, 567, 3, 2, 2, 2, 583, 575, 3, 2, 2, 2, 584, 142, 3, 2, 2, 2,
|
||||
585, 589, 9, 4, 2, 2, 586, 588, 5, 159, 80, 2, 587, 586, 3, 2, 2, 2, 588,
|
||||
591, 3, 2, 2, 2, 589, 587, 3, 2, 2, 2, 589, 590, 3, 2, 2, 2, 590, 594,
|
||||
3, 2, 2, 2, 591, 589, 3, 2, 2, 2, 592, 594, 7, 50, 2, 2, 593, 585, 3, 2,
|
||||
2, 2, 593, 592, 3, 2, 2, 2, 593, 594, 3, 2, 2, 2, 594, 595, 3, 2, 2, 2,
|
||||
595, 597, 7, 48, 2, 2, 596, 598, 5, 159, 80, 2, 597, 596, 3, 2, 2, 2, 598,
|
||||
599, 3, 2, 2, 2, 599, 597, 3, 2, 2, 2, 599, 600, 3, 2, 2, 2, 600, 610,
|
||||
3, 2, 2, 2, 601, 603, 5, 169, 85, 2, 602, 604, 9, 6, 2, 2, 603, 602, 3,
|
||||
2, 2, 2, 603, 604, 3, 2, 2, 2, 604, 606, 3, 2, 2, 2, 605, 607, 5, 159,
|
||||
80, 2, 606, 605, 3, 2, 2, 2, 607, 608, 3, 2, 2, 2, 608, 606, 3, 2, 2, 2,
|
||||
608, 609, 3, 2, 2, 2, 609, 611, 3, 2, 2, 2, 610, 601, 3, 2, 2, 2, 610,
|
||||
611, 3, 2, 2, 2, 611, 144, 3, 2, 2, 2, 612, 613, 7, 66, 2, 2, 613, 614,
|
||||
5, 139, 70, 2, 614, 146, 3, 2, 2, 2, 615, 623, 7, 41, 2, 2, 616, 617, 7,
|
||||
94, 2, 2, 617, 622, 11, 2, 2, 2, 618, 619, 7, 41, 2, 2, 619, 622, 7, 41,
|
||||
2, 2, 620, 622, 10, 7, 2, 2, 621, 616, 3, 2, 2, 2, 621, 618, 3, 2, 2, 2,
|
||||
621, 620, 3, 2, 2, 2, 622, 625, 3, 2, 2, 2, 623, 621, 3, 2, 2, 2, 623,
|
||||
624, 3, 2, 2, 2, 624, 626, 3, 2, 2, 2, 625, 623, 3, 2, 2, 2, 626, 640,
|
||||
7, 41, 2, 2, 627, 635, 7, 36, 2, 2, 628, 629, 7, 94, 2, 2, 629, 634, 11,
|
||||
2, 2, 2, 630, 631, 7, 36, 2, 2, 631, 634, 7, 36, 2, 2, 632, 634, 10, 8,
|
||||
2, 2, 633, 628, 3, 2, 2, 2, 633, 630, 3, 2, 2, 2, 633, 632, 3, 2, 2, 2,
|
||||
634, 637, 3, 2, 2, 2, 635, 633, 3, 2, 2, 2, 635, 636, 3, 2, 2, 2, 636,
|
||||
638, 3, 2, 2, 2, 637, 635, 3, 2, 2, 2, 638, 640, 7, 36, 2, 2, 639, 615,
|
||||
3, 2, 2, 2, 639, 627, 3, 2, 2, 2, 640, 148, 3, 2, 2, 2, 641, 642, 7, 49,
|
||||
2, 2, 642, 643, 7, 49, 2, 2, 643, 647, 3, 2, 2, 2, 644, 646, 10, 9, 2,
|
||||
2, 645, 644, 3, 2, 2, 2, 646, 649, 3, 2, 2, 2, 647, 645, 3, 2, 2, 2, 647,
|
||||
648, 3, 2, 2, 2, 648, 655, 3, 2, 2, 2, 649, 647, 3, 2, 2, 2, 650, 652,
|
||||
7, 15, 2, 2, 651, 650, 3, 2, 2, 2, 651, 652, 3, 2, 2, 2, 652, 653, 3, 2,
|
||||
2, 2, 653, 656, 7, 12, 2, 2, 654, 656, 7, 2, 2, 3, 655, 651, 3, 2, 2, 2,
|
||||
655, 654, 3, 2, 2, 2, 656, 657, 3, 2, 2, 2, 657, 658, 8, 75, 2, 2, 658,
|
||||
150, 3, 2, 2, 2, 659, 660, 7, 49, 2, 2, 660, 661, 7, 44, 2, 2, 661, 665,
|
||||
3, 2, 2, 2, 662, 664, 11, 2, 2, 2, 663, 662, 3, 2, 2, 2, 664, 667, 3, 2,
|
||||
2, 2, 665, 666, 3, 2, 2, 2, 665, 663, 3, 2, 2, 2, 666, 668, 3, 2, 2, 2,
|
||||
667, 665, 3, 2, 2, 2, 668, 669, 7, 44, 2, 2, 669, 670, 7, 49, 2, 2, 670,
|
||||
671, 3, 2, 2, 2, 671, 672, 8, 76, 2, 2, 672, 152, 3, 2, 2, 2, 673, 674,
|
||||
9, 10, 2, 2, 674, 675, 3, 2, 2, 2, 675, 676, 8, 77, 2, 2, 676, 154, 3,
|
||||
2, 2, 2, 677, 678, 11, 2, 2, 2, 678, 156, 3, 2, 2, 2, 679, 680, 9, 11,
|
||||
2, 2, 680, 158, 3, 2, 2, 2, 681, 682, 9, 12, 2, 2, 682, 160, 3, 2, 2, 2,
|
||||
683, 684, 9, 13, 2, 2, 684, 162, 3, 2, 2, 2, 685, 686, 9, 14, 2, 2, 686,
|
||||
164, 3, 2, 2, 2, 687, 688, 9, 15, 2, 2, 688, 166, 3, 2, 2, 2, 689, 690,
|
||||
9, 16, 2, 2, 690, 168, 3, 2, 2, 2, 691, 692, 9, 17, 2, 2, 692, 170, 3,
|
||||
2, 2, 2, 693, 694, 9, 18, 2, 2, 694, 172, 3, 2, 2, 2, 695, 696, 9, 19,
|
||||
2, 2, 696, 174, 3, 2, 2, 2, 697, 698, 9, 20, 2, 2, 698, 176, 3, 2, 2, 2,
|
||||
699, 700, 9, 21, 2, 2, 700, 178, 3, 2, 2, 2, 701, 702, 9, 22, 2, 2, 702,
|
||||
180, 3, 2, 2, 2, 703, 704, 9, 23, 2, 2, 704, 182, 3, 2, 2, 2, 705, 706,
|
||||
9, 24, 2, 2, 706, 184, 3, 2, 2, 2, 707, 708, 9, 25, 2, 2, 708, 186, 3,
|
||||
2, 2, 2, 709, 710, 9, 26, 2, 2, 710, 188, 3, 2, 2, 2, 711, 712, 9, 27,
|
||||
2, 2, 712, 190, 3, 2, 2, 2, 713, 714, 9, 28, 2, 2, 714, 192, 3, 2, 2, 2,
|
||||
715, 716, 9, 29, 2, 2, 716, 194, 3, 2, 2, 2, 717, 718, 9, 30, 2, 2, 718,
|
||||
196, 3, 2, 2, 2, 719, 720, 9, 31, 2, 2, 720, 198, 3, 2, 2, 2, 721, 722,
|
||||
9, 32, 2, 2, 722, 200, 3, 2, 2, 2, 723, 724, 9, 33, 2, 2, 724, 202, 3,
|
||||
2, 2, 2, 725, 726, 9, 34, 2, 2, 726, 204, 3, 2, 2, 2, 727, 728, 9, 35,
|
||||
2, 2, 728, 206, 3, 2, 2, 2, 729, 730, 9, 36, 2, 2, 730, 208, 3, 2, 2, 2,
|
||||
731, 732, 9, 37, 2, 2, 732, 210, 3, 2, 2, 2, 733, 734, 9, 38, 2, 2, 734,
|
||||
212, 3, 2, 2, 2, 735, 736, 11, 2, 2, 2, 736, 737, 3, 2, 2, 2, 737, 738,
|
||||
8, 107, 3, 2, 738, 214, 3, 2, 2, 2, 26, 2, 293, 413, 425, 556, 563, 573,
|
||||
581, 583, 589, 593, 599, 603, 608, 610, 621, 623, 633, 635, 639, 647, 651,
|
||||
655, 665, 4, 2, 3, 2, 2, 4, 2,
|
||||
}
|
||||
|
||||
var lexerChannelNames = []string{
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN", "ERRORCHANNEL",
|
||||
}
|
||||
|
||||
var lexerModeNames = []string{
|
||||
"DEFAULT_MODE",
|
||||
}
|
||||
|
||||
var lexerLiteralNames = []string{
|
||||
"", "'.'", "'=~'", "'!~'", "'=='", "'!='", "'<'", "'>'", "'<='", "'>='",
|
||||
"'+'", "'-'", "'*'", "'/'", "'%'", "'?'", "':'", "'::'", "'..'", "','",
|
||||
"'('", "')'", "'{'", "'}'", "'['", "']'",
|
||||
}
|
||||
|
||||
var lexerSymbolicNames = []string{
|
||||
"", "DOT", "T_REGEX_MATCH", "T_REGEX_NON_MATCH", "T_EQ", "T_NE", "T_LT",
|
||||
"T_GT", "T_LE", "T_GE", "T_PLUS", "T_MINUS", "T_TIMES", "T_DIV", "T_MOD",
|
||||
"T_QUESTION", "T_COLON", "T_SCOPE", "T_RANGE", "T_COMMA", "T_OPEN", "T_CLOSE",
|
||||
"T_OBJECT_OPEN", "T_OBJECT_CLOSE", "T_ARRAY_OPEN", "T_ARRAY_CLOSE", "T_AGGREGATE",
|
||||
"T_ALL", "T_AND", "T_ANY", "T_ASC", "T_COLLECT", "T_DESC", "T_DISTINCT",
|
||||
"T_FALSE", "T_FILTER", "T_FOR", "T_GRAPH", "T_IN", "T_INBOUND", "T_INSERT",
|
||||
"T_INTO", "T_K_SHORTEST_PATHS", "T_LET", "T_LIKE", "T_LIMIT", "T_NONE",
|
||||
"T_NOT", "T_NULL", "T_OR", "T_OUTBOUND", "T_REMOVE", "T_REPLACE", "T_RETURN",
|
||||
"T_SHORTEST_PATH", "T_SORT", "T_TRUE", "T_UPDATE", "T_UPSERT", "T_WITH",
|
||||
"T_KEEP", "T_COUNT", "T_OPTIONS", "T_PRUNE", "T_SEARCH", "T_TO", "T_CURRENT",
|
||||
"T_NEW", "T_OLD", "T_STRING", "T_INT", "T_FLOAT", "T_PARAMETER", "T_QUOTED_STRING",
|
||||
"SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES", "UNEXPECTED_CHAR",
|
||||
"ERROR_RECONGNIGION",
|
||||
}
|
||||
|
||||
var lexerRuleNames = []string{
|
||||
"DOT", "T_REGEX_MATCH", "T_REGEX_NON_MATCH", "T_EQ", "T_NE", "T_LT", "T_GT",
|
||||
"T_LE", "T_GE", "T_PLUS", "T_MINUS", "T_TIMES", "T_DIV", "T_MOD", "T_QUESTION",
|
||||
"T_COLON", "T_SCOPE", "T_RANGE", "T_COMMA", "T_OPEN", "T_CLOSE", "T_OBJECT_OPEN",
|
||||
"T_OBJECT_CLOSE", "T_ARRAY_OPEN", "T_ARRAY_CLOSE", "T_AGGREGATE", "T_ALL",
|
||||
"T_AND", "T_ANY", "T_ASC", "T_COLLECT", "T_DESC", "T_DISTINCT", "T_FALSE",
|
||||
"T_FILTER", "T_FOR", "T_GRAPH", "T_IN", "T_INBOUND", "T_INSERT", "T_INTO",
|
||||
"T_K_SHORTEST_PATHS", "T_LET", "T_LIKE", "T_LIMIT", "T_NONE", "T_NOT",
|
||||
"T_NULL", "T_OR", "T_OUTBOUND", "T_REMOVE", "T_REPLACE", "T_RETURN", "T_SHORTEST_PATH",
|
||||
"T_SORT", "T_TRUE", "T_UPDATE", "T_UPSERT", "T_WITH", "T_KEEP", "T_COUNT",
|
||||
"T_OPTIONS", "T_PRUNE", "T_SEARCH", "T_TO", "T_CURRENT", "T_NEW", "T_OLD",
|
||||
"T_STRING", "T_INT", "T_FLOAT", "T_PARAMETER", "T_QUOTED_STRING", "SINGLE_LINE_COMMENT",
|
||||
"MULTILINE_COMMENT", "SPACES", "UNEXPECTED_CHAR", "HEX_DIGIT", "DIGIT",
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
|
||||
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "ERROR_RECONGNIGION",
|
||||
}
|
||||
|
||||
type CAQLLexer struct {
|
||||
*antlr.BaseLexer
|
||||
channelNames []string
|
||||
@@ -403,28 +22,427 @@ type CAQLLexer struct {
|
||||
// TODO: EOF string
|
||||
}
|
||||
|
||||
// NewCAQLLexer produces a new lexer instance for the optional input antlr.CharStream.
|
||||
//
|
||||
// The *CAQLLexer instance produced may be reused by calling the SetInputStream method.
|
||||
// The initial lexer configuration is expensive to construct, and the object is not thread-safe;
|
||||
// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
|
||||
// objects can be used in a thread-safe manner.
|
||||
func NewCAQLLexer(input antlr.CharStream) *CAQLLexer {
|
||||
l := new(CAQLLexer)
|
||||
lexerDeserializer := antlr.NewATNDeserializer(nil)
|
||||
lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
|
||||
lexerDecisionToDFA := make([]*antlr.DFA, len(lexerAtn.DecisionToState))
|
||||
for index, ds := range lexerAtn.DecisionToState {
|
||||
lexerDecisionToDFA[index] = antlr.NewDFA(ds, index)
|
||||
}
|
||||
l.BaseLexer = antlr.NewBaseLexer(input)
|
||||
l.Interpreter = antlr.NewLexerATNSimulator(l, lexerAtn, lexerDecisionToDFA, antlr.NewPredictionContextCache())
|
||||
var caqllexerLexerStaticData struct {
|
||||
once sync.Once
|
||||
serializedATN []int32
|
||||
channelNames []string
|
||||
modeNames []string
|
||||
literalNames []string
|
||||
symbolicNames []string
|
||||
ruleNames []string
|
||||
predictionContextCache *antlr.PredictionContextCache
|
||||
atn *antlr.ATN
|
||||
decisionToDFA []*antlr.DFA
|
||||
}
|
||||
|
||||
l.channelNames = lexerChannelNames
|
||||
l.modeNames = lexerModeNames
|
||||
l.RuleNames = lexerRuleNames
|
||||
l.LiteralNames = lexerLiteralNames
|
||||
l.SymbolicNames = lexerSymbolicNames
|
||||
func caqllexerLexerInit() {
|
||||
staticData := &caqllexerLexerStaticData
|
||||
staticData.channelNames = []string{
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN", "ERRORCHANNEL",
|
||||
}
|
||||
staticData.modeNames = []string{
|
||||
"DEFAULT_MODE",
|
||||
}
|
||||
staticData.literalNames = []string{
|
||||
"", "'.'", "'=~'", "'!~'", "'=='", "'!='", "'<'", "'>'", "'<='", "'>='",
|
||||
"'+'", "'-'", "'*'", "'/'", "'%'", "'?'", "':'", "'::'", "'..'", "','",
|
||||
"'('", "')'", "'{'", "'}'", "'['", "']'",
|
||||
}
|
||||
staticData.symbolicNames = []string{
|
||||
"", "DOT", "T_REGEX_MATCH", "T_REGEX_NON_MATCH", "T_EQ", "T_NE", "T_LT",
|
||||
"T_GT", "T_LE", "T_GE", "T_PLUS", "T_MINUS", "T_TIMES", "T_DIV", "T_MOD",
|
||||
"T_QUESTION", "T_COLON", "T_SCOPE", "T_RANGE", "T_COMMA", "T_OPEN",
|
||||
"T_CLOSE", "T_OBJECT_OPEN", "T_OBJECT_CLOSE", "T_ARRAY_OPEN", "T_ARRAY_CLOSE",
|
||||
"T_AGGREGATE", "T_ALL", "T_AND", "T_ANY", "T_ASC", "T_COLLECT", "T_DESC",
|
||||
"T_DISTINCT", "T_FALSE", "T_FILTER", "T_FOR", "T_GRAPH", "T_IN", "T_INBOUND",
|
||||
"T_INSERT", "T_INTO", "T_K_SHORTEST_PATHS", "T_LET", "T_LIKE", "T_LIMIT",
|
||||
"T_NONE", "T_NOT", "T_NULL", "T_OR", "T_OUTBOUND", "T_REMOVE", "T_REPLACE",
|
||||
"T_RETURN", "T_SHORTEST_PATH", "T_SORT", "T_TRUE", "T_UPDATE", "T_UPSERT",
|
||||
"T_WITH", "T_KEEP", "T_COUNT", "T_OPTIONS", "T_PRUNE", "T_SEARCH", "T_TO",
|
||||
"T_CURRENT", "T_NEW", "T_OLD", "T_STRING", "T_INT", "T_FLOAT", "T_PARAMETER",
|
||||
"T_QUOTED_STRING", "SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES",
|
||||
"UNEXPECTED_CHAR", "ERROR_RECONGNIGION",
|
||||
}
|
||||
staticData.ruleNames = []string{
|
||||
"DOT", "T_REGEX_MATCH", "T_REGEX_NON_MATCH", "T_EQ", "T_NE", "T_LT",
|
||||
"T_GT", "T_LE", "T_GE", "T_PLUS", "T_MINUS", "T_TIMES", "T_DIV", "T_MOD",
|
||||
"T_QUESTION", "T_COLON", "T_SCOPE", "T_RANGE", "T_COMMA", "T_OPEN",
|
||||
"T_CLOSE", "T_OBJECT_OPEN", "T_OBJECT_CLOSE", "T_ARRAY_OPEN", "T_ARRAY_CLOSE",
|
||||
"T_AGGREGATE", "T_ALL", "T_AND", "T_ANY", "T_ASC", "T_COLLECT", "T_DESC",
|
||||
"T_DISTINCT", "T_FALSE", "T_FILTER", "T_FOR", "T_GRAPH", "T_IN", "T_INBOUND",
|
||||
"T_INSERT", "T_INTO", "T_K_SHORTEST_PATHS", "T_LET", "T_LIKE", "T_LIMIT",
|
||||
"T_NONE", "T_NOT", "T_NULL", "T_OR", "T_OUTBOUND", "T_REMOVE", "T_REPLACE",
|
||||
"T_RETURN", "T_SHORTEST_PATH", "T_SORT", "T_TRUE", "T_UPDATE", "T_UPSERT",
|
||||
"T_WITH", "T_KEEP", "T_COUNT", "T_OPTIONS", "T_PRUNE", "T_SEARCH", "T_TO",
|
||||
"T_CURRENT", "T_NEW", "T_OLD", "T_STRING", "T_INT", "T_FLOAT", "T_PARAMETER",
|
||||
"T_QUOTED_STRING", "SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES",
|
||||
"UNEXPECTED_CHAR", "HEX_DIGIT", "DIGIT", "A", "B", "C", "D", "E", "F",
|
||||
"G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
|
||||
"U", "V", "W", "X", "Y", "Z", "ERROR_RECONGNIGION",
|
||||
}
|
||||
staticData.predictionContextCache = antlr.NewPredictionContextCache()
|
||||
staticData.serializedATN = []int32{
|
||||
4, 0, 78, 737, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
|
||||
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
|
||||
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
|
||||
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
|
||||
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
|
||||
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
|
||||
31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
|
||||
7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7,
|
||||
41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46,
|
||||
2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2,
|
||||
52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57,
|
||||
7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7,
|
||||
62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67,
|
||||
2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2,
|
||||
73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78,
|
||||
7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7,
|
||||
83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88,
|
||||
2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2,
|
||||
94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99,
|
||||
7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103,
|
||||
2, 104, 7, 104, 2, 105, 7, 105, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
||||
2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1,
|
||||
7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11,
|
||||
1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1,
|
||||
16, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21,
|
||||
1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1,
|
||||
25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26,
|
||||
1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 292, 8, 27, 1, 28, 1,
|
||||
28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30,
|
||||
1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1,
|
||||
32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33,
|
||||
1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1,
|
||||
35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37,
|
||||
1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1,
|
||||
39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40,
|
||||
1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1,
|
||||
41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42,
|
||||
1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1,
|
||||
44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46,
|
||||
1, 46, 3, 46, 412, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1,
|
||||
48, 1, 48, 1, 48, 1, 48, 3, 48, 424, 8, 48, 1, 49, 1, 49, 1, 49, 1, 49,
|
||||
1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1,
|
||||
50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52,
|
||||
1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1,
|
||||
53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54,
|
||||
1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1,
|
||||
56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57,
|
||||
1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1,
|
||||
59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61,
|
||||
1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1,
|
||||
62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64,
|
||||
1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1,
|
||||
66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 5, 68, 553, 8, 68,
|
||||
10, 68, 12, 68, 556, 9, 68, 1, 69, 1, 69, 5, 69, 560, 8, 69, 10, 69, 12,
|
||||
69, 563, 9, 69, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 4, 69, 570, 8, 69, 11,
|
||||
69, 12, 69, 571, 1, 69, 1, 69, 1, 69, 1, 69, 4, 69, 578, 8, 69, 11, 69,
|
||||
12, 69, 579, 3, 69, 582, 8, 69, 1, 70, 1, 70, 5, 70, 586, 8, 70, 10, 70,
|
||||
12, 70, 589, 9, 70, 1, 70, 3, 70, 592, 8, 70, 1, 70, 1, 70, 4, 70, 596,
|
||||
8, 70, 11, 70, 12, 70, 597, 1, 70, 1, 70, 3, 70, 602, 8, 70, 1, 70, 4,
|
||||
70, 605, 8, 70, 11, 70, 12, 70, 606, 3, 70, 609, 8, 70, 1, 71, 1, 71, 1,
|
||||
71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 5, 72, 620, 8, 72, 10, 72,
|
||||
12, 72, 623, 9, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 5,
|
||||
72, 632, 8, 72, 10, 72, 12, 72, 635, 9, 72, 1, 72, 3, 72, 638, 8, 72, 1,
|
||||
73, 1, 73, 1, 73, 1, 73, 5, 73, 644, 8, 73, 10, 73, 12, 73, 647, 9, 73,
|
||||
1, 73, 3, 73, 650, 8, 73, 1, 73, 1, 73, 3, 73, 654, 8, 73, 1, 73, 1, 73,
|
||||
1, 74, 1, 74, 1, 74, 1, 74, 5, 74, 662, 8, 74, 10, 74, 12, 74, 665, 9,
|
||||
74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76,
|
||||
1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 81, 1,
|
||||
81, 1, 82, 1, 82, 1, 83, 1, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, 86, 1, 86,
|
||||
1, 87, 1, 87, 1, 88, 1, 88, 1, 89, 1, 89, 1, 90, 1, 90, 1, 91, 1, 91, 1,
|
||||
92, 1, 92, 1, 93, 1, 93, 1, 94, 1, 94, 1, 95, 1, 95, 1, 96, 1, 96, 1, 97,
|
||||
1, 97, 1, 98, 1, 98, 1, 99, 1, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 102,
|
||||
1, 102, 1, 103, 1, 103, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105,
|
||||
1, 663, 0, 106, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17,
|
||||
9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35,
|
||||
18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53,
|
||||
27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71,
|
||||
36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89,
|
||||
45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53,
|
||||
107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61,
|
||||
123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69,
|
||||
139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77,
|
||||
155, 0, 157, 0, 159, 0, 161, 0, 163, 0, 165, 0, 167, 0, 169, 0, 171, 0,
|
||||
173, 0, 175, 0, 177, 0, 179, 0, 181, 0, 183, 0, 185, 0, 187, 0, 189, 0,
|
||||
191, 0, 193, 0, 195, 0, 197, 0, 199, 0, 201, 0, 203, 0, 205, 0, 207, 0,
|
||||
209, 0, 211, 78, 1, 0, 37, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57,
|
||||
65, 90, 95, 95, 97, 122, 1, 0, 49, 57, 1, 0, 48, 49, 2, 0, 43, 43, 45,
|
||||
45, 2, 0, 39, 39, 92, 92, 2, 0, 34, 34, 92, 92, 2, 0, 10, 10, 13, 13, 3,
|
||||
0, 9, 11, 13, 13, 32, 32, 3, 0, 48, 57, 65, 70, 97, 102, 1, 0, 48, 57,
|
||||
2, 0, 65, 65, 97, 97, 2, 0, 66, 66, 98, 98, 2, 0, 67, 67, 99, 99, 2, 0,
|
||||
68, 68, 100, 100, 2, 0, 69, 69, 101, 101, 2, 0, 70, 70, 102, 102, 2, 0,
|
||||
71, 71, 103, 103, 2, 0, 72, 72, 104, 104, 2, 0, 73, 73, 105, 105, 2, 0,
|
||||
74, 74, 106, 106, 2, 0, 75, 75, 107, 107, 2, 0, 76, 76, 108, 108, 2, 0,
|
||||
77, 77, 109, 109, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0,
|
||||
80, 80, 112, 112, 2, 0, 81, 81, 113, 113, 2, 0, 82, 82, 114, 114, 2, 0,
|
||||
83, 83, 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 85, 85, 117, 117, 2, 0,
|
||||
86, 86, 118, 118, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0,
|
||||
89, 89, 121, 121, 2, 0, 90, 90, 122, 122, 736, 0, 1, 1, 0, 0, 0, 0, 3,
|
||||
1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11,
|
||||
1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0,
|
||||
19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0,
|
||||
0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0,
|
||||
0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0,
|
||||
0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1,
|
||||
0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57,
|
||||
1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0,
|
||||
65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0,
|
||||
0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0,
|
||||
0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0,
|
||||
0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1,
|
||||
0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103,
|
||||
1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0,
|
||||
0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1,
|
||||
0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0,
|
||||
125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0,
|
||||
0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139,
|
||||
1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0,
|
||||
0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1,
|
||||
0, 0, 0, 0, 211, 1, 0, 0, 0, 1, 213, 1, 0, 0, 0, 3, 215, 1, 0, 0, 0, 5,
|
||||
218, 1, 0, 0, 0, 7, 221, 1, 0, 0, 0, 9, 224, 1, 0, 0, 0, 11, 227, 1, 0,
|
||||
0, 0, 13, 229, 1, 0, 0, 0, 15, 231, 1, 0, 0, 0, 17, 234, 1, 0, 0, 0, 19,
|
||||
237, 1, 0, 0, 0, 21, 239, 1, 0, 0, 0, 23, 241, 1, 0, 0, 0, 25, 243, 1,
|
||||
0, 0, 0, 27, 245, 1, 0, 0, 0, 29, 247, 1, 0, 0, 0, 31, 249, 1, 0, 0, 0,
|
||||
33, 251, 1, 0, 0, 0, 35, 254, 1, 0, 0, 0, 37, 257, 1, 0, 0, 0, 39, 259,
|
||||
1, 0, 0, 0, 41, 261, 1, 0, 0, 0, 43, 263, 1, 0, 0, 0, 45, 265, 1, 0, 0,
|
||||
0, 47, 267, 1, 0, 0, 0, 49, 269, 1, 0, 0, 0, 51, 271, 1, 0, 0, 0, 53, 281,
|
||||
1, 0, 0, 0, 55, 291, 1, 0, 0, 0, 57, 293, 1, 0, 0, 0, 59, 297, 1, 0, 0,
|
||||
0, 61, 301, 1, 0, 0, 0, 63, 309, 1, 0, 0, 0, 65, 314, 1, 0, 0, 0, 67, 323,
|
||||
1, 0, 0, 0, 69, 329, 1, 0, 0, 0, 71, 336, 1, 0, 0, 0, 73, 340, 1, 0, 0,
|
||||
0, 75, 346, 1, 0, 0, 0, 77, 349, 1, 0, 0, 0, 79, 357, 1, 0, 0, 0, 81, 364,
|
||||
1, 0, 0, 0, 83, 369, 1, 0, 0, 0, 85, 386, 1, 0, 0, 0, 87, 390, 1, 0, 0,
|
||||
0, 89, 395, 1, 0, 0, 0, 91, 401, 1, 0, 0, 0, 93, 411, 1, 0, 0, 0, 95, 413,
|
||||
1, 0, 0, 0, 97, 423, 1, 0, 0, 0, 99, 425, 1, 0, 0, 0, 101, 434, 1, 0, 0,
|
||||
0, 103, 441, 1, 0, 0, 0, 105, 449, 1, 0, 0, 0, 107, 456, 1, 0, 0, 0, 109,
|
||||
470, 1, 0, 0, 0, 111, 475, 1, 0, 0, 0, 113, 480, 1, 0, 0, 0, 115, 487,
|
||||
1, 0, 0, 0, 117, 494, 1, 0, 0, 0, 119, 499, 1, 0, 0, 0, 121, 504, 1, 0,
|
||||
0, 0, 123, 510, 1, 0, 0, 0, 125, 518, 1, 0, 0, 0, 127, 524, 1, 0, 0, 0,
|
||||
129, 531, 1, 0, 0, 0, 131, 534, 1, 0, 0, 0, 133, 542, 1, 0, 0, 0, 135,
|
||||
546, 1, 0, 0, 0, 137, 550, 1, 0, 0, 0, 139, 581, 1, 0, 0, 0, 141, 591,
|
||||
1, 0, 0, 0, 143, 610, 1, 0, 0, 0, 145, 637, 1, 0, 0, 0, 147, 639, 1, 0,
|
||||
0, 0, 149, 657, 1, 0, 0, 0, 151, 671, 1, 0, 0, 0, 153, 675, 1, 0, 0, 0,
|
||||
155, 677, 1, 0, 0, 0, 157, 679, 1, 0, 0, 0, 159, 681, 1, 0, 0, 0, 161,
|
||||
683, 1, 0, 0, 0, 163, 685, 1, 0, 0, 0, 165, 687, 1, 0, 0, 0, 167, 689,
|
||||
1, 0, 0, 0, 169, 691, 1, 0, 0, 0, 171, 693, 1, 0, 0, 0, 173, 695, 1, 0,
|
||||
0, 0, 175, 697, 1, 0, 0, 0, 177, 699, 1, 0, 0, 0, 179, 701, 1, 0, 0, 0,
|
||||
181, 703, 1, 0, 0, 0, 183, 705, 1, 0, 0, 0, 185, 707, 1, 0, 0, 0, 187,
|
||||
709, 1, 0, 0, 0, 189, 711, 1, 0, 0, 0, 191, 713, 1, 0, 0, 0, 193, 715,
|
||||
1, 0, 0, 0, 195, 717, 1, 0, 0, 0, 197, 719, 1, 0, 0, 0, 199, 721, 1, 0,
|
||||
0, 0, 201, 723, 1, 0, 0, 0, 203, 725, 1, 0, 0, 0, 205, 727, 1, 0, 0, 0,
|
||||
207, 729, 1, 0, 0, 0, 209, 731, 1, 0, 0, 0, 211, 733, 1, 0, 0, 0, 213,
|
||||
214, 5, 46, 0, 0, 214, 2, 1, 0, 0, 0, 215, 216, 5, 61, 0, 0, 216, 217,
|
||||
5, 126, 0, 0, 217, 4, 1, 0, 0, 0, 218, 219, 5, 33, 0, 0, 219, 220, 5, 126,
|
||||
0, 0, 220, 6, 1, 0, 0, 0, 221, 222, 5, 61, 0, 0, 222, 223, 5, 61, 0, 0,
|
||||
223, 8, 1, 0, 0, 0, 224, 225, 5, 33, 0, 0, 225, 226, 5, 61, 0, 0, 226,
|
||||
10, 1, 0, 0, 0, 227, 228, 5, 60, 0, 0, 228, 12, 1, 0, 0, 0, 229, 230, 5,
|
||||
62, 0, 0, 230, 14, 1, 0, 0, 0, 231, 232, 5, 60, 0, 0, 232, 233, 5, 61,
|
||||
0, 0, 233, 16, 1, 0, 0, 0, 234, 235, 5, 62, 0, 0, 235, 236, 5, 61, 0, 0,
|
||||
236, 18, 1, 0, 0, 0, 237, 238, 5, 43, 0, 0, 238, 20, 1, 0, 0, 0, 239, 240,
|
||||
5, 45, 0, 0, 240, 22, 1, 0, 0, 0, 241, 242, 5, 42, 0, 0, 242, 24, 1, 0,
|
||||
0, 0, 243, 244, 5, 47, 0, 0, 244, 26, 1, 0, 0, 0, 245, 246, 5, 37, 0, 0,
|
||||
246, 28, 1, 0, 0, 0, 247, 248, 5, 63, 0, 0, 248, 30, 1, 0, 0, 0, 249, 250,
|
||||
5, 58, 0, 0, 250, 32, 1, 0, 0, 0, 251, 252, 5, 58, 0, 0, 252, 253, 5, 58,
|
||||
0, 0, 253, 34, 1, 0, 0, 0, 254, 255, 5, 46, 0, 0, 255, 256, 5, 46, 0, 0,
|
||||
256, 36, 1, 0, 0, 0, 257, 258, 5, 44, 0, 0, 258, 38, 1, 0, 0, 0, 259, 260,
|
||||
5, 40, 0, 0, 260, 40, 1, 0, 0, 0, 261, 262, 5, 41, 0, 0, 262, 42, 1, 0,
|
||||
0, 0, 263, 264, 5, 123, 0, 0, 264, 44, 1, 0, 0, 0, 265, 266, 5, 125, 0,
|
||||
0, 266, 46, 1, 0, 0, 0, 267, 268, 5, 91, 0, 0, 268, 48, 1, 0, 0, 0, 269,
|
||||
270, 5, 93, 0, 0, 270, 50, 1, 0, 0, 0, 271, 272, 3, 159, 79, 0, 272, 273,
|
||||
3, 171, 85, 0, 273, 274, 3, 171, 85, 0, 274, 275, 3, 193, 96, 0, 275, 276,
|
||||
3, 167, 83, 0, 276, 277, 3, 171, 85, 0, 277, 278, 3, 159, 79, 0, 278, 279,
|
||||
3, 197, 98, 0, 279, 280, 3, 167, 83, 0, 280, 52, 1, 0, 0, 0, 281, 282,
|
||||
3, 159, 79, 0, 282, 283, 3, 181, 90, 0, 283, 284, 3, 181, 90, 0, 284, 54,
|
||||
1, 0, 0, 0, 285, 286, 3, 159, 79, 0, 286, 287, 3, 185, 92, 0, 287, 288,
|
||||
3, 165, 82, 0, 288, 292, 1, 0, 0, 0, 289, 290, 5, 38, 0, 0, 290, 292, 5,
|
||||
38, 0, 0, 291, 285, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 292, 56, 1, 0, 0,
|
||||
0, 293, 294, 3, 159, 79, 0, 294, 295, 3, 185, 92, 0, 295, 296, 3, 207,
|
||||
103, 0, 296, 58, 1, 0, 0, 0, 297, 298, 3, 159, 79, 0, 298, 299, 3, 195,
|
||||
97, 0, 299, 300, 3, 163, 81, 0, 300, 60, 1, 0, 0, 0, 301, 302, 3, 163,
|
||||
81, 0, 302, 303, 3, 187, 93, 0, 303, 304, 3, 181, 90, 0, 304, 305, 3, 181,
|
||||
90, 0, 305, 306, 3, 167, 83, 0, 306, 307, 3, 163, 81, 0, 307, 308, 3, 197,
|
||||
98, 0, 308, 62, 1, 0, 0, 0, 309, 310, 3, 165, 82, 0, 310, 311, 3, 167,
|
||||
83, 0, 311, 312, 3, 195, 97, 0, 312, 313, 3, 163, 81, 0, 313, 64, 1, 0,
|
||||
0, 0, 314, 315, 3, 165, 82, 0, 315, 316, 3, 175, 87, 0, 316, 317, 3, 195,
|
||||
97, 0, 317, 318, 3, 197, 98, 0, 318, 319, 3, 175, 87, 0, 319, 320, 3, 185,
|
||||
92, 0, 320, 321, 3, 163, 81, 0, 321, 322, 3, 197, 98, 0, 322, 66, 1, 0,
|
||||
0, 0, 323, 324, 3, 169, 84, 0, 324, 325, 3, 159, 79, 0, 325, 326, 3, 181,
|
||||
90, 0, 326, 327, 3, 195, 97, 0, 327, 328, 3, 167, 83, 0, 328, 68, 1, 0,
|
||||
0, 0, 329, 330, 3, 169, 84, 0, 330, 331, 3, 175, 87, 0, 331, 332, 3, 181,
|
||||
90, 0, 332, 333, 3, 197, 98, 0, 333, 334, 3, 167, 83, 0, 334, 335, 3, 193,
|
||||
96, 0, 335, 70, 1, 0, 0, 0, 336, 337, 3, 169, 84, 0, 337, 338, 3, 187,
|
||||
93, 0, 338, 339, 3, 193, 96, 0, 339, 72, 1, 0, 0, 0, 340, 341, 3, 171,
|
||||
85, 0, 341, 342, 3, 193, 96, 0, 342, 343, 3, 159, 79, 0, 343, 344, 3, 189,
|
||||
94, 0, 344, 345, 3, 173, 86, 0, 345, 74, 1, 0, 0, 0, 346, 347, 3, 175,
|
||||
87, 0, 347, 348, 3, 185, 92, 0, 348, 76, 1, 0, 0, 0, 349, 350, 3, 175,
|
||||
87, 0, 350, 351, 3, 185, 92, 0, 351, 352, 3, 161, 80, 0, 352, 353, 3, 187,
|
||||
93, 0, 353, 354, 3, 199, 99, 0, 354, 355, 3, 185, 92, 0, 355, 356, 3, 165,
|
||||
82, 0, 356, 78, 1, 0, 0, 0, 357, 358, 3, 175, 87, 0, 358, 359, 3, 185,
|
||||
92, 0, 359, 360, 3, 195, 97, 0, 360, 361, 3, 167, 83, 0, 361, 362, 3, 193,
|
||||
96, 0, 362, 363, 3, 197, 98, 0, 363, 80, 1, 0, 0, 0, 364, 365, 3, 175,
|
||||
87, 0, 365, 366, 3, 185, 92, 0, 366, 367, 3, 197, 98, 0, 367, 368, 3, 187,
|
||||
93, 0, 368, 82, 1, 0, 0, 0, 369, 370, 3, 179, 89, 0, 370, 371, 5, 95, 0,
|
||||
0, 371, 372, 3, 195, 97, 0, 372, 373, 3, 173, 86, 0, 373, 374, 3, 187,
|
||||
93, 0, 374, 375, 3, 193, 96, 0, 375, 376, 3, 197, 98, 0, 376, 377, 3, 167,
|
||||
83, 0, 377, 378, 3, 195, 97, 0, 378, 379, 3, 197, 98, 0, 379, 380, 5, 95,
|
||||
0, 0, 380, 381, 3, 189, 94, 0, 381, 382, 3, 159, 79, 0, 382, 383, 3, 197,
|
||||
98, 0, 383, 384, 3, 173, 86, 0, 384, 385, 3, 195, 97, 0, 385, 84, 1, 0,
|
||||
0, 0, 386, 387, 3, 181, 90, 0, 387, 388, 3, 167, 83, 0, 388, 389, 3, 197,
|
||||
98, 0, 389, 86, 1, 0, 0, 0, 390, 391, 3, 181, 90, 0, 391, 392, 3, 175,
|
||||
87, 0, 392, 393, 3, 179, 89, 0, 393, 394, 3, 167, 83, 0, 394, 88, 1, 0,
|
||||
0, 0, 395, 396, 3, 181, 90, 0, 396, 397, 3, 175, 87, 0, 397, 398, 3, 183,
|
||||
91, 0, 398, 399, 3, 175, 87, 0, 399, 400, 3, 197, 98, 0, 400, 90, 1, 0,
|
||||
0, 0, 401, 402, 3, 185, 92, 0, 402, 403, 3, 187, 93, 0, 403, 404, 3, 185,
|
||||
92, 0, 404, 405, 3, 167, 83, 0, 405, 92, 1, 0, 0, 0, 406, 407, 3, 185,
|
||||
92, 0, 407, 408, 3, 187, 93, 0, 408, 409, 3, 197, 98, 0, 409, 412, 1, 0,
|
||||
0, 0, 410, 412, 5, 33, 0, 0, 411, 406, 1, 0, 0, 0, 411, 410, 1, 0, 0, 0,
|
||||
412, 94, 1, 0, 0, 0, 413, 414, 3, 185, 92, 0, 414, 415, 3, 199, 99, 0,
|
||||
415, 416, 3, 181, 90, 0, 416, 417, 3, 181, 90, 0, 417, 96, 1, 0, 0, 0,
|
||||
418, 419, 3, 187, 93, 0, 419, 420, 3, 193, 96, 0, 420, 424, 1, 0, 0, 0,
|
||||
421, 422, 5, 124, 0, 0, 422, 424, 5, 124, 0, 0, 423, 418, 1, 0, 0, 0, 423,
|
||||
421, 1, 0, 0, 0, 424, 98, 1, 0, 0, 0, 425, 426, 3, 187, 93, 0, 426, 427,
|
||||
3, 199, 99, 0, 427, 428, 3, 197, 98, 0, 428, 429, 3, 161, 80, 0, 429, 430,
|
||||
3, 187, 93, 0, 430, 431, 3, 199, 99, 0, 431, 432, 3, 185, 92, 0, 432, 433,
|
||||
3, 165, 82, 0, 433, 100, 1, 0, 0, 0, 434, 435, 3, 193, 96, 0, 435, 436,
|
||||
3, 167, 83, 0, 436, 437, 3, 183, 91, 0, 437, 438, 3, 187, 93, 0, 438, 439,
|
||||
3, 201, 100, 0, 439, 440, 3, 167, 83, 0, 440, 102, 1, 0, 0, 0, 441, 442,
|
||||
3, 193, 96, 0, 442, 443, 3, 167, 83, 0, 443, 444, 3, 189, 94, 0, 444, 445,
|
||||
3, 181, 90, 0, 445, 446, 3, 159, 79, 0, 446, 447, 3, 163, 81, 0, 447, 448,
|
||||
3, 167, 83, 0, 448, 104, 1, 0, 0, 0, 449, 450, 3, 193, 96, 0, 450, 451,
|
||||
3, 167, 83, 0, 451, 452, 3, 197, 98, 0, 452, 453, 3, 199, 99, 0, 453, 454,
|
||||
3, 193, 96, 0, 454, 455, 3, 185, 92, 0, 455, 106, 1, 0, 0, 0, 456, 457,
|
||||
3, 195, 97, 0, 457, 458, 3, 173, 86, 0, 458, 459, 3, 187, 93, 0, 459, 460,
|
||||
3, 193, 96, 0, 460, 461, 3, 197, 98, 0, 461, 462, 3, 167, 83, 0, 462, 463,
|
||||
3, 195, 97, 0, 463, 464, 3, 197, 98, 0, 464, 465, 5, 95, 0, 0, 465, 466,
|
||||
3, 189, 94, 0, 466, 467, 3, 159, 79, 0, 467, 468, 3, 197, 98, 0, 468, 469,
|
||||
3, 173, 86, 0, 469, 108, 1, 0, 0, 0, 470, 471, 3, 195, 97, 0, 471, 472,
|
||||
3, 187, 93, 0, 472, 473, 3, 193, 96, 0, 473, 474, 3, 197, 98, 0, 474, 110,
|
||||
1, 0, 0, 0, 475, 476, 3, 197, 98, 0, 476, 477, 3, 193, 96, 0, 477, 478,
|
||||
3, 199, 99, 0, 478, 479, 3, 167, 83, 0, 479, 112, 1, 0, 0, 0, 480, 481,
|
||||
3, 199, 99, 0, 481, 482, 3, 189, 94, 0, 482, 483, 3, 165, 82, 0, 483, 484,
|
||||
3, 159, 79, 0, 484, 485, 3, 197, 98, 0, 485, 486, 3, 167, 83, 0, 486, 114,
|
||||
1, 0, 0, 0, 487, 488, 3, 199, 99, 0, 488, 489, 3, 189, 94, 0, 489, 490,
|
||||
3, 195, 97, 0, 490, 491, 3, 167, 83, 0, 491, 492, 3, 193, 96, 0, 492, 493,
|
||||
3, 197, 98, 0, 493, 116, 1, 0, 0, 0, 494, 495, 3, 203, 101, 0, 495, 496,
|
||||
3, 175, 87, 0, 496, 497, 3, 197, 98, 0, 497, 498, 3, 173, 86, 0, 498, 118,
|
||||
1, 0, 0, 0, 499, 500, 3, 179, 89, 0, 500, 501, 3, 167, 83, 0, 501, 502,
|
||||
3, 167, 83, 0, 502, 503, 3, 189, 94, 0, 503, 120, 1, 0, 0, 0, 504, 505,
|
||||
3, 163, 81, 0, 505, 506, 3, 187, 93, 0, 506, 507, 3, 199, 99, 0, 507, 508,
|
||||
3, 185, 92, 0, 508, 509, 3, 197, 98, 0, 509, 122, 1, 0, 0, 0, 510, 511,
|
||||
3, 187, 93, 0, 511, 512, 3, 189, 94, 0, 512, 513, 3, 197, 98, 0, 513, 514,
|
||||
3, 175, 87, 0, 514, 515, 3, 187, 93, 0, 515, 516, 3, 185, 92, 0, 516, 517,
|
||||
3, 195, 97, 0, 517, 124, 1, 0, 0, 0, 518, 519, 3, 189, 94, 0, 519, 520,
|
||||
3, 193, 96, 0, 520, 521, 3, 199, 99, 0, 521, 522, 3, 185, 92, 0, 522, 523,
|
||||
3, 167, 83, 0, 523, 126, 1, 0, 0, 0, 524, 525, 3, 195, 97, 0, 525, 526,
|
||||
3, 167, 83, 0, 526, 527, 3, 159, 79, 0, 527, 528, 3, 193, 96, 0, 528, 529,
|
||||
3, 163, 81, 0, 529, 530, 3, 173, 86, 0, 530, 128, 1, 0, 0, 0, 531, 532,
|
||||
3, 197, 98, 0, 532, 533, 3, 187, 93, 0, 533, 130, 1, 0, 0, 0, 534, 535,
|
||||
3, 163, 81, 0, 535, 536, 3, 199, 99, 0, 536, 537, 3, 193, 96, 0, 537, 538,
|
||||
3, 193, 96, 0, 538, 539, 3, 167, 83, 0, 539, 540, 3, 185, 92, 0, 540, 541,
|
||||
3, 197, 98, 0, 541, 132, 1, 0, 0, 0, 542, 543, 3, 185, 92, 0, 543, 544,
|
||||
3, 167, 83, 0, 544, 545, 3, 203, 101, 0, 545, 134, 1, 0, 0, 0, 546, 547,
|
||||
3, 187, 93, 0, 547, 548, 3, 181, 90, 0, 548, 549, 3, 165, 82, 0, 549, 136,
|
||||
1, 0, 0, 0, 550, 554, 7, 0, 0, 0, 551, 553, 7, 1, 0, 0, 552, 551, 1, 0,
|
||||
0, 0, 553, 556, 1, 0, 0, 0, 554, 552, 1, 0, 0, 0, 554, 555, 1, 0, 0, 0,
|
||||
555, 138, 1, 0, 0, 0, 556, 554, 1, 0, 0, 0, 557, 561, 7, 2, 0, 0, 558,
|
||||
560, 3, 157, 78, 0, 559, 558, 1, 0, 0, 0, 560, 563, 1, 0, 0, 0, 561, 559,
|
||||
1, 0, 0, 0, 561, 562, 1, 0, 0, 0, 562, 582, 1, 0, 0, 0, 563, 561, 1, 0,
|
||||
0, 0, 564, 582, 5, 48, 0, 0, 565, 566, 5, 48, 0, 0, 566, 567, 5, 120, 0,
|
||||
0, 567, 569, 1, 0, 0, 0, 568, 570, 3, 155, 77, 0, 569, 568, 1, 0, 0, 0,
|
||||
570, 571, 1, 0, 0, 0, 571, 569, 1, 0, 0, 0, 571, 572, 1, 0, 0, 0, 572,
|
||||
582, 1, 0, 0, 0, 573, 574, 5, 48, 0, 0, 574, 575, 5, 98, 0, 0, 575, 577,
|
||||
1, 0, 0, 0, 576, 578, 7, 3, 0, 0, 577, 576, 1, 0, 0, 0, 578, 579, 1, 0,
|
||||
0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 582, 1, 0, 0, 0,
|
||||
581, 557, 1, 0, 0, 0, 581, 564, 1, 0, 0, 0, 581, 565, 1, 0, 0, 0, 581,
|
||||
573, 1, 0, 0, 0, 582, 140, 1, 0, 0, 0, 583, 587, 7, 2, 0, 0, 584, 586,
|
||||
3, 157, 78, 0, 585, 584, 1, 0, 0, 0, 586, 589, 1, 0, 0, 0, 587, 585, 1,
|
||||
0, 0, 0, 587, 588, 1, 0, 0, 0, 588, 592, 1, 0, 0, 0, 589, 587, 1, 0, 0,
|
||||
0, 590, 592, 5, 48, 0, 0, 591, 583, 1, 0, 0, 0, 591, 590, 1, 0, 0, 0, 591,
|
||||
592, 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 595, 5, 46, 0, 0, 594, 596,
|
||||
3, 157, 78, 0, 595, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 595, 1,
|
||||
0, 0, 0, 597, 598, 1, 0, 0, 0, 598, 608, 1, 0, 0, 0, 599, 601, 3, 167,
|
||||
83, 0, 600, 602, 7, 4, 0, 0, 601, 600, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0,
|
||||
602, 604, 1, 0, 0, 0, 603, 605, 3, 157, 78, 0, 604, 603, 1, 0, 0, 0, 605,
|
||||
606, 1, 0, 0, 0, 606, 604, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 609,
|
||||
1, 0, 0, 0, 608, 599, 1, 0, 0, 0, 608, 609, 1, 0, 0, 0, 609, 142, 1, 0,
|
||||
0, 0, 610, 611, 5, 64, 0, 0, 611, 612, 3, 137, 68, 0, 612, 144, 1, 0, 0,
|
||||
0, 613, 621, 5, 39, 0, 0, 614, 615, 5, 92, 0, 0, 615, 620, 9, 0, 0, 0,
|
||||
616, 617, 5, 39, 0, 0, 617, 620, 5, 39, 0, 0, 618, 620, 8, 5, 0, 0, 619,
|
||||
614, 1, 0, 0, 0, 619, 616, 1, 0, 0, 0, 619, 618, 1, 0, 0, 0, 620, 623,
|
||||
1, 0, 0, 0, 621, 619, 1, 0, 0, 0, 621, 622, 1, 0, 0, 0, 622, 624, 1, 0,
|
||||
0, 0, 623, 621, 1, 0, 0, 0, 624, 638, 5, 39, 0, 0, 625, 633, 5, 34, 0,
|
||||
0, 626, 627, 5, 92, 0, 0, 627, 632, 9, 0, 0, 0, 628, 629, 5, 34, 0, 0,
|
||||
629, 632, 5, 34, 0, 0, 630, 632, 8, 6, 0, 0, 631, 626, 1, 0, 0, 0, 631,
|
||||
628, 1, 0, 0, 0, 631, 630, 1, 0, 0, 0, 632, 635, 1, 0, 0, 0, 633, 631,
|
||||
1, 0, 0, 0, 633, 634, 1, 0, 0, 0, 634, 636, 1, 0, 0, 0, 635, 633, 1, 0,
|
||||
0, 0, 636, 638, 5, 34, 0, 0, 637, 613, 1, 0, 0, 0, 637, 625, 1, 0, 0, 0,
|
||||
638, 146, 1, 0, 0, 0, 639, 640, 5, 47, 0, 0, 640, 641, 5, 47, 0, 0, 641,
|
||||
645, 1, 0, 0, 0, 642, 644, 8, 7, 0, 0, 643, 642, 1, 0, 0, 0, 644, 647,
|
||||
1, 0, 0, 0, 645, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 653, 1, 0,
|
||||
0, 0, 647, 645, 1, 0, 0, 0, 648, 650, 5, 13, 0, 0, 649, 648, 1, 0, 0, 0,
|
||||
649, 650, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 654, 5, 10, 0, 0, 652,
|
||||
654, 5, 0, 0, 1, 653, 649, 1, 0, 0, 0, 653, 652, 1, 0, 0, 0, 654, 655,
|
||||
1, 0, 0, 0, 655, 656, 6, 73, 0, 0, 656, 148, 1, 0, 0, 0, 657, 658, 5, 47,
|
||||
0, 0, 658, 659, 5, 42, 0, 0, 659, 663, 1, 0, 0, 0, 660, 662, 9, 0, 0, 0,
|
||||
661, 660, 1, 0, 0, 0, 662, 665, 1, 0, 0, 0, 663, 664, 1, 0, 0, 0, 663,
|
||||
661, 1, 0, 0, 0, 664, 666, 1, 0, 0, 0, 665, 663, 1, 0, 0, 0, 666, 667,
|
||||
5, 42, 0, 0, 667, 668, 5, 47, 0, 0, 668, 669, 1, 0, 0, 0, 669, 670, 6,
|
||||
74, 0, 0, 670, 150, 1, 0, 0, 0, 671, 672, 7, 8, 0, 0, 672, 673, 1, 0, 0,
|
||||
0, 673, 674, 6, 75, 0, 0, 674, 152, 1, 0, 0, 0, 675, 676, 9, 0, 0, 0, 676,
|
||||
154, 1, 0, 0, 0, 677, 678, 7, 9, 0, 0, 678, 156, 1, 0, 0, 0, 679, 680,
|
||||
7, 10, 0, 0, 680, 158, 1, 0, 0, 0, 681, 682, 7, 11, 0, 0, 682, 160, 1,
|
||||
0, 0, 0, 683, 684, 7, 12, 0, 0, 684, 162, 1, 0, 0, 0, 685, 686, 7, 13,
|
||||
0, 0, 686, 164, 1, 0, 0, 0, 687, 688, 7, 14, 0, 0, 688, 166, 1, 0, 0, 0,
|
||||
689, 690, 7, 15, 0, 0, 690, 168, 1, 0, 0, 0, 691, 692, 7, 16, 0, 0, 692,
|
||||
170, 1, 0, 0, 0, 693, 694, 7, 17, 0, 0, 694, 172, 1, 0, 0, 0, 695, 696,
|
||||
7, 18, 0, 0, 696, 174, 1, 0, 0, 0, 697, 698, 7, 19, 0, 0, 698, 176, 1,
|
||||
0, 0, 0, 699, 700, 7, 20, 0, 0, 700, 178, 1, 0, 0, 0, 701, 702, 7, 21,
|
||||
0, 0, 702, 180, 1, 0, 0, 0, 703, 704, 7, 22, 0, 0, 704, 182, 1, 0, 0, 0,
|
||||
705, 706, 7, 23, 0, 0, 706, 184, 1, 0, 0, 0, 707, 708, 7, 24, 0, 0, 708,
|
||||
186, 1, 0, 0, 0, 709, 710, 7, 25, 0, 0, 710, 188, 1, 0, 0, 0, 711, 712,
|
||||
7, 26, 0, 0, 712, 190, 1, 0, 0, 0, 713, 714, 7, 27, 0, 0, 714, 192, 1,
|
||||
0, 0, 0, 715, 716, 7, 28, 0, 0, 716, 194, 1, 0, 0, 0, 717, 718, 7, 29,
|
||||
0, 0, 718, 196, 1, 0, 0, 0, 719, 720, 7, 30, 0, 0, 720, 198, 1, 0, 0, 0,
|
||||
721, 722, 7, 31, 0, 0, 722, 200, 1, 0, 0, 0, 723, 724, 7, 32, 0, 0, 724,
|
||||
202, 1, 0, 0, 0, 725, 726, 7, 33, 0, 0, 726, 204, 1, 0, 0, 0, 727, 728,
|
||||
7, 34, 0, 0, 728, 206, 1, 0, 0, 0, 729, 730, 7, 35, 0, 0, 730, 208, 1,
|
||||
0, 0, 0, 731, 732, 7, 36, 0, 0, 732, 210, 1, 0, 0, 0, 733, 734, 9, 0, 0,
|
||||
0, 734, 735, 1, 0, 0, 0, 735, 736, 6, 105, 1, 0, 736, 212, 1, 0, 0, 0,
|
||||
24, 0, 291, 411, 423, 554, 561, 571, 579, 581, 587, 591, 597, 601, 606,
|
||||
608, 619, 621, 631, 633, 637, 645, 649, 653, 663, 2, 0, 1, 0, 0, 2, 0,
|
||||
}
|
||||
deserializer := antlr.NewATNDeserializer(nil)
|
||||
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
|
||||
atn := staticData.atn
|
||||
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
|
||||
decisionToDFA := staticData.decisionToDFA
|
||||
for index, state := range atn.DecisionToState {
|
||||
decisionToDFA[index] = antlr.NewDFA(state, index)
|
||||
}
|
||||
}
|
||||
|
||||
// CAQLLexerInit initializes any static state used to implement CAQLLexer. By default the
|
||||
// static state used to implement the lexer is lazily initialized during the first call to
|
||||
// NewCAQLLexer(). You can call this function if you wish to initialize the static state ahead
|
||||
// of time.
|
||||
func CAQLLexerInit() {
|
||||
staticData := &caqllexerLexerStaticData
|
||||
staticData.once.Do(caqllexerLexerInit)
|
||||
}
|
||||
|
||||
// NewCAQLLexer produces a new lexer instance for the optional input antlr.CharStream.
|
||||
func NewCAQLLexer(input antlr.CharStream) *CAQLLexer {
|
||||
CAQLLexerInit()
|
||||
l := new(CAQLLexer)
|
||||
l.BaseLexer = antlr.NewBaseLexer(input)
|
||||
staticData := &caqllexerLexerStaticData
|
||||
l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.predictionContextCache)
|
||||
l.channelNames = staticData.channelNames
|
||||
l.modeNames = staticData.modeNames
|
||||
l.RuleNames = staticData.ruleNames
|
||||
l.LiteralNames = staticData.literalNames
|
||||
l.SymbolicNames = staticData.symbolicNames
|
||||
l.GrammarFileName = "CAQLLexer.g4"
|
||||
// TODO: l.EOF = antlr.TokenEOF
|
||||
|
||||
|
||||
@@ -1,161 +1,180 @@
|
||||
// Code generated from CAQLParser.g4 by ANTLR 4.9.3. DO NOT EDIT.
|
||||
// Code generated from CAQLParser.g4 by ANTLR 4.10.1. DO NOT EDIT.
|
||||
|
||||
package parser // CAQLParser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/antlr/antlr4/runtime/Go/antlr"
|
||||
)
|
||||
|
||||
// Suppress unused import errors
|
||||
var _ = fmt.Printf
|
||||
var _ = reflect.Copy
|
||||
var _ = strconv.Itoa
|
||||
|
||||
var parserATN = []uint16{
|
||||
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 80, 192,
|
||||
4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7,
|
||||
4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 3, 2,
|
||||
3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 32, 10, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3,
|
||||
48, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 66, 10, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 7, 3, 86, 10, 3, 12, 3, 14, 3, 89, 11, 3, 3, 4, 3, 4,
|
||||
3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 97, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5,
|
||||
3, 5, 3, 5, 3, 5, 5, 5, 107, 10, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5,
|
||||
3, 5, 3, 5, 7, 5, 117, 10, 5, 12, 5, 14, 5, 120, 11, 5, 3, 6, 3, 6, 5,
|
||||
6, 124, 10, 6, 3, 7, 3, 7, 3, 7, 5, 7, 129, 10, 7, 3, 7, 3, 7, 7, 7, 133,
|
||||
10, 7, 12, 7, 14, 7, 136, 11, 7, 3, 7, 5, 7, 139, 10, 7, 3, 7, 3, 7, 3,
|
||||
8, 3, 8, 3, 9, 3, 9, 5, 9, 147, 10, 9, 3, 9, 3, 9, 7, 9, 151, 10, 9, 12,
|
||||
9, 14, 9, 154, 11, 9, 3, 9, 5, 9, 157, 10, 9, 3, 9, 3, 9, 3, 10, 3, 10,
|
||||
5, 10, 163, 10, 10, 3, 10, 3, 10, 7, 10, 167, 10, 10, 12, 10, 14, 10, 170,
|
||||
11, 10, 3, 10, 5, 10, 173, 10, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3,
|
||||
11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 188, 10, 11,
|
||||
3, 12, 3, 12, 3, 12, 4, 134, 152, 4, 4, 8, 13, 2, 4, 6, 8, 10, 12, 14,
|
||||
16, 18, 20, 22, 2, 11, 3, 2, 12, 13, 3, 2, 14, 16, 3, 2, 8, 11, 3, 2, 6,
|
||||
7, 5, 2, 29, 29, 31, 31, 48, 48, 4, 2, 6, 11, 40, 40, 4, 2, 4, 5, 46, 46,
|
||||
7, 2, 36, 36, 50, 50, 58, 58, 72, 73, 75, 75, 4, 2, 71, 71, 75, 75, 2,
|
||||
216, 2, 24, 3, 2, 2, 2, 4, 31, 3, 2, 2, 2, 6, 96, 3, 2, 2, 2, 8, 106, 3,
|
||||
2, 2, 2, 10, 123, 3, 2, 2, 2, 12, 125, 3, 2, 2, 2, 14, 142, 3, 2, 2, 2,
|
||||
16, 144, 3, 2, 2, 2, 18, 160, 3, 2, 2, 2, 20, 187, 3, 2, 2, 2, 22, 189,
|
||||
3, 2, 2, 2, 24, 25, 5, 4, 3, 2, 25, 26, 7, 2, 2, 3, 26, 3, 3, 2, 2, 2,
|
||||
27, 28, 8, 3, 1, 2, 28, 32, 5, 14, 8, 2, 29, 32, 5, 8, 5, 2, 30, 32, 5,
|
||||
6, 4, 2, 31, 27, 3, 2, 2, 2, 31, 29, 3, 2, 2, 2, 31, 30, 3, 2, 2, 2, 32,
|
||||
87, 3, 2, 2, 2, 33, 34, 12, 15, 2, 2, 34, 35, 9, 2, 2, 2, 35, 86, 5, 4,
|
||||
3, 16, 36, 37, 12, 14, 2, 2, 37, 38, 9, 3, 2, 2, 38, 86, 5, 4, 3, 15, 39,
|
||||
40, 12, 13, 2, 2, 40, 41, 7, 20, 2, 2, 41, 86, 5, 4, 3, 14, 42, 43, 12,
|
||||
12, 2, 2, 43, 44, 9, 4, 2, 2, 44, 86, 5, 4, 3, 13, 45, 47, 12, 11, 2, 2,
|
||||
46, 48, 7, 49, 2, 2, 47, 46, 3, 2, 2, 2, 47, 48, 3, 2, 2, 2, 48, 49, 3,
|
||||
2, 2, 2, 49, 50, 7, 40, 2, 2, 50, 86, 5, 4, 3, 12, 51, 52, 12, 10, 2, 2,
|
||||
52, 53, 9, 5, 2, 2, 53, 86, 5, 4, 3, 11, 54, 55, 12, 9, 2, 2, 55, 56, 9,
|
||||
6, 2, 2, 56, 57, 9, 7, 2, 2, 57, 86, 5, 4, 3, 10, 58, 59, 12, 8, 2, 2,
|
||||
59, 60, 9, 6, 2, 2, 60, 61, 7, 49, 2, 2, 61, 62, 7, 40, 2, 2, 62, 86, 5,
|
||||
4, 3, 9, 63, 65, 12, 7, 2, 2, 64, 66, 7, 49, 2, 2, 65, 64, 3, 2, 2, 2,
|
||||
65, 66, 3, 2, 2, 2, 66, 67, 3, 2, 2, 2, 67, 68, 9, 8, 2, 2, 68, 86, 5,
|
||||
4, 3, 8, 69, 70, 12, 6, 2, 2, 70, 71, 7, 30, 2, 2, 71, 86, 5, 4, 3, 7,
|
||||
72, 73, 12, 5, 2, 2, 73, 74, 7, 51, 2, 2, 74, 86, 5, 4, 3, 6, 75, 76, 12,
|
||||
4, 2, 2, 76, 77, 7, 17, 2, 2, 77, 78, 5, 4, 3, 2, 78, 79, 7, 18, 2, 2,
|
||||
79, 80, 5, 4, 3, 5, 80, 86, 3, 2, 2, 2, 81, 82, 12, 3, 2, 2, 82, 83, 7,
|
||||
17, 2, 2, 83, 84, 7, 18, 2, 2, 84, 86, 5, 4, 3, 4, 85, 33, 3, 2, 2, 2,
|
||||
85, 36, 3, 2, 2, 2, 85, 39, 3, 2, 2, 2, 85, 42, 3, 2, 2, 2, 85, 45, 3,
|
||||
2, 2, 2, 85, 51, 3, 2, 2, 2, 85, 54, 3, 2, 2, 2, 85, 58, 3, 2, 2, 2, 85,
|
||||
63, 3, 2, 2, 2, 85, 69, 3, 2, 2, 2, 85, 72, 3, 2, 2, 2, 85, 75, 3, 2, 2,
|
||||
2, 85, 81, 3, 2, 2, 2, 86, 89, 3, 2, 2, 2, 87, 85, 3, 2, 2, 2, 87, 88,
|
||||
3, 2, 2, 2, 88, 5, 3, 2, 2, 2, 89, 87, 3, 2, 2, 2, 90, 91, 7, 12, 2, 2,
|
||||
91, 97, 5, 4, 3, 2, 92, 93, 7, 13, 2, 2, 93, 97, 5, 4, 3, 2, 94, 95, 7,
|
||||
49, 2, 2, 95, 97, 5, 4, 3, 2, 96, 90, 3, 2, 2, 2, 96, 92, 3, 2, 2, 2, 96,
|
||||
94, 3, 2, 2, 2, 97, 7, 3, 2, 2, 2, 98, 99, 8, 5, 1, 2, 99, 107, 7, 71,
|
||||
2, 2, 100, 107, 5, 10, 6, 2, 101, 107, 5, 12, 7, 2, 102, 103, 7, 22, 2,
|
||||
2, 103, 104, 5, 4, 3, 2, 104, 105, 7, 23, 2, 2, 105, 107, 3, 2, 2, 2, 106,
|
||||
98, 3, 2, 2, 2, 106, 100, 3, 2, 2, 2, 106, 101, 3, 2, 2, 2, 106, 102, 3,
|
||||
2, 2, 2, 107, 118, 3, 2, 2, 2, 108, 109, 12, 4, 2, 2, 109, 110, 7, 3, 2,
|
||||
2, 110, 117, 7, 71, 2, 2, 111, 112, 12, 3, 2, 2, 112, 113, 7, 26, 2, 2,
|
||||
113, 114, 5, 4, 3, 2, 114, 115, 7, 27, 2, 2, 115, 117, 3, 2, 2, 2, 116,
|
||||
108, 3, 2, 2, 2, 116, 111, 3, 2, 2, 2, 117, 120, 3, 2, 2, 2, 118, 116,
|
||||
3, 2, 2, 2, 118, 119, 3, 2, 2, 2, 119, 9, 3, 2, 2, 2, 120, 118, 3, 2, 2,
|
||||
2, 121, 124, 5, 16, 9, 2, 122, 124, 5, 18, 10, 2, 123, 121, 3, 2, 2, 2,
|
||||
123, 122, 3, 2, 2, 2, 124, 11, 3, 2, 2, 2, 125, 126, 7, 71, 2, 2, 126,
|
||||
128, 7, 22, 2, 2, 127, 129, 5, 4, 3, 2, 128, 127, 3, 2, 2, 2, 128, 129,
|
||||
3, 2, 2, 2, 129, 134, 3, 2, 2, 2, 130, 131, 7, 21, 2, 2, 131, 133, 5, 4,
|
||||
3, 2, 132, 130, 3, 2, 2, 2, 133, 136, 3, 2, 2, 2, 134, 135, 3, 2, 2, 2,
|
||||
134, 132, 3, 2, 2, 2, 135, 138, 3, 2, 2, 2, 136, 134, 3, 2, 2, 2, 137,
|
||||
139, 7, 21, 2, 2, 138, 137, 3, 2, 2, 2, 138, 139, 3, 2, 2, 2, 139, 140,
|
||||
3, 2, 2, 2, 140, 141, 7, 23, 2, 2, 141, 13, 3, 2, 2, 2, 142, 143, 9, 9,
|
||||
2, 2, 143, 15, 3, 2, 2, 2, 144, 146, 7, 26, 2, 2, 145, 147, 5, 4, 3, 2,
|
||||
146, 145, 3, 2, 2, 2, 146, 147, 3, 2, 2, 2, 147, 152, 3, 2, 2, 2, 148,
|
||||
149, 7, 21, 2, 2, 149, 151, 5, 4, 3, 2, 150, 148, 3, 2, 2, 2, 151, 154,
|
||||
3, 2, 2, 2, 152, 153, 3, 2, 2, 2, 152, 150, 3, 2, 2, 2, 153, 156, 3, 2,
|
||||
2, 2, 154, 152, 3, 2, 2, 2, 155, 157, 7, 21, 2, 2, 156, 155, 3, 2, 2, 2,
|
||||
156, 157, 3, 2, 2, 2, 157, 158, 3, 2, 2, 2, 158, 159, 7, 27, 2, 2, 159,
|
||||
17, 3, 2, 2, 2, 160, 162, 7, 24, 2, 2, 161, 163, 5, 20, 11, 2, 162, 161,
|
||||
3, 2, 2, 2, 162, 163, 3, 2, 2, 2, 163, 168, 3, 2, 2, 2, 164, 165, 7, 21,
|
||||
2, 2, 165, 167, 5, 20, 11, 2, 166, 164, 3, 2, 2, 2, 167, 170, 3, 2, 2,
|
||||
2, 168, 166, 3, 2, 2, 2, 168, 169, 3, 2, 2, 2, 169, 172, 3, 2, 2, 2, 170,
|
||||
168, 3, 2, 2, 2, 171, 173, 7, 21, 2, 2, 172, 171, 3, 2, 2, 2, 172, 173,
|
||||
3, 2, 2, 2, 173, 174, 3, 2, 2, 2, 174, 175, 7, 25, 2, 2, 175, 19, 3, 2,
|
||||
2, 2, 176, 188, 7, 71, 2, 2, 177, 178, 5, 22, 12, 2, 178, 179, 7, 18, 2,
|
||||
2, 179, 180, 5, 4, 3, 2, 180, 188, 3, 2, 2, 2, 181, 182, 7, 26, 2, 2, 182,
|
||||
183, 5, 4, 3, 2, 183, 184, 7, 27, 2, 2, 184, 185, 7, 18, 2, 2, 185, 186,
|
||||
5, 4, 3, 2, 186, 188, 3, 2, 2, 2, 187, 176, 3, 2, 2, 2, 187, 177, 3, 2,
|
||||
2, 2, 187, 181, 3, 2, 2, 2, 188, 21, 3, 2, 2, 2, 189, 190, 9, 10, 2, 2,
|
||||
190, 23, 3, 2, 2, 2, 22, 31, 47, 65, 85, 87, 96, 106, 116, 118, 123, 128,
|
||||
134, 138, 146, 152, 156, 162, 168, 172, 187,
|
||||
}
|
||||
var literalNames = []string{
|
||||
"", "'.'", "'=~'", "'!~'", "'=='", "'!='", "'<'", "'>'", "'<='", "'>='",
|
||||
"'+'", "'-'", "'*'", "'/'", "'%'", "'?'", "':'", "'::'", "'..'", "','",
|
||||
"'('", "')'", "'{'", "'}'", "'['", "']'",
|
||||
}
|
||||
var symbolicNames = []string{
|
||||
"", "DOT", "T_REGEX_MATCH", "T_REGEX_NON_MATCH", "T_EQ", "T_NE", "T_LT",
|
||||
"T_GT", "T_LE", "T_GE", "T_PLUS", "T_MINUS", "T_TIMES", "T_DIV", "T_MOD",
|
||||
"T_QUESTION", "T_COLON", "T_SCOPE", "T_RANGE", "T_COMMA", "T_OPEN", "T_CLOSE",
|
||||
"T_OBJECT_OPEN", "T_OBJECT_CLOSE", "T_ARRAY_OPEN", "T_ARRAY_CLOSE", "T_AGGREGATE",
|
||||
"T_ALL", "T_AND", "T_ANY", "T_ASC", "T_COLLECT", "T_DESC", "T_DISTINCT",
|
||||
"T_FALSE", "T_FILTER", "T_FOR", "T_GRAPH", "T_IN", "T_INBOUND", "T_INSERT",
|
||||
"T_INTO", "T_K_SHORTEST_PATHS", "T_LET", "T_LIKE", "T_LIMIT", "T_NONE",
|
||||
"T_NOT", "T_NULL", "T_OR", "T_OUTBOUND", "T_REMOVE", "T_REPLACE", "T_RETURN",
|
||||
"T_SHORTEST_PATH", "T_SORT", "T_TRUE", "T_UPDATE", "T_UPSERT", "T_WITH",
|
||||
"T_KEEP", "T_COUNT", "T_OPTIONS", "T_PRUNE", "T_SEARCH", "T_TO", "T_CURRENT",
|
||||
"T_NEW", "T_OLD", "T_STRING", "T_INT", "T_FLOAT", "T_PARAMETER", "T_QUOTED_STRING",
|
||||
"SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES", "UNEXPECTED_CHAR",
|
||||
"ERROR_RECONGNIGION",
|
||||
}
|
||||
|
||||
var ruleNames = []string{
|
||||
"parse", "expression", "operator_unary", "reference", "compound_value",
|
||||
"function_call", "value_literal", "array", "object", "object_element",
|
||||
"object_element_name",
|
||||
}
|
||||
var _ = sync.Once{}
|
||||
|
||||
type CAQLParser struct {
|
||||
*antlr.BaseParser
|
||||
}
|
||||
|
||||
// NewCAQLParser produces a new parser instance for the optional input antlr.TokenStream.
|
||||
//
|
||||
// The *CAQLParser instance produced may be reused by calling the SetInputStream method.
|
||||
// The initial parser configuration is expensive to construct, and the object is not thread-safe;
|
||||
// however, if used within a Golang sync.Pool, the construction cost amortizes well and the
|
||||
// objects can be used in a thread-safe manner.
|
||||
func NewCAQLParser(input antlr.TokenStream) *CAQLParser {
|
||||
this := new(CAQLParser)
|
||||
deserializer := antlr.NewATNDeserializer(nil)
|
||||
deserializedATN := deserializer.DeserializeFromUInt16(parserATN)
|
||||
decisionToDFA := make([]*antlr.DFA, len(deserializedATN.DecisionToState))
|
||||
for index, ds := range deserializedATN.DecisionToState {
|
||||
decisionToDFA[index] = antlr.NewDFA(ds, index)
|
||||
}
|
||||
this.BaseParser = antlr.NewBaseParser(input)
|
||||
var caqlparserParserStaticData struct {
|
||||
once sync.Once
|
||||
serializedATN []int32
|
||||
literalNames []string
|
||||
symbolicNames []string
|
||||
ruleNames []string
|
||||
predictionContextCache *antlr.PredictionContextCache
|
||||
atn *antlr.ATN
|
||||
decisionToDFA []*antlr.DFA
|
||||
}
|
||||
|
||||
this.Interpreter = antlr.NewParserATNSimulator(this, deserializedATN, decisionToDFA, antlr.NewPredictionContextCache())
|
||||
this.RuleNames = ruleNames
|
||||
this.LiteralNames = literalNames
|
||||
this.SymbolicNames = symbolicNames
|
||||
func caqlparserParserInit() {
|
||||
staticData := &caqlparserParserStaticData
|
||||
staticData.literalNames = []string{
|
||||
"", "'.'", "'=~'", "'!~'", "'=='", "'!='", "'<'", "'>'", "'<='", "'>='",
|
||||
"'+'", "'-'", "'*'", "'/'", "'%'", "'?'", "':'", "'::'", "'..'", "','",
|
||||
"'('", "')'", "'{'", "'}'", "'['", "']'",
|
||||
}
|
||||
staticData.symbolicNames = []string{
|
||||
"", "DOT", "T_REGEX_MATCH", "T_REGEX_NON_MATCH", "T_EQ", "T_NE", "T_LT",
|
||||
"T_GT", "T_LE", "T_GE", "T_PLUS", "T_MINUS", "T_TIMES", "T_DIV", "T_MOD",
|
||||
"T_QUESTION", "T_COLON", "T_SCOPE", "T_RANGE", "T_COMMA", "T_OPEN",
|
||||
"T_CLOSE", "T_OBJECT_OPEN", "T_OBJECT_CLOSE", "T_ARRAY_OPEN", "T_ARRAY_CLOSE",
|
||||
"T_AGGREGATE", "T_ALL", "T_AND", "T_ANY", "T_ASC", "T_COLLECT", "T_DESC",
|
||||
"T_DISTINCT", "T_FALSE", "T_FILTER", "T_FOR", "T_GRAPH", "T_IN", "T_INBOUND",
|
||||
"T_INSERT", "T_INTO", "T_K_SHORTEST_PATHS", "T_LET", "T_LIKE", "T_LIMIT",
|
||||
"T_NONE", "T_NOT", "T_NULL", "T_OR", "T_OUTBOUND", "T_REMOVE", "T_REPLACE",
|
||||
"T_RETURN", "T_SHORTEST_PATH", "T_SORT", "T_TRUE", "T_UPDATE", "T_UPSERT",
|
||||
"T_WITH", "T_KEEP", "T_COUNT", "T_OPTIONS", "T_PRUNE", "T_SEARCH", "T_TO",
|
||||
"T_CURRENT", "T_NEW", "T_OLD", "T_STRING", "T_INT", "T_FLOAT", "T_PARAMETER",
|
||||
"T_QUOTED_STRING", "SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES",
|
||||
"UNEXPECTED_CHAR", "ERROR_RECONGNIGION",
|
||||
}
|
||||
staticData.ruleNames = []string{
|
||||
"parse", "expression", "operator_unary", "reference", "compound_value",
|
||||
"function_call", "value_literal", "array", "object", "object_element",
|
||||
"object_element_name",
|
||||
}
|
||||
staticData.predictionContextCache = antlr.NewPredictionContextCache()
|
||||
staticData.serializedATN = []int32{
|
||||
4, 1, 78, 190, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7,
|
||||
4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7,
|
||||
10, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 30, 8, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 3, 1, 46, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 64, 8, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 5, 1, 84, 8, 1, 10, 1, 12, 1, 87, 9, 1, 1, 2, 1, 2,
|
||||
1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 95, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
|
||||
3, 1, 3, 1, 3, 3, 3, 105, 8, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
|
||||
3, 1, 3, 5, 3, 115, 8, 3, 10, 3, 12, 3, 118, 9, 3, 1, 4, 1, 4, 3, 4, 122,
|
||||
8, 4, 1, 5, 1, 5, 1, 5, 3, 5, 127, 8, 5, 1, 5, 1, 5, 5, 5, 131, 8, 5, 10,
|
||||
5, 12, 5, 134, 9, 5, 1, 5, 3, 5, 137, 8, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1,
|
||||
7, 1, 7, 3, 7, 145, 8, 7, 1, 7, 1, 7, 5, 7, 149, 8, 7, 10, 7, 12, 7, 152,
|
||||
9, 7, 1, 7, 3, 7, 155, 8, 7, 1, 7, 1, 7, 1, 8, 1, 8, 3, 8, 161, 8, 8, 1,
|
||||
8, 1, 8, 5, 8, 165, 8, 8, 10, 8, 12, 8, 168, 9, 8, 1, 8, 3, 8, 171, 8,
|
||||
8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1,
|
||||
9, 1, 9, 3, 9, 186, 8, 9, 1, 10, 1, 10, 1, 10, 2, 132, 150, 2, 2, 6, 11,
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 0, 9, 1, 0, 10, 11, 1, 0, 12, 14,
|
||||
1, 0, 6, 9, 1, 0, 4, 5, 3, 0, 27, 27, 29, 29, 46, 46, 2, 0, 4, 9, 38, 38,
|
||||
2, 0, 2, 3, 44, 44, 5, 0, 34, 34, 48, 48, 56, 56, 70, 71, 73, 73, 2, 0,
|
||||
69, 69, 73, 73, 214, 0, 22, 1, 0, 0, 0, 2, 29, 1, 0, 0, 0, 4, 94, 1, 0,
|
||||
0, 0, 6, 104, 1, 0, 0, 0, 8, 121, 1, 0, 0, 0, 10, 123, 1, 0, 0, 0, 12,
|
||||
140, 1, 0, 0, 0, 14, 142, 1, 0, 0, 0, 16, 158, 1, 0, 0, 0, 18, 185, 1,
|
||||
0, 0, 0, 20, 187, 1, 0, 0, 0, 22, 23, 3, 2, 1, 0, 23, 24, 5, 0, 0, 1, 24,
|
||||
1, 1, 0, 0, 0, 25, 26, 6, 1, -1, 0, 26, 30, 3, 12, 6, 0, 27, 30, 3, 6,
|
||||
3, 0, 28, 30, 3, 4, 2, 0, 29, 25, 1, 0, 0, 0, 29, 27, 1, 0, 0, 0, 29, 28,
|
||||
1, 0, 0, 0, 30, 85, 1, 0, 0, 0, 31, 32, 10, 13, 0, 0, 32, 33, 7, 0, 0,
|
||||
0, 33, 84, 3, 2, 1, 14, 34, 35, 10, 12, 0, 0, 35, 36, 7, 1, 0, 0, 36, 84,
|
||||
3, 2, 1, 13, 37, 38, 10, 11, 0, 0, 38, 39, 5, 18, 0, 0, 39, 84, 3, 2, 1,
|
||||
12, 40, 41, 10, 10, 0, 0, 41, 42, 7, 2, 0, 0, 42, 84, 3, 2, 1, 11, 43,
|
||||
45, 10, 9, 0, 0, 44, 46, 5, 47, 0, 0, 45, 44, 1, 0, 0, 0, 45, 46, 1, 0,
|
||||
0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 5, 38, 0, 0, 48, 84, 3, 2, 1, 10, 49,
|
||||
50, 10, 8, 0, 0, 50, 51, 7, 3, 0, 0, 51, 84, 3, 2, 1, 9, 52, 53, 10, 7,
|
||||
0, 0, 53, 54, 7, 4, 0, 0, 54, 55, 7, 5, 0, 0, 55, 84, 3, 2, 1, 8, 56, 57,
|
||||
10, 6, 0, 0, 57, 58, 7, 4, 0, 0, 58, 59, 5, 47, 0, 0, 59, 60, 5, 38, 0,
|
||||
0, 60, 84, 3, 2, 1, 7, 61, 63, 10, 5, 0, 0, 62, 64, 5, 47, 0, 0, 63, 62,
|
||||
1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 66, 7, 6, 0, 0,
|
||||
66, 84, 3, 2, 1, 6, 67, 68, 10, 4, 0, 0, 68, 69, 5, 28, 0, 0, 69, 84, 3,
|
||||
2, 1, 5, 70, 71, 10, 3, 0, 0, 71, 72, 5, 49, 0, 0, 72, 84, 3, 2, 1, 4,
|
||||
73, 74, 10, 2, 0, 0, 74, 75, 5, 15, 0, 0, 75, 76, 3, 2, 1, 0, 76, 77, 5,
|
||||
16, 0, 0, 77, 78, 3, 2, 1, 3, 78, 84, 1, 0, 0, 0, 79, 80, 10, 1, 0, 0,
|
||||
80, 81, 5, 15, 0, 0, 81, 82, 5, 16, 0, 0, 82, 84, 3, 2, 1, 2, 83, 31, 1,
|
||||
0, 0, 0, 83, 34, 1, 0, 0, 0, 83, 37, 1, 0, 0, 0, 83, 40, 1, 0, 0, 0, 83,
|
||||
43, 1, 0, 0, 0, 83, 49, 1, 0, 0, 0, 83, 52, 1, 0, 0, 0, 83, 56, 1, 0, 0,
|
||||
0, 83, 61, 1, 0, 0, 0, 83, 67, 1, 0, 0, 0, 83, 70, 1, 0, 0, 0, 83, 73,
|
||||
1, 0, 0, 0, 83, 79, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, 0, 0, 0,
|
||||
85, 86, 1, 0, 0, 0, 86, 3, 1, 0, 0, 0, 87, 85, 1, 0, 0, 0, 88, 89, 5, 10,
|
||||
0, 0, 89, 95, 3, 2, 1, 0, 90, 91, 5, 11, 0, 0, 91, 95, 3, 2, 1, 0, 92,
|
||||
93, 5, 47, 0, 0, 93, 95, 3, 2, 1, 0, 94, 88, 1, 0, 0, 0, 94, 90, 1, 0,
|
||||
0, 0, 94, 92, 1, 0, 0, 0, 95, 5, 1, 0, 0, 0, 96, 97, 6, 3, -1, 0, 97, 105,
|
||||
5, 69, 0, 0, 98, 105, 3, 8, 4, 0, 99, 105, 3, 10, 5, 0, 100, 101, 5, 20,
|
||||
0, 0, 101, 102, 3, 2, 1, 0, 102, 103, 5, 21, 0, 0, 103, 105, 1, 0, 0, 0,
|
||||
104, 96, 1, 0, 0, 0, 104, 98, 1, 0, 0, 0, 104, 99, 1, 0, 0, 0, 104, 100,
|
||||
1, 0, 0, 0, 105, 116, 1, 0, 0, 0, 106, 107, 10, 2, 0, 0, 107, 108, 5, 1,
|
||||
0, 0, 108, 115, 5, 69, 0, 0, 109, 110, 10, 1, 0, 0, 110, 111, 5, 24, 0,
|
||||
0, 111, 112, 3, 2, 1, 0, 112, 113, 5, 25, 0, 0, 113, 115, 1, 0, 0, 0, 114,
|
||||
106, 1, 0, 0, 0, 114, 109, 1, 0, 0, 0, 115, 118, 1, 0, 0, 0, 116, 114,
|
||||
1, 0, 0, 0, 116, 117, 1, 0, 0, 0, 117, 7, 1, 0, 0, 0, 118, 116, 1, 0, 0,
|
||||
0, 119, 122, 3, 14, 7, 0, 120, 122, 3, 16, 8, 0, 121, 119, 1, 0, 0, 0,
|
||||
121, 120, 1, 0, 0, 0, 122, 9, 1, 0, 0, 0, 123, 124, 5, 69, 0, 0, 124, 126,
|
||||
5, 20, 0, 0, 125, 127, 3, 2, 1, 0, 126, 125, 1, 0, 0, 0, 126, 127, 1, 0,
|
||||
0, 0, 127, 132, 1, 0, 0, 0, 128, 129, 5, 19, 0, 0, 129, 131, 3, 2, 1, 0,
|
||||
130, 128, 1, 0, 0, 0, 131, 134, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 132,
|
||||
130, 1, 0, 0, 0, 133, 136, 1, 0, 0, 0, 134, 132, 1, 0, 0, 0, 135, 137,
|
||||
5, 19, 0, 0, 136, 135, 1, 0, 0, 0, 136, 137, 1, 0, 0, 0, 137, 138, 1, 0,
|
||||
0, 0, 138, 139, 5, 21, 0, 0, 139, 11, 1, 0, 0, 0, 140, 141, 7, 7, 0, 0,
|
||||
141, 13, 1, 0, 0, 0, 142, 144, 5, 24, 0, 0, 143, 145, 3, 2, 1, 0, 144,
|
||||
143, 1, 0, 0, 0, 144, 145, 1, 0, 0, 0, 145, 150, 1, 0, 0, 0, 146, 147,
|
||||
5, 19, 0, 0, 147, 149, 3, 2, 1, 0, 148, 146, 1, 0, 0, 0, 149, 152, 1, 0,
|
||||
0, 0, 150, 151, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 151, 154, 1, 0, 0, 0,
|
||||
152, 150, 1, 0, 0, 0, 153, 155, 5, 19, 0, 0, 154, 153, 1, 0, 0, 0, 154,
|
||||
155, 1, 0, 0, 0, 155, 156, 1, 0, 0, 0, 156, 157, 5, 25, 0, 0, 157, 15,
|
||||
1, 0, 0, 0, 158, 160, 5, 22, 0, 0, 159, 161, 3, 18, 9, 0, 160, 159, 1,
|
||||
0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 166, 1, 0, 0, 0, 162, 163, 5, 19, 0,
|
||||
0, 163, 165, 3, 18, 9, 0, 164, 162, 1, 0, 0, 0, 165, 168, 1, 0, 0, 0, 166,
|
||||
164, 1, 0, 0, 0, 166, 167, 1, 0, 0, 0, 167, 170, 1, 0, 0, 0, 168, 166,
|
||||
1, 0, 0, 0, 169, 171, 5, 19, 0, 0, 170, 169, 1, 0, 0, 0, 170, 171, 1, 0,
|
||||
0, 0, 171, 172, 1, 0, 0, 0, 172, 173, 5, 23, 0, 0, 173, 17, 1, 0, 0, 0,
|
||||
174, 186, 5, 69, 0, 0, 175, 176, 3, 20, 10, 0, 176, 177, 5, 16, 0, 0, 177,
|
||||
178, 3, 2, 1, 0, 178, 186, 1, 0, 0, 0, 179, 180, 5, 24, 0, 0, 180, 181,
|
||||
3, 2, 1, 0, 181, 182, 5, 25, 0, 0, 182, 183, 5, 16, 0, 0, 183, 184, 3,
|
||||
2, 1, 0, 184, 186, 1, 0, 0, 0, 185, 174, 1, 0, 0, 0, 185, 175, 1, 0, 0,
|
||||
0, 185, 179, 1, 0, 0, 0, 186, 19, 1, 0, 0, 0, 187, 188, 7, 8, 0, 0, 188,
|
||||
21, 1, 0, 0, 0, 20, 29, 45, 63, 83, 85, 94, 104, 114, 116, 121, 126, 132,
|
||||
136, 144, 150, 154, 160, 166, 170, 185,
|
||||
}
|
||||
deserializer := antlr.NewATNDeserializer(nil)
|
||||
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
|
||||
atn := staticData.atn
|
||||
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
|
||||
decisionToDFA := staticData.decisionToDFA
|
||||
for index, state := range atn.DecisionToState {
|
||||
decisionToDFA[index] = antlr.NewDFA(state, index)
|
||||
}
|
||||
}
|
||||
|
||||
// CAQLParserInit initializes any static state used to implement CAQLParser. By default the
|
||||
// static state used to implement the parser is lazily initialized during the first call to
|
||||
// NewCAQLParser(). You can call this function if you wish to initialize the static state ahead
|
||||
// of time.
|
||||
func CAQLParserInit() {
|
||||
staticData := &caqlparserParserStaticData
|
||||
staticData.once.Do(caqlparserParserInit)
|
||||
}
|
||||
|
||||
// NewCAQLParser produces a new parser instance for the optional input antlr.TokenStream.
|
||||
func NewCAQLParser(input antlr.TokenStream) *CAQLParser {
|
||||
CAQLParserInit()
|
||||
this := new(CAQLParser)
|
||||
this.BaseParser = antlr.NewBaseParser(input)
|
||||
staticData := &caqlparserParserStaticData
|
||||
this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.predictionContextCache)
|
||||
this.RuleNames = staticData.ruleNames
|
||||
this.LiteralNames = staticData.literalNames
|
||||
this.SymbolicNames = staticData.symbolicNames
|
||||
this.GrammarFileName = "CAQLParser.g4"
|
||||
|
||||
return this
|
||||
@@ -298,7 +317,13 @@ func NewParseContext(parser antlr.Parser, parent antlr.ParserRuleContext, invoki
|
||||
func (s *ParseContext) GetParser() antlr.Parser { return s.parser }
|
||||
|
||||
func (s *ParseContext) Expression() IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -417,7 +442,13 @@ func (s *ExpressionContext) GetEq_op() antlr.Token { return s.eq_op }
|
||||
func (s *ExpressionContext) SetEq_op(v antlr.Token) { s.eq_op = v }
|
||||
|
||||
func (s *ExpressionContext) Value_literal() IValue_literalContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IValue_literalContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IValue_literalContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -427,7 +458,13 @@ func (s *ExpressionContext) Value_literal() IValue_literalContext {
|
||||
}
|
||||
|
||||
func (s *ExpressionContext) Reference() IReferenceContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IReferenceContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IReferenceContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -437,7 +474,13 @@ func (s *ExpressionContext) Reference() IReferenceContext {
|
||||
}
|
||||
|
||||
func (s *ExpressionContext) Operator_unary() IOperator_unaryContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IOperator_unaryContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IOperator_unaryContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -447,12 +490,20 @@ func (s *ExpressionContext) Operator_unary() IOperator_unaryContext {
|
||||
}
|
||||
|
||||
func (s *ExpressionContext) AllExpression() []IExpressionContext {
|
||||
var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem())
|
||||
var tst = make([]IExpressionContext, len(ts))
|
||||
children := s.GetChildren()
|
||||
len := 0
|
||||
for _, ctx := range children {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
len++
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range ts {
|
||||
if t != nil {
|
||||
tst := make([]IExpressionContext, len)
|
||||
i := 0
|
||||
for _, ctx := range children {
|
||||
if t, ok := ctx.(IExpressionContext); ok {
|
||||
tst[i] = t.(IExpressionContext)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +511,17 @@ func (s *ExpressionContext) AllExpression() []IExpressionContext {
|
||||
}
|
||||
|
||||
func (s *ExpressionContext) Expression(i int) IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i)
|
||||
var t antlr.RuleContext
|
||||
j := 0
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
if j == i {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1044,7 +1105,13 @@ func (s *Operator_unaryContext) T_PLUS() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *Operator_unaryContext) Expression() IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1189,7 +1256,13 @@ func (s *ReferenceContext) T_STRING() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *ReferenceContext) Compound_value() ICompound_valueContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*ICompound_valueContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(ICompound_valueContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1199,7 +1272,13 @@ func (s *ReferenceContext) Compound_value() ICompound_valueContext {
|
||||
}
|
||||
|
||||
func (s *ReferenceContext) Function_call() IFunction_callContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IFunction_callContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IFunction_callContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1213,7 +1292,13 @@ func (s *ReferenceContext) T_OPEN() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *ReferenceContext) Expression() IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1227,7 +1312,13 @@ func (s *ReferenceContext) T_CLOSE() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *ReferenceContext) Reference() IReferenceContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IReferenceContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IReferenceContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1441,7 +1532,13 @@ func NewCompound_valueContext(parser antlr.Parser, parent antlr.ParserRuleContex
|
||||
func (s *Compound_valueContext) GetParser() antlr.Parser { return s.parser }
|
||||
|
||||
func (s *Compound_valueContext) Array() IArrayContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IArrayContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IArrayContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1451,7 +1548,13 @@ func (s *Compound_valueContext) Array() IArrayContext {
|
||||
}
|
||||
|
||||
func (s *Compound_valueContext) Object() IObjectContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IObjectContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IObjectContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1578,12 +1681,20 @@ func (s *Function_callContext) T_CLOSE() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *Function_callContext) AllExpression() []IExpressionContext {
|
||||
var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem())
|
||||
var tst = make([]IExpressionContext, len(ts))
|
||||
children := s.GetChildren()
|
||||
len := 0
|
||||
for _, ctx := range children {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
len++
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range ts {
|
||||
if t != nil {
|
||||
tst := make([]IExpressionContext, len)
|
||||
i := 0
|
||||
for _, ctx := range children {
|
||||
if t, ok := ctx.(IExpressionContext); ok {
|
||||
tst[i] = t.(IExpressionContext)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1591,7 +1702,17 @@ func (s *Function_callContext) AllExpression() []IExpressionContext {
|
||||
}
|
||||
|
||||
func (s *Function_callContext) Expression(i int) IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i)
|
||||
var t antlr.RuleContext
|
||||
j := 0
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
if j == i {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -1882,12 +2003,20 @@ func (s *ArrayContext) T_ARRAY_CLOSE() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *ArrayContext) AllExpression() []IExpressionContext {
|
||||
var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem())
|
||||
var tst = make([]IExpressionContext, len(ts))
|
||||
children := s.GetChildren()
|
||||
len := 0
|
||||
for _, ctx := range children {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
len++
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range ts {
|
||||
if t != nil {
|
||||
tst := make([]IExpressionContext, len)
|
||||
i := 0
|
||||
for _, ctx := range children {
|
||||
if t, ok := ctx.(IExpressionContext); ok {
|
||||
tst[i] = t.(IExpressionContext)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1895,7 +2024,17 @@ func (s *ArrayContext) AllExpression() []IExpressionContext {
|
||||
}
|
||||
|
||||
func (s *ArrayContext) Expression(i int) IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i)
|
||||
var t antlr.RuleContext
|
||||
j := 0
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
if j == i {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -2060,12 +2199,20 @@ func (s *ObjectContext) T_OBJECT_CLOSE() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *ObjectContext) AllObject_element() []IObject_elementContext {
|
||||
var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IObject_elementContext)(nil)).Elem())
|
||||
var tst = make([]IObject_elementContext, len(ts))
|
||||
children := s.GetChildren()
|
||||
len := 0
|
||||
for _, ctx := range children {
|
||||
if _, ok := ctx.(IObject_elementContext); ok {
|
||||
len++
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range ts {
|
||||
if t != nil {
|
||||
tst := make([]IObject_elementContext, len)
|
||||
i := 0
|
||||
for _, ctx := range children {
|
||||
if t, ok := ctx.(IObject_elementContext); ok {
|
||||
tst[i] = t.(IObject_elementContext)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2073,7 +2220,17 @@ func (s *ObjectContext) AllObject_element() []IObject_elementContext {
|
||||
}
|
||||
|
||||
func (s *ObjectContext) Object_element(i int) IObject_elementContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IObject_elementContext)(nil)).Elem(), i)
|
||||
var t antlr.RuleContext
|
||||
j := 0
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IObject_elementContext); ok {
|
||||
if j == i {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -2234,7 +2391,13 @@ func (s *Object_elementContext) T_STRING() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *Object_elementContext) Object_element_name() IObject_element_nameContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IObject_element_nameContext)(nil)).Elem(), 0)
|
||||
var t antlr.RuleContext
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IObject_element_nameContext); ok {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
@@ -2248,12 +2411,20 @@ func (s *Object_elementContext) T_COLON() antlr.TerminalNode {
|
||||
}
|
||||
|
||||
func (s *Object_elementContext) AllExpression() []IExpressionContext {
|
||||
var ts = s.GetTypedRuleContexts(reflect.TypeOf((*IExpressionContext)(nil)).Elem())
|
||||
var tst = make([]IExpressionContext, len(ts))
|
||||
children := s.GetChildren()
|
||||
len := 0
|
||||
for _, ctx := range children {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
len++
|
||||
}
|
||||
}
|
||||
|
||||
for i, t := range ts {
|
||||
if t != nil {
|
||||
tst := make([]IExpressionContext, len)
|
||||
i := 0
|
||||
for _, ctx := range children {
|
||||
if t, ok := ctx.(IExpressionContext); ok {
|
||||
tst[i] = t.(IExpressionContext)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2261,7 +2432,17 @@ func (s *Object_elementContext) AllExpression() []IExpressionContext {
|
||||
}
|
||||
|
||||
func (s *Object_elementContext) Expression(i int) IExpressionContext {
|
||||
var t = s.GetTypedRuleContext(reflect.TypeOf((*IExpressionContext)(nil)).Elem(), i)
|
||||
var t antlr.RuleContext
|
||||
j := 0
|
||||
for _, ctx := range s.GetChildren() {
|
||||
if _, ok := ctx.(IExpressionContext); ok {
|
||||
if j == i {
|
||||
t = ctx.(antlr.RuleContext)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from CAQLParser.g4 by ANTLR 4.9.3. DO NOT EDIT.
|
||||
// Code generated from CAQLParser.g4 by ANTLR 4.10.1. DO NOT EDIT.
|
||||
|
||||
package parser // CAQLParser
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated from CAQLParser.g4 by ANTLR 4.9.3. DO NOT EDIT.
|
||||
// Code generated from CAQLParser.g4 by ANTLR 4.10.1. DO NOT EDIT.
|
||||
|
||||
package parser // CAQLParser
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1285,8 +1285,12 @@ definitions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
salt:
|
||||
type: string
|
||||
sha256:
|
||||
type: string
|
||||
sha512:
|
||||
type: string
|
||||
required:
|
||||
- blocked
|
||||
- apikey
|
||||
@@ -1334,6 +1338,8 @@ definitions:
|
||||
type: boolean
|
||||
id:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
roles:
|
||||
items:
|
||||
type: string
|
||||
@@ -1625,38 +1631,7 @@ paths:
|
||||
blocked: false
|
||||
id: bob
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- admin
|
||||
schema:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
security:
|
||||
@@ -2828,39 +2803,6 @@ paths:
|
||||
icon: mdi-check
|
||||
id: clean
|
||||
name: Clean
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
ticketTypes:
|
||||
- default_playbooks: []
|
||||
default_template: default
|
||||
@@ -2949,39 +2891,6 @@ paths:
|
||||
icon: mdi-check
|
||||
id: clean
|
||||
name: Clean
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
ticketTypes:
|
||||
- default_playbooks: []
|
||||
default_template: default
|
||||
@@ -7354,62 +7263,12 @@ paths:
|
||||
blocked: false
|
||||
id: bob
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- admin
|
||||
- apikey: true
|
||||
blocked: false
|
||||
id: script
|
||||
roles:
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- engineer
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
@@ -7443,21 +7302,7 @@ paths:
|
||||
blocked: false
|
||||
id: syncscript
|
||||
roles:
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- analyst
|
||||
secret: v39bOuobnlEljfWzjAgoKzhmnh1xSMxH
|
||||
schema:
|
||||
$ref: '#/definitions/NewUserResponse'
|
||||
@@ -7504,26 +7349,7 @@ paths:
|
||||
blocked: false
|
||||
id: script
|
||||
roles:
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- engineer
|
||||
schema:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
security:
|
||||
@@ -7563,38 +7389,8 @@ paths:
|
||||
blocked: false
|
||||
id: bob
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- analyst
|
||||
- admin
|
||||
schema:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
security:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1166,8 +1166,12 @@ definitions:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
salt:
|
||||
type: string
|
||||
sha256:
|
||||
type: string
|
||||
sha512:
|
||||
type: string
|
||||
required:
|
||||
- blocked
|
||||
- apikey
|
||||
@@ -1215,6 +1219,8 @@ definitions:
|
||||
type: boolean
|
||||
id:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
roles:
|
||||
items:
|
||||
type: string
|
||||
@@ -1506,38 +1512,7 @@ paths:
|
||||
blocked: false
|
||||
id: bob
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- admin
|
||||
schema:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
security:
|
||||
@@ -2416,39 +2391,6 @@ paths:
|
||||
icon: mdi-check
|
||||
id: clean
|
||||
name: Clean
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
ticketTypes:
|
||||
- default_playbooks: []
|
||||
default_template: default
|
||||
@@ -2537,39 +2479,6 @@ paths:
|
||||
icon: mdi-check
|
||||
id: clean
|
||||
name: Clean
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
ticketTypes:
|
||||
- default_playbooks: []
|
||||
default_template: default
|
||||
@@ -6942,62 +6851,12 @@ paths:
|
||||
blocked: false
|
||||
id: bob
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- admin
|
||||
- apikey: true
|
||||
blocked: false
|
||||
id: script
|
||||
roles:
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- engineer
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
@@ -7031,21 +6890,7 @@ paths:
|
||||
blocked: false
|
||||
id: syncscript
|
||||
roles:
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- analyst
|
||||
secret: v39bOuobnlEljfWzjAgoKzhmnh1xSMxH
|
||||
schema:
|
||||
$ref: '#/definitions/NewUserResponse'
|
||||
@@ -7092,26 +6937,7 @@ paths:
|
||||
blocked: false
|
||||
id: script
|
||||
roles:
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- engineer
|
||||
schema:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
security:
|
||||
@@ -7151,38 +6977,8 @@ paths:
|
||||
blocked: false
|
||||
id: bob
|
||||
roles:
|
||||
- admin:backup:read
|
||||
- admin:backup:restore
|
||||
- admin:dashboard:write
|
||||
- admin:group:write
|
||||
- admin:job:read
|
||||
- admin:job:write
|
||||
- admin:log:read
|
||||
- admin:settings:write
|
||||
- admin:ticket:delete
|
||||
- admin:user:write
|
||||
- admin:userdata:read
|
||||
- admin:userdata:write
|
||||
- analyst:automation:read
|
||||
- analyst:currentsettings:write
|
||||
- analyst:currentuser:read
|
||||
- analyst:currentuserdata:read
|
||||
- analyst:dashboard:read
|
||||
- analyst:file
|
||||
- analyst:group:read
|
||||
- analyst:playbook:read
|
||||
- analyst:rule:read
|
||||
- analyst:settings:read
|
||||
- analyst:template:read
|
||||
- analyst:ticket:read
|
||||
- analyst:ticket:write
|
||||
- analyst:tickettype:read
|
||||
- analyst:user:read
|
||||
- engineer:automation:write
|
||||
- engineer:playbook:write
|
||||
- engineer:rule:write
|
||||
- engineer:template:write
|
||||
- engineer:tickettype:write
|
||||
- analyst
|
||||
- admin
|
||||
schema:
|
||||
$ref: '#/definitions/UserResponse'
|
||||
security:
|
||||
|
||||
@@ -116,10 +116,10 @@ func init() {
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"default_groups":{"items":{"type":"string"},"type":"array"},"default_playbooks":{"items":{"type":"string"},"type":"array"},"default_template":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name","icon","default_template","default_playbooks"],"$id":"#/definitions/TicketTypeResponse"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifacts":{"items":{"$ref":"#/definitions/Artifact"},"type":"array"},"comments":{"items":{"$ref":"#/definitions/Comment"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"type":"object"},"files":{"items":{"$ref":"#/definitions/File"},"type":"array"},"id":{"format":"int64","type":"integer"},"logs":{"items":{"$ref":"#/definitions/LogEntry"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"playbooks":{"type":"object","additionalProperties":{"$ref":"#/definitions/PlaybookResponse"}},"read":{"items":{"type":"string"},"type":"array"},"references":{"items":{"$ref":"#/definitions/Reference"},"type":"array"},"schema":{"type":"string"},"status":{"type":"string"},"tickets":{"items":{"$ref":"#/definitions/TicketSimpleResponse"},"type":"array"},"type":{"type":"string"},"write":{"items":{"type":"string"},"type":"array"}},"required":["id","name","type","status","created","modified","schema"],"$id":"#/definitions/TicketWithTickets"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"],"$id":"#/definitions/Type"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"sha256":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"roles":{"items":{"type":"string"},"type":"array"},"salt":{"type":"string"},"sha256":{"type":"string"},"sha512":{"type":"string"}},"required":["blocked","apikey","roles"],"$id":"#/definitions/User"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"$id":"#/definitions/UserData"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"email":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"name":{"type":"string"},"timeformat":{"title":"Time Format (https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens)","type":"string"}},"required":["id"],"$id":"#/definitions/UserDataResponse"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"password":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserForm"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"apikey":{"type":"boolean"},"blocked":{"type":"boolean"},"id":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"}},"required":["id","blocked","roles","apikey"],"$id":"#/definitions/UserResponse"}`),
|
||||
gojsonschema.NewStringLoader(`{"type":"object","properties":{"aggregation":{"type":"string"},"filter":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["bar","line","pie"]},"width":{"maximum":12,"type":"integer"}},"required":["name","type","aggregation","width"],"$id":"#/definitions/Widget"}`),
|
||||
)
|
||||
@@ -588,7 +588,9 @@ type User struct {
|
||||
Apikey bool `json:"apikey"`
|
||||
Blocked bool `json:"blocked"`
|
||||
Roles []string `json:"roles"`
|
||||
Salt *string `json:"salt,omitempty"`
|
||||
Sha256 *string `json:"sha256,omitempty"`
|
||||
Sha512 *string `json:"sha512,omitempty"`
|
||||
}
|
||||
|
||||
type UserData struct {
|
||||
@@ -607,10 +609,11 @@ type UserDataResponse struct {
|
||||
}
|
||||
|
||||
type UserForm struct {
|
||||
Apikey bool `json:"apikey"`
|
||||
Blocked bool `json:"blocked"`
|
||||
ID string `json:"id"`
|
||||
Roles []string `json:"roles"`
|
||||
Apikey bool `json:"apikey"`
|
||||
Blocked bool `json:"blocked"`
|
||||
ID string `json:"id"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
|
||||
113
go.mod
113
go.mod
@@ -1,53 +1,90 @@
|
||||
module github.com/SecurityBrewery/catalyst
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
replace github.com/xeipuuv/gojsonschema => github.com/warjiang/gojsonschema v1.2.1-0.20210329105853-aa9f9a8cfec7
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kong v0.6.1
|
||||
github.com/alecthomas/kong-yaml v0.1.1
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220816024939-bc8df83d7b9d
|
||||
github.com/arangodb/go-driver v1.3.3
|
||||
github.com/aws/aws-sdk-go v1.44.109
|
||||
github.com/blevesearch/bleve/v2 v2.3.2
|
||||
github.com/coreos/go-oidc/v3 v3.4.0
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20201201034508-7d75c1d40d88+incompatible
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/gobwas/ws v1.1.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845
|
||||
github.com/imdario/mergo v0.3.13
|
||||
github.com/jonas-plum/maut v0.0.0-20221001191853-1856efe6da0b
|
||||
github.com/mingrammer/commonregex v1.0.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tidwall/gjson v1.14.3
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
github.com/tus/tusd v1.9.2
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/alecthomas/kong v0.2.17
|
||||
github.com/alecthomas/kong-yaml v0.1.1
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211101200231-0802afb9c160
|
||||
github.com/arangodb/go-driver v1.2.1
|
||||
github.com/aws/aws-sdk-go v1.41.19
|
||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.1 // indirect
|
||||
github.com/blevesearch/bleve/v2 v2.2.2
|
||||
github.com/blevesearch/bleve_index_api v1.0.1 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.3 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.0 // indirect
|
||||
github.com/blevesearch/segment v0.9.0 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
|
||||
github.com/blevesearch/vellum v1.0.7 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.3 // indirect
|
||||
github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f // indirect
|
||||
github.com/containerd/containerd v1.5.8 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.1.0
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20201201034508-7d75c1d40d88+incompatible
|
||||
github.com/containerd/containerd v1.6.8 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/eclipse/paho.mqtt.golang v1.3.5 // indirect
|
||||
github.com/emitter-io/go/v2 v2.0.9
|
||||
github.com/go-chi/chi v1.5.4
|
||||
github.com/go-chi/cors v1.2.0
|
||||
github.com/gobwas/ws v1.1.0
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/icza/dyno v0.0.0-20210726202311-f1bafe5d9996
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/mingrammer/commonregex v1.0.1
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tidwall/gjson v1.12.1
|
||||
github.com/tidwall/sjson v1.2.4
|
||||
github.com/tus/tusd v1.8.0
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211105192438-b53810dc28af // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220810155839-1856144b1d9c // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
)
|
||||
|
||||
replace github.com/xeipuuv/gojsonschema => github.com/warjiang/gojsonschema v1.2.1-0.20210329105853-aa9f9a8cfec7
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
type Hooks struct {
|
||||
DatabaseAfterConnectFuncs []func(ctx context.Context, client driver.Client, name string)
|
||||
IngestionFilterFunc func(ctx context.Context, index *index.Index) (string, error)
|
||||
TicketReadFilterFunc func(ctx context.Context) (string, map[string]interface{}, error)
|
||||
TicketWriteFilterFunc func(ctx context.Context) (string, map[string]interface{}, error)
|
||||
TicketReadFilterFunc func(ctx context.Context) (string, map[string]any, error)
|
||||
TicketWriteFilterFunc func(ctx context.Context) (string, map[string]any, error)
|
||||
GetGroupsFunc func(ctx context.Context, username string) ([]string, error)
|
||||
}
|
||||
|
||||
@@ -26,20 +26,23 @@ func (h *Hooks) IngestionFilter(ctx context.Context, index *index.Index) (string
|
||||
if h.IngestionFilterFunc != nil {
|
||||
return h.IngestionFilterFunc(ctx, index)
|
||||
}
|
||||
|
||||
return "[]", nil
|
||||
}
|
||||
|
||||
func (h *Hooks) TicketReadFilter(ctx context.Context) (string, map[string]interface{}, error) {
|
||||
func (h *Hooks) TicketReadFilter(ctx context.Context) (string, map[string]any, error) {
|
||||
if h.TicketReadFilterFunc != nil {
|
||||
return h.TicketReadFilterFunc(ctx)
|
||||
}
|
||||
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
func (h *Hooks) TicketWriteFilter(ctx context.Context) (string, map[string]interface{}, error) {
|
||||
func (h *Hooks) TicketWriteFilter(ctx context.Context) (string, map[string]any, error) {
|
||||
if h.TicketWriteFilterFunc != nil {
|
||||
return h.TicketWriteFilterFunc(ctx)
|
||||
}
|
||||
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
@@ -47,5 +50,6 @@ func (h *Hooks) GetGroups(ctx context.Context, username string) ([]string, error
|
||||
if h.GetGroupsFunc != nil {
|
||||
return h.GetGroupsFunc(ctx, username)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ func (i *Index) Index(incidents []*model.TicketSimpleResponse) {
|
||||
for _, incident := range incidents {
|
||||
if incident.ID == 0 {
|
||||
log.Println(errors.New("no ID"), incident)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -44,8 +45,8 @@ func (i *Index) Index(incidents []*model.TicketSimpleResponse) {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
err := i.internal.Batch(b)
|
||||
if err != nil {
|
||||
|
||||
if err := i.internal.Batch(b); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
@@ -59,6 +60,7 @@ func (i *Index) Search(term string) (ids []string, err error) {
|
||||
for _, match := range result.Hits {
|
||||
ids = append(ids, match.ID)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
@@ -76,6 +78,7 @@ func (i *Index) Truncate() error {
|
||||
return err
|
||||
}
|
||||
i.internal = index
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
)
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type args struct {
|
||||
term string
|
||||
}
|
||||
@@ -22,7 +24,10 @@ func TestIndex(t *testing.T) {
|
||||
{name: "Not exists", args: args{"bar"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
i, cleanup, err := test.Index(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -37,6 +42,7 @@ func TestIndex(t *testing.T) {
|
||||
gotIds, err := i.Search(tt.args.term)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Search() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotIds, tt.wantIds) {
|
||||
@@ -47,6 +53,8 @@ func TestIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIndex_Truncate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
wantErr bool
|
||||
@@ -54,7 +62,10 @@ func TestIndex_Truncate(t *testing.T) {
|
||||
{name: "Truncate"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
i, cleanup, err := test.Index(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
18
restore.go
18
restore.go
@@ -29,11 +29,13 @@ func restoreHandler(catalystStorage *storage.Storage, db *database.Database, c *
|
||||
uf, header, err := r.FormFile("backup")
|
||||
if err != nil {
|
||||
api.JSONError(w, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err = Restore(r.Context(), catalystStorage, db, c, uf, header.Size); err != nil {
|
||||
api.JSONError(w, err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -52,7 +54,7 @@ func Restore(ctx context.Context, catalystStorage *storage.Storage, db *database
|
||||
}
|
||||
|
||||
if fsys.Comment != GetVersion() {
|
||||
return errors.New(fmt.Sprintf("wrong version, got: %s, want: %s", fsys.Comment, GetVersion()))
|
||||
return fmt.Errorf("wrong version, got: %s, want: %s", fsys.Comment, GetVersion())
|
||||
}
|
||||
|
||||
dir, err := os.MkdirTemp("", "catalyst-restore")
|
||||
@@ -89,17 +91,19 @@ func restoreS3(catalystStorage *storage.Storage, p string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreBucket(catalystStorage *storage.Storage, entry fs.DirEntry, minioDir fs.FS) error {
|
||||
_, err := catalystStorage.S3().CreateBucket(&s3.CreateBucketInput{Bucket: pointer.String(entry.Name())})
|
||||
if err != nil {
|
||||
awsError, ok := err.(awserr.Error)
|
||||
if !ok || (awsError.Code() != s3.ErrCodeBucketAlreadyExists && awsError.Code() != s3.ErrCodeBucketAlreadyOwnedByYou) {
|
||||
return err
|
||||
var awsError awserr.Error
|
||||
if errors.As(err, &awsError) && (awsError.Code() == s3.ErrCodeBucketAlreadyExists || awsError.Code() == s3.ErrCodeBucketAlreadyOwnedByYou) {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
uploader := catalystStorage.Uploader()
|
||||
@@ -115,11 +119,13 @@ func restoreBucket(catalystStorage *storage.Storage, entry fs.DirEntry, minioDir
|
||||
return nil
|
||||
}
|
||||
_, err = uploader.Upload(&s3manager.UploadInput{Body: f, Bucket: pointer.String(entry.Name()), Key: pointer.String(path)})
|
||||
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -131,6 +137,7 @@ func unzip(archive *zip.Reader, dir string) error {
|
||||
|
||||
if d.IsDir() {
|
||||
_ = os.MkdirAll(path.Join(dir, p), os.ModePerm)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -163,5 +170,6 @@ func arangorestore(dir string, config *database.Config) error {
|
||||
"--server.database", name,
|
||||
}
|
||||
cmd := exec.Command("arangorestore", args...)
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
183
role/role.go
183
role/role.go
@@ -1,183 +0,0 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
|
||||
type Role string
|
||||
|
||||
const (
|
||||
Analyst string = "analyst"
|
||||
Engineer string = "engineer"
|
||||
Admin string = "admin"
|
||||
|
||||
AutomationRead Role = "analyst:automation:read"
|
||||
CurrentuserRead Role = "analyst:currentuser:read"
|
||||
CurrentuserdataRead Role = "analyst:currentuserdata:read"
|
||||
CurrentuserdataWrite Role = "analyst:currentsettings:write"
|
||||
DashboardRead Role = "analyst:dashboard:read"
|
||||
FileReadWrite Role = "analyst:file"
|
||||
GroupRead Role = "analyst:group:read"
|
||||
PlaybookRead Role = "analyst:playbook:read"
|
||||
RuleRead Role = "analyst:rule:read"
|
||||
SettingsRead Role = "analyst:settings:read"
|
||||
TemplateRead Role = "analyst:template:read"
|
||||
TicketRead Role = "analyst:ticket:read"
|
||||
TicketWrite Role = "analyst:ticket:write"
|
||||
TickettypeRead Role = "analyst:tickettype:read"
|
||||
UserRead Role = "analyst:user:read"
|
||||
|
||||
AutomationWrite Role = "engineer:automation:write"
|
||||
PlaybookWrite Role = "engineer:playbook:write"
|
||||
RuleWrite Role = "engineer:rule:write"
|
||||
TemplateWrite Role = "engineer:template:write"
|
||||
TickettypeWrite Role = "engineer:tickettype:write"
|
||||
|
||||
BackupRead Role = "admin:backup:read"
|
||||
BackupRestore Role = "admin:backup:restore"
|
||||
DashboardWrite Role = "admin:dashboard:write"
|
||||
GroupWrite Role = "admin:group:write"
|
||||
JobRead Role = "admin:job:read"
|
||||
JobWrite Role = "admin:job:write"
|
||||
LogRead Role = "admin:log:read"
|
||||
SettingsWrite Role = "admin:settings:write"
|
||||
TicketDelete Role = "admin:ticket:delete"
|
||||
UserWrite Role = "admin:user:write"
|
||||
UserdataRead Role = "admin:userdata:read"
|
||||
UserdataWrite Role = "admin:userdata:write"
|
||||
)
|
||||
|
||||
func (p Role) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func UserHasRoles(user *model.UserResponse, roles []Role) bool {
|
||||
hasRoles := true
|
||||
for _, role := range roles {
|
||||
if !UserHasRole(user, role) {
|
||||
hasRoles = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return hasRoles
|
||||
}
|
||||
|
||||
func UserHasRole(user *model.UserResponse, role Role) bool {
|
||||
return ContainsRole(FromStrings(user.Roles), role)
|
||||
}
|
||||
|
||||
func ContainsRole(roles []Role, role Role) bool {
|
||||
for _, r := range roles {
|
||||
if r.String() == role.String() { // || strings.HasPrefix(role.String(), r.String()+":")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Explodes(s []string) []Role {
|
||||
var roles []Role
|
||||
for _, e := range s {
|
||||
roles = append(roles, Explode(e)...)
|
||||
}
|
||||
roles = unique(roles)
|
||||
sort.Slice(roles, func(i, j int) bool {
|
||||
return roles[i].String() < roles[j].String()
|
||||
})
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func Explode(s string) []Role {
|
||||
var roles []Role
|
||||
|
||||
switch s {
|
||||
case Admin:
|
||||
roles = append(roles, listPrefix(Admin)...)
|
||||
fallthrough
|
||||
case Engineer:
|
||||
roles = append(roles, listPrefix(Engineer)...)
|
||||
fallthrough
|
||||
case Analyst:
|
||||
roles = append(roles, listPrefix(Analyst)...)
|
||||
return roles
|
||||
}
|
||||
|
||||
for _, role := range List() {
|
||||
if role.String() == s {
|
||||
roles = append(roles, role)
|
||||
}
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func listPrefix(s string) []Role {
|
||||
var roles []Role
|
||||
|
||||
for _, role := range List() {
|
||||
if strings.HasPrefix(role.String(), s+":") {
|
||||
roles = append(roles, role)
|
||||
}
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func unique(l []Role) []Role {
|
||||
keys := make(map[Role]bool)
|
||||
var list []Role
|
||||
for _, entry := range l {
|
||||
if _, value := keys[entry]; !value {
|
||||
keys[entry] = true
|
||||
list = append(list, entry)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func List() []Role {
|
||||
return []Role{
|
||||
AutomationRead, CurrentuserdataRead, CurrentuserdataWrite,
|
||||
CurrentuserRead, FileReadWrite, GroupRead, PlaybookRead, RuleRead,
|
||||
UserdataRead, SettingsRead, TemplateRead, TicketRead, TickettypeRead,
|
||||
TicketWrite, UserRead, AutomationWrite, PlaybookWrite, RuleWrite,
|
||||
TemplateWrite, TickettypeWrite, BackupRead, BackupRestore, GroupWrite,
|
||||
LogRead, UserdataWrite, TicketDelete, UserWrite, JobRead, JobWrite,
|
||||
SettingsWrite, DashboardRead, DashboardWrite,
|
||||
}
|
||||
}
|
||||
|
||||
func fromString(s string) (Role, error) {
|
||||
for _, role := range List() {
|
||||
if role.String() == s {
|
||||
return role, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("unknown role")
|
||||
}
|
||||
|
||||
func Strings(roles []Role) []string {
|
||||
var s []string
|
||||
for _, role := range roles {
|
||||
s = append(s, role.String())
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func FromStrings(s []string) []Role {
|
||||
var roles []Role
|
||||
for _, e := range s {
|
||||
role, err := fromString(e)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
roles = append(roles, role)
|
||||
}
|
||||
return roles
|
||||
}
|
||||
50
roles.go
Normal file
50
roles.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package catalyst
|
||||
|
||||
import maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
var Admin = &maut.Role{
|
||||
Name: "admin",
|
||||
Permissions: append(engineer.Permissions,
|
||||
"backup:create",
|
||||
"backup:restore",
|
||||
"dashboard:write",
|
||||
"job:read",
|
||||
"job:write",
|
||||
"log:read",
|
||||
"settings:write",
|
||||
"ticket:delete",
|
||||
"user:write",
|
||||
"userdata:write",
|
||||
),
|
||||
}
|
||||
|
||||
var engineer = &maut.Role{
|
||||
Name: "engineer",
|
||||
Permissions: append(analyst.Permissions,
|
||||
"automation:write",
|
||||
"playbook:write",
|
||||
"template:write",
|
||||
"tickettype:write",
|
||||
),
|
||||
}
|
||||
|
||||
var analyst = &maut.Role{
|
||||
Name: "analyst",
|
||||
Permissions: []string{
|
||||
"automation:read",
|
||||
"currentuser:read",
|
||||
"currentuserdata:read",
|
||||
"currentuserdata:write",
|
||||
"dashboard:read",
|
||||
"file:read",
|
||||
"file:write",
|
||||
"playbook:read",
|
||||
"settings:read",
|
||||
"template:read",
|
||||
"ticket:read",
|
||||
"ticket:write",
|
||||
"tickettype:read",
|
||||
"user:read",
|
||||
"userdata:read",
|
||||
},
|
||||
}
|
||||
115
server.go
115
server.go
@@ -2,39 +2,33 @@ package catalyst
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
maut "github.com/jonas-plum/maut/auth"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/busservice"
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/database/busdb"
|
||||
"github.com/SecurityBrewery/catalyst/generated/api"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
"github.com/SecurityBrewery/catalyst/hooks"
|
||||
"github.com/SecurityBrewery/catalyst/index"
|
||||
"github.com/SecurityBrewery/catalyst/role"
|
||||
"github.com/SecurityBrewery/catalyst/service"
|
||||
"github.com/SecurityBrewery/catalyst/storage"
|
||||
"github.com/SecurityBrewery/catalyst/ui"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
IndexPath string
|
||||
DB *database.Config
|
||||
Storage *storage.Config
|
||||
Bus *bus.Config
|
||||
|
||||
Secret []byte
|
||||
Auth *AuthConfig
|
||||
Auth *maut.Config
|
||||
ExternalAddress string
|
||||
InitialAPIKey string
|
||||
InternalAddress string
|
||||
Network string
|
||||
Port int
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
@@ -47,14 +41,9 @@ type Server struct {
|
||||
|
||||
func New(hooks *hooks.Hooks, config *Config) (*Server, error) {
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute*10)
|
||||
defer cancel()
|
||||
|
||||
err := config.Auth.Load(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
catalystStorage, err := storage.New(config.Storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -65,42 +54,26 @@ func New(hooks *hooks.Hooks, config *Config) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
catalystBus, err := bus.New(config.Bus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
catalystBus := bus.New()
|
||||
|
||||
catalystDatabase, err := database.New(ctx, catalystIndex, catalystBus, hooks, config.DB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = busservice.New(config.Bus.APIUrl, config.InitialAPIKey, config.Network, catalystBus, catalystDatabase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
busservice.New(config.InternalAddress+"/api", config.Auth.InitialAPIKey, config.Network, catalystBus, catalystDatabase)
|
||||
|
||||
catalystService, err := service.New(catalystBus, catalystDatabase, catalystStorage, GetVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.InitialAPIKey != "" {
|
||||
_ = catalystDatabase.UserDelete(ctx, "setup")
|
||||
|
||||
ctx = busdb.UserContext(ctx, &model.UserResponse{
|
||||
ID: "setup",
|
||||
Roles: role.Strings(role.Explode(role.Admin)),
|
||||
Apikey: false,
|
||||
Blocked: false,
|
||||
})
|
||||
_, err = catalystDatabase.UserCreateSetupAPIKey(ctx, config.InitialAPIKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authenticator, err := maut.NewAuthenticator(ctx, config.Auth, newCatalystResolver(catalystDatabase))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiServer, err := setupAPI(catalystService, catalystStorage, catalystDatabase, config.DB, catalystBus, config)
|
||||
apiServer, err := setupAPI(authenticator, catalystService, catalystStorage, catalystDatabase, config.DB, catalystBus, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -114,34 +87,54 @@ func New(hooks *hooks.Hooks, config *Config) (*Server, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func setupAPI(catalystService *service.Service, catalystStorage *storage.Storage, catalystDatabase *database.Database, dbConfig *database.Config, bus *bus.Bus, config *Config) (chi.Router, error) {
|
||||
middlewares := []func(next http.Handler) http.Handler{Authenticate(catalystDatabase, config.Auth), AuthorizeBlockedUser()}
|
||||
func setupAPI(authenticator *maut.Authenticator, catalystService *service.Service, catalystStorage *storage.Storage, catalystDatabase *database.Database, dbConfig *database.Config, bus *bus.Bus, config *Config) (chi.Router, error) {
|
||||
middlewares := []func(next http.Handler) http.Handler{
|
||||
authenticator.Authenticate(),
|
||||
authenticator.AuthorizeBlockedUser(),
|
||||
}
|
||||
|
||||
// create server
|
||||
apiServerMiddleware := []func(next http.Handler) http.Handler{cors.AllowAll().Handler}
|
||||
apiServerMiddleware = append(apiServerMiddleware, middlewares...)
|
||||
apiServer := api.NewServer(catalystService, AuthorizeRole, apiServerMiddleware...)
|
||||
|
||||
fileReadWrite := AuthorizeRole([]string{role.FileReadWrite.String()})
|
||||
tudHandler := tusdUpload(catalystDatabase, bus, catalystStorage.S3(), config.ExternalAddress)
|
||||
apiServer.With(fileReadWrite).Head("/files/{ticketID}/tusd/{id}", tudHandler)
|
||||
apiServer.With(fileReadWrite).Patch("/files/{ticketID}/tusd/{id}", tudHandler)
|
||||
apiServer.With(fileReadWrite).Post("/files/{ticketID}/tusd", tudHandler)
|
||||
apiServer.With(fileReadWrite).Post("/files/{ticketID}/upload", upload(catalystDatabase, catalystStorage.S3(), catalystStorage.Uploader()))
|
||||
apiServer.With(fileReadWrite).Get("/files/{ticketID}/download/{key}", download(catalystStorage.Downloader()))
|
||||
|
||||
apiServer.With(AuthorizeRole([]string{role.BackupRead.String()})).Get("/backup/create", backupHandler(catalystStorage, dbConfig))
|
||||
apiServer.With(AuthorizeRole([]string{role.BackupRestore.String()})).Post("/backup/restore", restoreHandler(catalystStorage, catalystDatabase, dbConfig))
|
||||
apiServer := api.NewServer(catalystService, permissionAuth(authenticator), middlewares...)
|
||||
apiServer.Mount("/files", fileServer(authenticator, catalystDatabase, bus, catalystStorage, config))
|
||||
apiServer.Mount("/backup", backupServer(authenticator, catalystStorage, catalystDatabase, dbConfig))
|
||||
|
||||
server := chi.NewRouter()
|
||||
server.Use(middleware.RequestID, middleware.RealIP, middleware.Logger, middleware.Recoverer, cors.AllowAll().Handler)
|
||||
server.Use(middleware.RequestID, middleware.RealIP, middleware.Logger, middleware.Recoverer)
|
||||
server.Mount("/api", apiServer)
|
||||
|
||||
server.Get("/callback", callback(config.Auth))
|
||||
server.Mount("/auth", authenticator.Server())
|
||||
server.With(middlewares...).Handle("/wss", handleWebSocket(bus))
|
||||
|
||||
fsys, _ := fs.Sub(ui.UI, "dist")
|
||||
server.With(middlewares...).NotFound(api.VueStatic(fsys))
|
||||
server.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/ui/", http.StatusFound)
|
||||
})
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func permissionAuth(authenticator *maut.Authenticator) func([]string) func(http.Handler) http.Handler {
|
||||
return func(strings []string) func(http.Handler) http.Handler {
|
||||
return authenticator.AuthorizePermission(strings...)
|
||||
}
|
||||
}
|
||||
|
||||
func fileServer(authenticator *maut.Authenticator, catalystDatabase *database.Database, bus *bus.Bus, catalystStorage *storage.Storage, config *Config) *chi.Mux {
|
||||
fileRW := authenticator.AuthorizePermission("file:read", "file:write") // TODO: add test
|
||||
tudHandler := tusdUpload(catalystDatabase, bus, catalystStorage.S3(), config.ExternalAddress)
|
||||
server := chi.NewRouter()
|
||||
server.With(fileRW).Head("/{ticketID}/tusd/{id}", tudHandler)
|
||||
server.With(fileRW).Patch("/{ticketID}/tusd/{id}", tudHandler)
|
||||
server.With(fileRW).Post("/{ticketID}/tusd", tudHandler)
|
||||
server.With(fileRW).Post("/{ticketID}/upload", upload(catalystDatabase, catalystStorage.S3(), catalystStorage.Uploader()))
|
||||
server.With(fileRW).Get("/{ticketID}/download/{key}", download(catalystStorage.Downloader()))
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func backupServer(authenticator *maut.Authenticator, catalystStorage *storage.Storage, catalystDatabase *database.Database, dbConfig *database.Config) *chi.Mux {
|
||||
server := chi.NewRouter()
|
||||
// TODO: add test
|
||||
server.With(authenticator.AuthorizePermission("backup:create")).Get("/create", backupHandler(catalystStorage, dbConfig))
|
||||
server.With(authenticator.AuthorizePermission("backup:restore")).Post("/restore", restoreHandler(catalystStorage, catalystDatabase, dbConfig))
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ func automationResponseID(automation *model.AutomationResponse) []driver.Documen
|
||||
if automation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return automationID(automation.ID)
|
||||
}
|
||||
|
||||
@@ -27,6 +28,7 @@ func (s *Service) ListAutomations(ctx context.Context) ([]*model.AutomationRespo
|
||||
|
||||
func (s *Service) CreateAutomation(ctx context.Context, form *model.AutomationForm) (doc *model.AutomationResponse, err error) {
|
||||
defer s.publishRequest(ctx, err, "CreateAutomation", automationResponseID(doc))
|
||||
|
||||
return s.database.AutomationCreate(ctx, form)
|
||||
}
|
||||
|
||||
@@ -36,10 +38,12 @@ func (s *Service) GetAutomation(ctx context.Context, id string) (*model.Automati
|
||||
|
||||
func (s *Service) UpdateAutomation(ctx context.Context, id string, form *model.AutomationForm) (doc *model.AutomationResponse, err error) {
|
||||
defer s.publishRequest(ctx, err, "UpdateAutomation", automationResponseID(doc))
|
||||
|
||||
return s.database.AutomationUpdate(ctx, id, form)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteAutomation(ctx context.Context, id string) (err error) {
|
||||
defer s.publishRequest(ctx, err, "DeleteAutomation", automationID(id))
|
||||
|
||||
return s.database.AutomationDelete(ctx, id)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ func dashboardResponseID(doc *model.DashboardResponse) []driver.DocumentID {
|
||||
if doc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return templateID(doc.ID)
|
||||
}
|
||||
|
||||
@@ -27,6 +28,7 @@ func (s *Service) ListDashboards(ctx context.Context) ([]*model.DashboardRespons
|
||||
|
||||
func (s *Service) CreateDashboard(ctx context.Context, dashboard *model.Dashboard) (doc *model.DashboardResponse, err error) {
|
||||
defer s.publishRequest(ctx, err, "CreateDashboard", dashboardResponseID(doc))
|
||||
|
||||
return s.database.DashboardCreate(ctx, dashboard)
|
||||
}
|
||||
|
||||
@@ -36,14 +38,16 @@ func (s *Service) GetDashboard(ctx context.Context, id string) (*model.Dashboard
|
||||
|
||||
func (s *Service) UpdateDashboard(ctx context.Context, id string, form *model.Dashboard) (doc *model.DashboardResponse, err error) {
|
||||
defer s.publishRequest(ctx, err, "UpdateDashboard", dashboardResponseID(doc))
|
||||
|
||||
return s.database.DashboardUpdate(ctx, id, form)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteDashboard(ctx context.Context, id string) (err error) {
|
||||
defer s.publishRequest(ctx, err, "DeleteDashboard", dashboardID(id))
|
||||
|
||||
return s.database.DashboardDelete(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) DashboardData(ctx context.Context, aggregation string, filter *string) (map[string]interface{}, error) {
|
||||
func (s *Service) DashboardData(ctx context.Context, aggregation string, filter *string) (map[string]any, error) {
|
||||
return s.database.WidgetData(ctx, aggregation, filter)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/SecurityBrewery/catalyst/bus"
|
||||
"github.com/SecurityBrewery/catalyst/database"
|
||||
"github.com/SecurityBrewery/catalyst/generated/model"
|
||||
)
|
||||
@@ -15,6 +16,7 @@ func jobResponseID(job *model.JobResponse) []driver.DocumentID {
|
||||
if job == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return jobID(job.ID)
|
||||
}
|
||||
|
||||
@@ -31,7 +33,15 @@ func (s *Service) RunJob(ctx context.Context, form *model.JobForm) (doc *model.J
|
||||
newJobID := uuid.NewString()
|
||||
|
||||
defer s.publishRequest(ctx, err, "RunJob", jobID(newJobID))
|
||||
err = s.bus.PublishJob(newJobID, form.Automation, form.Payload, msgContext, form.Origin)
|
||||
s.bus.JobChannel.Publish(&bus.JobMsg{
|
||||
ID: newJobID,
|
||||
Automation: form.Automation,
|
||||
Origin: form.Origin,
|
||||
Message: &model.Message{
|
||||
Context: msgContext,
|
||||
Payload: form.Payload,
|
||||
},
|
||||
})
|
||||
|
||||
return &model.JobResponse{
|
||||
Automation: form.Automation,
|
||||
@@ -39,7 +49,7 @@ func (s *Service) RunJob(ctx context.Context, form *model.JobForm) (doc *model.J
|
||||
Origin: form.Origin,
|
||||
Payload: form.Payload,
|
||||
Status: "published",
|
||||
}, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetJob(ctx context.Context, id string) (*model.JobResponse, error) {
|
||||
@@ -48,5 +58,6 @@ func (s *Service) GetJob(ctx context.Context, id string) (*model.JobResponse, er
|
||||
|
||||
func (s *Service) UpdateJob(ctx context.Context, id string, job *model.JobUpdate) (doc *model.JobResponse, err error) {
|
||||
defer s.publishRequest(ctx, err, "UpdateJob", jobResponseID(doc))
|
||||
|
||||
return s.database.JobUpdate(ctx, id, job)
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ import (
|
||||
|
||||
func (s *Service) GetLogs(ctx context.Context, reference string) ([]*model.LogEntry, error) {
|
||||
id, _ := url.QueryUnescape(reference)
|
||||
|
||||
return s.database.LogList(ctx, id)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user