Compare commits
375 Commits
Author | SHA1 | Date |
---|---|---|
|
0183efd7a6 | |
|
89be587687 | |
|
944e9ca074 | |
|
898360115c | |
|
2ab209ac79 | |
|
ea767ec2d6 | |
|
4f0749ccbe | |
|
25f347b8e3 | |
|
69527a8509 | |
|
7f7d7ab4dc | |
|
8127e0d9aa | |
|
5ef8065704 | |
|
e9db75b884 | |
|
49972737cc | |
|
6bb3a921e3 | |
|
f419ff07f1 | |
|
172c4b9ffb | |
|
7f4259c78c | |
|
b7d4a85d8e | |
|
220305e7f8 | |
|
54579b175d | |
|
ed833023a1 | |
|
e4aeaa39ec | |
|
66e852304e | |
|
ed20511377 | |
|
5ece9cb846 | |
|
a5db094db2 | |
|
b577b03ccd | |
|
b74707b638 | |
|
420c4b0a5d | |
|
b6d18ce164 | |
|
4fcec60d82 | |
|
b970fce669 | |
|
5866d72253 | |
|
a14282a62a | |
|
ac30fbb0fc | |
|
89d7f242ff | |
|
247d2f1787 | |
|
d7890aa80f | |
|
fdcc9c6621 | |
|
6074a5dd00 | |
|
fa15304b93 | |
|
d9e095682b | |
|
55195a1093 | |
|
5f0ea899fd | |
|
bef5ca02b1 | |
|
faa6e482a5 | |
|
dcc7f60b7e | |
|
14c9808302 | |
|
8902dfbedc | |
|
aab8a5eb4c | |
|
25ff937cba | |
|
0573db7ea7 | |
|
26fa2653c2 | |
|
117afbfb15 | |
|
79cd2734ea | |
|
f8bf3dfb8d | |
|
1ff9c12c5d | |
|
839d650465 | |
|
0c4d175436 | |
|
72a4c14920 | |
|
5f21a1e660 | |
|
b17b093bea | |
|
2d50a1ac59 | |
|
ff85c86891 | |
|
5e1ec254a4 | |
|
b719682e7c | |
|
57ca03e134 | |
|
e1f0d646e8 | |
|
c8e145c085 | |
|
29e7130f4e | |
|
101557c658 | |
|
58e59cc016 | |
|
0d755fc8ee | |
|
e599c3ddaa | |
|
7f923343d7 | |
|
7234efd347 | |
|
1638d6784a | |
|
970eb38581 | |
|
7e83db582d | |
|
d9e4d8417e | |
|
41da06acf9 | |
|
ed7b5a8463 | |
|
a5b5866b87 | |
|
1165445571 | |
|
2bdec7eee9 | |
|
4873194142 | |
|
f8cc0a90a2 | |
|
597b8c9087 | |
|
e01444b71e | |
|
1a037586c2 | |
|
e7c18ba842 | |
|
804a4e96bc | |
|
5232f49739 | |
|
37fae3a457 | |
|
21054692c5 | |
|
4f91bc3761 | |
|
bf3039e902 | |
|
938544c7cc | |
|
273e1a146c | |
|
90a8990c2c | |
|
5f327fc737 | |
|
51786f3afe | |
|
4e7eb5e917 | |
|
5545dd4bf3 | |
|
3263df9e8a | |
|
87dc1c8d54 | |
|
a325a0ebc4 | |
|
09cbd37793 | |
|
d827c32b12 | |
|
50a66ff652 | |
|
fd9cb3f38a | |
|
3874a5b758 | |
|
3959491fa2 | |
|
69982ee29b | |
|
69cdfc5dd0 | |
|
420d2070c5 | |
|
698892dc88 | |
|
1d44031f59 | |
|
9c1f0c794b | |
|
1f3cf95afb | |
|
9437cf58b7 | |
|
86e2dd78c1 | |
|
691058da50 | |
|
9d09211678 | |
|
164e280392 | |
|
08c111a0aa | |
|
50c2ef5a6d | |
|
ff21351864 | |
|
67a19857eb | |
|
28bbf3c872 | |
|
0825a7714e | |
|
60e5bf647c | |
|
cd64d1a52a | |
|
bf3565e38b | |
|
7b562a8c59 | |
|
0eceb42eb8 | |
|
374c76faa6 | |
|
7423b8263c | |
|
462fd1f3f9 | |
|
33ad30870f | |
|
b8de057078 | |
|
bd08cea355 | |
|
0b239070fb | |
|
7503f0d628 | |
|
a02767d994 | |
|
9f4fe2598b | |
|
a00691fe8b | |
|
9d58d8b584 | |
|
fb46bb9334 | |
|
00f0bd7366 | |
|
9efc00c2a3 | |
|
9d2c93a1cd | |
|
544d32401e | |
|
16ea8cb728 | |
|
5e52f7f0b5 | |
|
17085b44c0 | |
|
902cfde41f | |
|
c7d079691b | |
|
fc80010c19 | |
|
b6c336d06a | |
|
e73efaa4ed | |
|
9297ded05f | |
|
616ddc30d8 | |
|
8dcb09cd67 | |
|
8b97d596e0 | |
|
12082b3182 | |
|
50bdd64781 | |
|
91cb1d0864 | |
|
3bcb00034f | |
|
39aaeadfe9 | |
|
1e07a4be59 | |
|
7e496f33f3 | |
|
92dc14c781 | |
|
719d78316d | |
|
dbf78d8eda | |
|
cc2aa27264 | |
|
720214ce79 | |
|
5e74572bf1 | |
|
f97e583b96 | |
|
98895fef9a | |
|
4a6f6a5994 | |
|
7b2161e8aa | |
|
fd40adbb1b | |
|
0560e1306d | |
|
33b38dfec8 | |
|
ae21ffb011 | |
|
c4d23a7575 | |
|
86762454a9 | |
|
41635b1c57 | |
|
d041c56f08 | |
|
a08fc34ff8 | |
|
4ddcd841a6 | |
|
32b16e1dc6 | |
|
d1c63b8454 | |
|
7e262852f5 | |
|
8aa70f674c | |
|
e6600c5f66 | |
|
6946c56782 | |
|
3c7c3ea8db | |
|
0eec8ed7f1 | |
|
38ddcb8117 | |
|
26a4e2fa69 | |
|
15f24bcc16 | |
|
1124ea8281 | |
|
9b91eb7056 | |
|
bd6c3cdc87 | |
|
effdddfa6d | |
|
87f06850ab | |
|
53c1165144 | |
|
f654eb62e3 | |
|
eb27f7118e | |
|
028d887b8a | |
|
1aba8365dc | |
|
99ea826d46 | |
|
f10f5a62d7 | |
|
b81630ac5c | |
|
ab4e25d41f | |
|
c08fe17823 | |
|
57790e510a | |
|
a8db83aceb | |
|
9175465c11 | |
|
8e87c1ee7c | |
|
197dd6065b | |
|
bfd5eb246b | |
|
0e84171713 | |
|
c82f7ac9e0 | |
|
e15d6700f0 | |
|
2d965b5ca2 | |
|
bcdb799d26 | |
|
529b910cb9 | |
|
b7bbc55481 | |
|
496ac7fd16 | |
|
a102b66dcf | |
|
bb29e4c24d | |
|
5267d5286f | |
|
7e6ac525d7 | |
|
53b91615bb | |
|
cea879c320 | |
|
77336cb170 | |
|
fc0b70ce19 | |
|
0048ab1f8e | |
|
36276bbf06 | |
|
43819fbe35 | |
|
c1467b704a | |
|
cc74f22cb5 | |
|
328bd2610d | |
|
8041e8ddc7 | |
|
1577937993 | |
|
12d2595660 | |
|
dfed4daff7 | |
|
5da7e55e9d | |
|
999d554db0 | |
|
d176b92e26 | |
|
ef88b13ce9 | |
|
f47f175c9c | |
|
3459e7ef8c | |
|
596394eb26 | |
|
e923bf2728 | |
|
e5b5a61bc3 | |
|
5cad842713 | |
|
7c32e784e6 | |
|
4e83a443fd | |
|
5479d12c4a | |
|
786bb52736 | |
|
bbc1dc0b42 | |
|
aaf6867762 | |
|
515090467e | |
|
6f24d44eca | |
|
731bdc4e32 | |
|
916d3d3ef0 | |
|
7226819296 | |
|
547a367990 | |
|
b1f08d4f4d | |
|
9fbbef44d2 | |
|
c7b7d29655 | |
|
6640951936 | |
|
4c13f7d933 | |
|
f05556a1b7 | |
|
d4d4b81aa8 | |
|
207676e823 | |
|
dc5c36abf3 | |
|
c73ad1c291 | |
|
8ee307664b | |
|
c7f896d6d8 | |
|
5778044ef1 | |
|
def75c22b3 | |
|
52ab1d592b | |
|
f9d5247901 | |
|
5df86aba05 | |
|
b85a60d2ac | |
|
da8ae75699 | |
|
5449439d8c | |
|
516a7a0541 | |
|
f86aad1328 | |
|
d30371d934 | |
|
f5f7ac8ef9 | |
|
a7df0a15e5 | |
|
5df01f1c38 | |
|
6cfff1688a | |
|
84b76bd47d | |
|
b2147c5623 | |
|
bc9af94721 | |
|
54877ef081 | |
|
a8a28724f3 | |
|
1ba88a9d4a | |
|
77124b385a | |
|
7e530592df | |
|
d65c8afa34 | |
|
78d6dec0e9 | |
|
852ae25509 | |
|
4fbd960852 | |
|
d4b5796564 | |
|
d23414ecc2 | |
|
7b4bedb944 | |
|
3e858b2768 | |
|
9e56c6cce6 | |
|
784d7b55fc | |
|
0aceec5301 | |
|
811307d298 | |
|
452d64f8c8 | |
|
27c171aca2 | |
|
cea8be3a8f | |
|
5d9a8d08e9 | |
|
39099d0415 | |
|
11cb8f3972 | |
|
f49fa05a25 | |
|
399af62ca8 | |
|
7a29dc1eb1 | |
|
7e12fc4c7f | |
|
52593fb9cf | |
|
7a57192588 | |
|
94680e81e4 | |
|
8f9febfe08 | |
|
6307a98f92 | |
|
e8258d6846 | |
|
faa3627879 | |
|
bb440940c9 | |
|
8b0fd42992 | |
|
d23bc6a01c | |
|
318994ac20 | |
|
803b4f5c1c | |
|
ad4ecca381 | |
|
9f0943d4f4 | |
|
162eba1a3d | |
|
11b8597eca | |
|
8ad33ad9d3 | |
|
944ba06e33 | |
|
fd2a99639d | |
|
3349d1d54a | |
|
96590d2cac | |
|
eabfc5e372 | |
|
ee5a04644f | |
|
0390cdff2c | |
|
2c5557a337 | |
|
2acc7835f3 | |
|
0bd912899b | |
|
7f2d8cd82f | |
|
8cf7902563 | |
|
997d1ab4af | |
|
11f97f6499 | |
|
304d32e371 | |
|
d4550bd747 | |
|
f2275e4910 | |
|
0fd84b718f | |
|
4bdeea3a64 | |
|
50a7ae45db | |
|
727ee8ab18 | |
|
d8a4896c58 | |
|
a0393efce5 | |
|
291cbbf7f3 | |
|
9f0e22ed35 | |
|
02a5900995 | |
|
d1b19a3435 | |
|
85e730c091 |
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
exclude_paths:
|
||||
- "chaoscenter/web/src/api/auth/**"
|
|
@ -1,7 +1,9 @@
|
|||
---
|
||||
name: "\U0001f41b Bug report"
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help improve the project
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
---
|
||||
name: "\U0001f6f8 Feature request"
|
||||
name: "\U0001F6F8 Feature request"
|
||||
about: Suggest an idea that will improve the project
|
||||
labels: enhancement
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: Organization Maintainer Request
|
||||
about: Request to become a maintianer in LitmusChaos Org
|
||||
title: "REQUEST: Promote <your-GH-handle> to maintainer for LitmusChaos"
|
||||
labels: type/maintainer-request
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### GitHub Username
|
||||
|
||||
@<your-GH-handle>
|
||||
|
||||
### Requirements
|
||||
|
||||
- [ ] I have reviewed [the community role guidelines](/community-roles.md)
|
||||
- [ ] I have [enabled 2FA on my GitHub account](https://github.com/settings/security)
|
||||
- [ ] I am an active member of 1 or more LitmusChaos subprojects for atleast the last 6 months.
|
||||
- [ ] I am an active participant in issue/PR reviews for atleast 2 subprojects and for the past 6 months.
|
||||
- [ ] I have been involved in technical and project discussions with other maintainers.
|
||||
- [ ] I have atleast one sponsor that meet the sponsor requirements listed in the community role guidelines
|
||||
- [ ] I have spoken to my sponsor ahead of this application, and they have agreed to sponsor my application
|
||||
- [ ] I understand that I can [make my membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership) if I'd like to once I am invited to the organization
|
||||
|
||||
### Sponsors
|
||||
|
||||
- @<sponsor-1>
|
||||
- @<sponsor-2>
|
||||
|
||||
### List of contributions to the LitmusChaos project
|
||||
|
||||
- PRs reviewed / authored
|
||||
- Issues responded to
|
||||
- Projects I am involved with
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
name: Organization Member Request
|
||||
about: Request membership in LitmusChaos Org
|
||||
title: "REQUEST: New member request for <your-GH-handle>"
|
||||
labels: type/member-request
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### GitHub Username
|
||||
|
||||
@<your-GH-handle>
|
||||
|
||||
### Requirements
|
||||
|
||||
- [ ] I have reviewed [the community role guidelines](/community-roles.md)
|
||||
- [ ] I have [enabled 2FA on my GitHub account](https://github.com/settings/security)
|
||||
- [ ] I am actively contributing to 1 or more LitmusChaos subprojects
|
||||
- [ ] I have atleast one sponsor that meet the sponsor requirements listed in the community role guidelines
|
||||
- [ ] I have spoken to my sponsor ahead of this application, and they have agreed to sponsor my application
|
||||
- [ ] I understand that I can [make my membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership) if I'd like to once I am invited to the organization
|
||||
|
||||
### Sponsors
|
||||
|
||||
- @<sponsor-1>
|
||||
- @<sponsor-2>
|
||||
|
||||
### List of contributions to the LitmusChaos project
|
||||
|
||||
- PRs reviewed / authored
|
||||
- Issues responded to
|
||||
- Projects I am involved with
|
|
@ -1,7 +1,10 @@
|
|||
---
|
||||
name: "\U0001F914 Question"
|
||||
about: Question not answered in our community meetings, Docs or Readme.
|
||||
labels: question
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Question
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: Organization Reviewer Request
|
||||
about: Request reviewer membership in LitmusChaos Org
|
||||
title: "REQUEST: Promote <your-GH-handle> to reviewer for LitmusChaos"
|
||||
labels: type/reviewer-request
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### GitHub Username
|
||||
|
||||
@<your-GH-handle>
|
||||
|
||||
### Requirements
|
||||
|
||||
- [ ] I have reviewed [the community role guidelines](/community-roles.md)
|
||||
- [ ] I have [enabled 2FA on my GitHub account](https://github.com/settings/security)
|
||||
- [ ] I am an active member of 1 or more LitmusChaos subprojects for atleast the last 3 months.
|
||||
- [ ] I am an active participant in issue/PR reviews for atleast 1 month.
|
||||
- [ ] I have reviewed or authored atleast 5 significant PRs.
|
||||
- [ ] I have atleast one sponsor that meet the sponsor requirements listed in the community role guidelines
|
||||
- [ ] I have spoken to my sponsor ahead of this application, and they have agreed to sponsor my application
|
||||
- [ ] I understand that I can [make my membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership) if I'd like to once I am invited to the organization
|
||||
|
||||
### Sponsors
|
||||
|
||||
- @<sponsor-1>
|
||||
- @<sponsor-2>
|
||||
|
||||
### List of contributions to the LitmusChaos project
|
||||
|
||||
- PRs reviewed / authored
|
||||
- Issues responded to
|
||||
- Projects I am involved with
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: Security Issue
|
||||
about: Report the potential vulnerability or security issue
|
||||
title: 'seclog: [Event Description] '
|
||||
labels: security, vulnerability
|
||||
assignees: Saranya-jena, Jonsy13, SarthakJain26
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
_Short summary of the problem. Make the impact and severity as clear as possible. For example: An unsafe deserialization vulnerability allows any unauthenticated user to execute arbitrary code on the server._
|
||||
|
||||
### Details
|
||||
_Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._
|
||||
|
||||
### PoC
|
||||
_Complete instructions, including specific configuration details, to reproduce the vulnerability._
|
||||
|
||||
### Impact
|
||||
_What kind of vulnerability is it? Who is impacted?_
|
||||
|
||||
### Remediation
|
||||
_Propose a remediation suggestion if you have one. Make it clear that this is just a suggestion, as the maintainer might have a better idea to fix the issue._
|
|
@ -0,0 +1,21 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/authentication"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/event-tracker"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/subscriber"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/graphql/server"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -18,11 +18,10 @@ jobs:
|
|||
authentication: ${{ steps.filter.outputs.authentication }}
|
||||
subscriber: ${{ steps.filter.outputs.subscriber }}
|
||||
event-tracker: ${{ steps.filter.outputs.event-tracker }}
|
||||
# upgrade-agent-cp: ${{ steps.filter.outputs.upgrade-agent-cp }}
|
||||
# dex-server: ${{ steps.filter.outputs.dex-server }}
|
||||
dex-server: ${{ steps.filter.outputs.dex-server }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
|
@ -36,22 +35,31 @@ jobs:
|
|||
- 'chaoscenter/subscriber/**'
|
||||
event-tracker:
|
||||
- 'chaoscenter/event-tracker/**'
|
||||
# upgrade-agent-cp:
|
||||
# - 'chaoscenter/upgrade-agents/control-plane/**'
|
||||
# dex-server:
|
||||
# - 'chaoscenter/dex-server/**'
|
||||
dex-server:
|
||||
- 'chaoscenter/dex-server/**'
|
||||
|
||||
gitleaks-scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run GitLeaks
|
||||
run: |
|
||||
wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.2/gitleaks_8.18.2_linux_x64.tar.gz && \
|
||||
tar -zxvf gitleaks_8.18.2_linux_x64.tar.gz && \
|
||||
sudo mv gitleaks /usr/local/bin && gitleaks detect --source . -v
|
||||
|
||||
backend-checks:
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.graphql-server == 'true' || needs.changes.outputs.authentication == 'true' || needs.changes.outputs.subscriber == 'true' || needs.changes.outputs.event-tracker == 'true'
|
||||
if: needs.changes.outputs.graphql-server == 'true' || needs.changes.outputs.authentication == 'true' || needs.changes.outputs.subscriber == 'true' || needs.changes.outputs.event-tracker == 'true' || needs.changes.outputs.dex-server == 'true'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20" # By default, the go version is v1.15 in runner.
|
||||
go-version: "1.22" # By default, the go version is v1.15 in runner.
|
||||
|
||||
- name: Check Golang imports order
|
||||
uses: Jerome1337/goimports-action@v1.0.3
|
||||
|
@ -70,9 +78,8 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
|
@ -82,22 +89,39 @@ jobs:
|
|||
cd chaoscenter
|
||||
make frontend-services-checks
|
||||
|
||||
# backend-unit-tests:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs:
|
||||
# - changes
|
||||
# - backend-checks
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v2
|
||||
# - uses: actions/setup-go@v2
|
||||
# with:
|
||||
# go-version: "1.20" # By default, the go version is v1.15 in runner.
|
||||
# - name: Backend unit tests
|
||||
# shell: bash
|
||||
# run: |
|
||||
# cd chaoscenter
|
||||
# make unit-tests
|
||||
backend-unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- changes
|
||||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.22" # By default, the go version is v1.15 in runner.
|
||||
- name: Backend unit tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter
|
||||
make backend-unit-tests
|
||||
|
||||
web-unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- changes
|
||||
- frontend-checks
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Chaoscenter web unit tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter
|
||||
make web-unit-tests
|
||||
|
||||
docker-build-graphql-server:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -108,7 +132,7 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.graphql-server == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build graphql server docker image
|
||||
shell: bash
|
||||
|
@ -121,7 +145,7 @@ jobs:
|
|||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '0'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
@ -135,7 +159,7 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.authentication == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build auth server docker image
|
||||
shell: bash
|
||||
|
@ -148,7 +172,7 @@ jobs:
|
|||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-auth-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '0'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
@ -162,7 +186,7 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.subscriber == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build subscriber docker image
|
||||
shell: bash
|
||||
|
@ -175,7 +199,7 @@ jobs:
|
|||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-subscriber:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '0'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
@ -188,7 +212,7 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: yarn build check
|
||||
run: |
|
||||
|
@ -219,7 +243,7 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.event-tracker == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build event tracker docker image
|
||||
shell: bash
|
||||
|
@ -232,48 +256,33 @@ jobs:
|
|||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-event-tracker:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '0'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
# docker-build-upgrade-agent-cp:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs:
|
||||
# - backend-checks
|
||||
# - changes
|
||||
# - backend-unit-tests
|
||||
# if: ${{ needs.changes.outputs.upgrade-agent-cp == 'true' }}
|
||||
# steps:
|
||||
# - name: Checkout code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Build control plane upgrade agent docker image
|
||||
# shell: bash
|
||||
# run: |
|
||||
# cd chaoscenter/upgrade-agents/control-plane
|
||||
# docker build . -f Dockerfile -t docker.io/litmuschaos/upgrade-agent-cp:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
# - name: Run Trivy vulnerability scanner
|
||||
# uses: aquasecurity/trivy-action@master
|
||||
# with:
|
||||
# image-ref: 'docker.io/litmuschaos/upgrade-agent-cp:${{ github.sha }}'
|
||||
# format: 'table'
|
||||
# exit-code: '1'
|
||||
# ignore-unfixed: true
|
||||
# vuln-type: 'os,library'
|
||||
# severity: 'CRITICAL,HIGH'
|
||||
docker-build-dex-server:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
if: ${{ needs.changes.outputs.dex-server == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# docker-build-dex-server:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs:
|
||||
# - backend-checks
|
||||
# - changes
|
||||
# - backend-unit-tests
|
||||
# if: needs.changes.outputs.dex-server == 'true'
|
||||
# steps:
|
||||
# - name: Checkout code
|
||||
# uses: actions/checkout@v2
|
||||
# - name: Build dex-server docker image
|
||||
# shell: bash
|
||||
# run: |
|
||||
# cd chaoscenter/dex-server
|
||||
# docker images && docker build . -f Dockerfile --build-arg TARGETARCH=amd64
|
||||
- name: Build dex-server docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter/dex-server
|
||||
docker images && docker build . -f Dockerfile -t docker.io/litmuschaos/litmusportal-dex-server:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-dex-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '0'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
name: CIFuzz
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'chaoscenter/authentication/**'
|
||||
- 'chaoscenter/graphql/**'
|
||||
- 'chaoscenter/subscriber/**'
|
||||
permissions: {}
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer: [address]
|
||||
steps:
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'litmuschaos'
|
||||
language: go
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'litmuschaos'
|
||||
language: go
|
||||
fuzz-seconds: 120
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
output-sarif: true
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: ${{ matrix.sanitizer }}-artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Sarif
|
||||
if: always() && steps.build.outcome == 'success'
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: cifuzz-sarif/results.sarif
|
||||
checkout_path: cifuzz-sarif
|
|
@ -39,11 +39,11 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
@ -54,7 +54,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
@ -68,4 +68,4 @@ jobs:
|
|||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
|
|
@ -2,6 +2,7 @@ name: Litmus-CI
|
|||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
|
@ -16,9 +17,7 @@ jobs:
|
|||
- uses: octokit/request-action@v2.x
|
||||
id: get_PR_commits
|
||||
with:
|
||||
route: GET /repos/:repo/pulls/:pull_number/commits
|
||||
repo: ${{ github.repository }}
|
||||
pull_number: ${{ github.event.issue.number }}
|
||||
route: GET /repos/${{ github.repository }}/pull_number/${{ github.event.issue.number }}/commits
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
@ -38,12 +37,12 @@ jobs:
|
|||
outputs:
|
||||
changed-paths: ${{ steps.filter.outputs.changes }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.find-latest-commit-sha.outputs.commit-sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
ref: ${{ needs.find-latest-commit-sha.outputs.commit-sha }}
|
||||
|
@ -64,7 +63,7 @@ jobs:
|
|||
matrix:
|
||||
path: ${{ fromJSON(needs.find-changes.outputs.changed-paths) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.find-latest-commit-sha.outputs.commit-sha }}
|
||||
fetch-depth: 0
|
||||
|
@ -77,7 +76,7 @@ jobs:
|
|||
directory: ${{ matrix.path }}
|
||||
|
||||
- name: upload docker artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Docker-Images
|
||||
path: Images
|
||||
|
@ -88,7 +87,7 @@ jobs:
|
|||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout litmus-E2E Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: litmuschaos/litmus-e2e
|
||||
path: litmus-e2e
|
||||
|
@ -111,7 +110,7 @@ jobs:
|
|||
kubectl get pods -n kube-system
|
||||
|
||||
- name: download docker artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Docker-Images
|
||||
path: Images
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
deploy:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
|
|
|
@ -16,10 +16,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20" # By default, the go version is v1.15 in runner.
|
||||
go-version: "1.22" # By default, the go version is v1.15 in runner.
|
||||
- run: |
|
||||
make backend-services-checks
|
||||
|
||||
|
@ -27,8 +27,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
- run: |
|
||||
|
@ -38,7 +38,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- run: |
|
||||
img_tag=""
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
|
@ -59,11 +59,10 @@ jobs:
|
|||
echo export FRONTEND_IMAGE="litmusportal-frontend" >> env-vars
|
||||
echo export SUBSCRIBER_IMAGE="litmusportal-subscriber" >> env-vars
|
||||
echo export EVENT_TRACKER="litmusportal-event-tracker" >> env-vars
|
||||
# echo export UPGRADE_AGENT_CP="upgrade-agent-cp" >> env-vars
|
||||
# echo export DEX_SERVER="litmusportal-dex-server" >> env-vars
|
||||
echo export DEX_SERVER="litmusportal-dex-server" >> env-vars
|
||||
|
||||
- name: Uploading envs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter/env-vars
|
||||
|
@ -75,10 +74,10 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
@ -113,10 +112,10 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
@ -151,10 +150,10 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
@ -189,10 +188,10 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
@ -240,10 +239,10 @@ jobs:
|
|||
- frontend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
@ -254,7 +253,7 @@ jobs:
|
|||
with:
|
||||
version: latest
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
|
@ -280,3 +279,42 @@ jobs:
|
|||
FRONTEND_IMAGE=${{ matrix.frontend.image_name }}
|
||||
timestamp=`date "+%s"`
|
||||
make push-frontend
|
||||
|
||||
|
||||
docker-build-and-push-dex-server:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- get-envs
|
||||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push dex server docker image
|
||||
env:
|
||||
IMAGE_NAME: ${DEX_SERVER}
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
DIRECTORY: "dex-server"
|
||||
run: |
|
||||
source env-vars
|
||||
make push-portal-component
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
57748d7e930664094c76ae6b02577db2ce3955fa:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:140
|
||||
57748d7e930664094c76ae6b02577db2ce3955fa:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:185
|
||||
57748d7e930664094c76ae6b02577db2ce3955fa:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:278
|
||||
28f9079f4a1909f277a47f0d45be1f82a84de27d:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:46
|
||||
cd1841bb0d83decc0ab0f09ddda69cc572c50da9:auth/api.html:jwt:293
|
||||
cd1841bb0d83decc0ab0f09ddda69cc572c50da9:auth/swagger.yml:jwt:56
|
||||
512b7bbc16d1f8198872fb517814362b81b18388:docs/auth/swagger.yml:jwt:56
|
||||
512b7bbc16d1f8198872fb517814362b81b18388:docs/auth/api.html:jwt:293
|
||||
32e35ad4fb9c7c2ba8ed4a6043176f1e73801814:monitoring/utils/sample-application-under-test/percona/cr.yaml:hashicorp-tf-password:213
|
||||
352f8c6ae9a3b93ce841e6bc3b3a296c0f917846:litmus-portal/backend/auth/pkg/providers/github/github.go:generic-api-key:14
|
||||
92da50bb8ff07fce3427e70449844b77b7ee3270:apps/datastax/deployers/common/opscenter/conf-dir/conf/ssl/opscenter.key:private-key:1
|
||||
edb1d8dc6e2d1044a9fcb3677727363bbe1e267c:mysql-master/Dockerfile:generic-api-key:37
|
||||
edb1d8dc6e2d1044a9fcb3677727363bbe1e267c:mysql-slave/Dockerfile:generic-api-key:37
|
||||
fd88a74c81ae34796a42fafed1a61bf1edc06607:auth/v3.0.0/auth-api.json:jwt:140
|
||||
fd88a74c81ae34796a42fafed1a61bf1edc06607:auth/v3.0.0/auth-api.json:jwt:185
|
||||
fd88a74c81ae34796a42fafed1a61bf1edc06607:auth/v3.0.0/auth-api.json:jwt:278
|
||||
6eb10ad366fb3bd040cda61e8c64e47f15d576be:auth/v3.0.0/auth-api.json:jwt:46
|
|
@ -0,0 +1,2 @@
|
|||
# Accept the risk (RedHat)
|
||||
CVE-2024-2961
|
132
ADOPTERS.md
132
ADOPTERS.md
|
@ -1,79 +1,87 @@
|
|||
This is a list of organizations that have publicly acknowledged usage of LitmusChaos and shared details of how they are leveraging it for chaos engineering.
|
||||
Please send a PR to this file (along with details in a respective [org](./adopters/organizations) folder) to add/remove entries. If you are an independent user
|
||||
and wish to to share your adoption story, please raise a PR against the [users](USERS.md) file.
|
||||
This is a list of organizations that have publicly acknowledged usage of LitmusChaos and shared details of how they are leveraging it for chaos engineering.
|
||||
Please send a PR to this file (along with details in a respective [org](./adopters/organizations) folder) to add/remove entries. If you are an independent user
|
||||
and wish to to share your adoption story, please raise a PR against the [users](USERS.md) file.
|
||||
|
||||
These organizations have been broadly classified on the basis of how they contribute to the ecosystem: as vendors, as solution providers or as pure end-users of
|
||||
cloud-native technologies. Also included in this list are CNCF (or other) open-source projects that have integrated with Litmus or use it as part of their release/delivery process.
|
||||
These organizations have been broadly classified on the basis of how they contribute to the ecosystem: as vendors, as solution providers or as pure end-users of
|
||||
cloud-native technologies. Also included in this list are CNCF (or other) open-source projects that have integrated with Litmus or use it as part of their release/delivery process.
|
||||
|
||||
### Cloud-Native End Users
|
||||
### Cloud-Native End Users
|
||||
|
||||
The companies listed here conform to [CNCF's definition of end-users](https://github.com/cncf/enduser-public#cncf-end-user-community).
|
||||
The companies listed here conform to [CNCF's definition of end-users](https://github.com/cncf/enduser-public#cncf-end-user-community).
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
|[AnutaNetworks](https://www.anutanetworks.com/)|Chaos Engineering as part of SRE practices in QA environments |[Our Story](adopters/organizations/anutanetworks.md)|
|
||||
|[AkriData](https://www.akridata.com/)|Pod Chaos Experiments in AWS & Azure|[Our Story](adopters/organizations/akridata.md)|
|
||||
|[Halodoc](https://www.halodoc.com/)|Resiliency Validation of Kubernetes Workloads and Infra on AWS |[Our Story](adopters/organizations/halodoc.md)|
|
||||
|[Intuit](https://www.intuit.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Argo Based Chaos Workflows](https://youtu.be/Uwqop-s99LA?t=720)|[Our Story](adopters/organizations/intuit.md)|
|
||||
|[Kitopi](https://www.kitopi.com/)|Chaos Engineering as part of SRE practice|[Our Story](adopters/organizations/kitopi.md)|
|
||||
|[Lenskart](https://www.lenskart.com/)|Chaos Engineering for better Resiliency | [Our Story](adopters/organizations/lenskart.md)|
|
||||
|[Mercedes](https://www.mercedes-benz.com/)|Resiliency validation for applications|[Our Story](adopters/organizations/mercedes.md)|
|
||||
|[Orange](https://www.orange.com)|[Cloud Infra Resiliency](https://youtu.be/UOhjFbCrncw?list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&t=186...a)|[Our Story](adopters/organizations/orange.md)|
|
||||
|[Pôle Emploi](https://www.pole-emploi.fr)|Chaos Engineering as part of SRE practice|[Our Story](adopters/organizations/pole_emploi.md)|
|
||||
|[iFood](https://www.ifood.com.br/)|Chaos Engineering for a Food Delivery Platform|[Our Story](adopters/organizations/ifood.md)|
|
||||
|[FIS](https://www.fisglobal.com/en/)|Larger SRE Transformation with Chaos Engineering|[Our Story](adopters/organizations/fis.md)|
|
||||
|[Adidas](https://adidas.com/)|Implementing Chaos Engineering as a practice at Adidas|[Our Story](adopters/organizations/adidas.md)|
|
||||
|[Cyren](https://www.cyren.com/)|Implementing Chaos Engineering as a practice at Cyren|[Our Story](https://www.infoq.com/articles/chaos-engineering-cloud-native/)|
|
||||
|[AB-Inbev](https://www.ab-inbev.com/)|Implementing Chaos Engineering as a practice at AB-Inbev|[Our Story](adopters/organizations/abinbev.md)|
|
||||
| Organization | Usecase | Details |
|
||||
| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
|
||||
| [AnutaNetworks](https://www.anutanetworks.com/) | Chaos Engineering as part of SRE practices in QA environments | [Our Story](adopters/organizations/anutanetworks.md) |
|
||||
| [AkriData](https://www.akridata.com/) | Pod Chaos Experiments in AWS & Azure | [Our Story](adopters/organizations/akridata.md) |
|
||||
| [Halodoc](https://www.halodoc.com/) | Resiliency Validation of Kubernetes Workloads and Infra on AWS | [Our Story](adopters/organizations/halodoc.md) |
|
||||
| [Intuit](https://www.intuit.com?utm_source=github&utm_campaign=litmuschaos_repo) | [Argo Based Chaos Workflows](https://youtu.be/Uwqop-s99LA?t=720) | [Our Story](adopters/organizations/intuit.md) |
|
||||
| [Kitopi](https://www.kitopi.com/) | Chaos Engineering as part of SRE practice | [Our Story](adopters/organizations/kitopi.md) |
|
||||
| [Lenskart](https://www.lenskart.com/) | Chaos Engineering for better Resiliency | [Our Story](adopters/organizations/lenskart.md) |
|
||||
| [Mercedes](https://www.mercedes-benz.com/) | Resiliency validation for applications | [Our Story](adopters/organizations/mercedes.md) |
|
||||
| [Orange](https://www.orange.com) | [Cloud Infra Resiliency](https://youtu.be/UOhjFbCrncw?list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&t=186...a) | [Our Story](adopters/organizations/orange.md) |
|
||||
| [Pôle Emploi](https://www.pole-emploi.fr) | Chaos Engineering as part of SRE practice | [Our Story](adopters/organizations/pole_emploi.md) |
|
||||
| [iFood](https://www.ifood.com.br/) | Chaos Engineering for a Food Delivery Platform | [Our Story](adopters/organizations/ifood.md) |
|
||||
| [FIS](https://www.fisglobal.com/en/) | Larger SRE Transformation with Chaos Engineering | [Our Story](adopters/organizations/fis.md) |
|
||||
| [Adidas](https://adidas.com/) | Implementing Chaos Engineering as a practice at Adidas | [Our Story](adopters/organizations/adidas.md) |
|
||||
| [Cyren](https://www.cyren.com/) | Implementing Chaos Engineering as a practice at Cyren | [Our Story](https://www.infoq.com/articles/chaos-engineering-cloud-native/) |
|
||||
| [AB-Inbev](https://www.ab-inbev.com/) | Implementing Chaos Engineering as a practice at AB-Inbev | [Our Story](adopters/organizations/abinbev.md) |
|
||||
| [Group Baobab](https://baobab.com/en/home/) | Orchestrating Chaos using LitmusChaos at Baobab | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1647648343) |
|
||||
| [Flipkart](https://www.flipkart.com/) | Chaos Engineering at Flipkart | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1966904935) |
|
||||
| [Talend](https://www.talend.com/) | Chaos Engineering for our pipelines and weekly checks | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-2005254600) |
|
||||
| [Delivery Hero](https://www.deliveryhero.com/) | Enhancing Resiliency of Our Services | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1997465958) |
|
||||
| [Wingie Enuygun Company](https://www.wingie.com/) | Chaos Engineering for an Online Travel and Finance Platform | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-2331265698) |
|
||||
| [EmiratesNBD](https://www.emiratesnbd.com) | Chaos Engineering for Government Owned Bank | [Our Story](adopters/organizations/emirates-nbd.md) |
|
||||
| [PokerBaazi](https://www.pokerbaazi.com/) | Chaos Engineering for Online Gaming | [Our Story](adopters/organizations/pokerbaazi.md) |
|
||||
| [Amadeus](https://amadeus.com/) | Enhance the resilience and reliability in Amadeus through Chaos Engineering | [Our Story](adopters/organizations/amadeus.md) |
|
||||
|
||||
### Cloud-Native Vendors
|
||||
|
||||
The companies listed here sell cloud-native products/technologies. They use LitmusChaos as part of the resiliency validation of these products OR as part of other
|
||||
devops/reliability pipelines (such as for customer portals/websites etc.,) within the company.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
|[KubeSphere](https://kubesphere.io/)|Chaos Engineering|To Be Added|
|
||||
|[Kublr](https://kublr.com/)|Identify the weak spots and components prone to failures under stress|[Our Story](adopters/organizations/kublr.md)|
|
||||
|[MayaData](https://mayadata.io)|[Director Online](https://director.mayadata.io/)|[Our Story](adopters/organizations/mayadata.md)|
|
||||
|[NetApp](https://www.netapp.com)|[Chaos Engineering](https://www.netapp.com/us/index.aspx)|[Our Story](adopters/organizations/netapp.md)|
|
||||
|[Okteto](https://okteto.com)|[Okteto-Litmus Integration](https://okteto.com/blog/chaos-engineering-with-litmus/)| [Our Story](adopters/organizations/okteto.md)|
|
||||
|[RedHat](https://www.redhat.com/en)|[RedHat Openshift Virtualization Maturity](https://www.youtube.com/watch?v=VITGHJ47gx8&list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&index=7)|[Our Story](adopters/organizations/redhat.md)|
|
||||
|[VMWare](https://www.vmware.com/)|Chaos Engineering in CD|[Our Story](adopters/organizations/vmware.md)|
|
||||
|[Zebrium](https://www.zebrium.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Zebrium K8s Chaos Project](https://github.com/zebrium/zebrium-kubernetes-demo)|[Our Story](adopters/organizations/zebrium.md)|
|
||||
|[Container Solutions](https://www.container-solutions.com/)|Building Chaos Engineering for E-Commerce Customers|[Our Story](adopters/organizations/containersolutions.md)|
|
||||
|[Infracloud Technologies](https://www.infracloud.io/)|Developing Resiliency Framework at Infracloud|[Our Story](adopters/organizations/infracloud.md)|
|
||||
devops/reliability pipelines (such as for customer portals/websites etc.,) within the company.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
|
||||
| [KubeSphere](https://kubesphere.io/) | Chaos Engineering | To Be Added |
|
||||
| [Kublr](https://kublr.com/) | Identify the weak spots and components prone to failures under stress | [Our Story](adopters/organizations/kublr.md) |
|
||||
| [MayaData](https://mayadata.io) | [Director Online](https://director.mayadata.io/) | [Our Story](adopters/organizations/mayadata.md) |
|
||||
| [NetApp](https://www.netapp.com) | [Chaos Engineering](https://www.netapp.com/us/index.aspx) | [Our Story](adopters/organizations/netapp.md) |
|
||||
| [Okteto](https://okteto.com) | [Okteto-Litmus Integration](https://okteto.com/blog/chaos-engineering-with-litmus/) | [Our Story](adopters/organizations/okteto.md) |
|
||||
| [RedHat](https://www.redhat.com/en) | [RedHat Openshift Virtualization Maturity](https://www.youtube.com/watch?v=VITGHJ47gx8&list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&index=7) | [Our Story](adopters/organizations/redhat.md) |
|
||||
| [VMWare](https://www.vmware.com/) | Chaos Engineering in CD | [Our Story](adopters/organizations/vmware.md) |
|
||||
| [Zebrium](https://www.zebrium.com?utm_source=github&utm_campaign=litmuschaos_repo) | [Zebrium K8s Chaos Project](https://github.com/zebrium/zebrium-kubernetes-demo) | [Our Story](adopters/organizations/zebrium.md) |
|
||||
| [Container Solutions](https://www.container-solutions.com/) | Building Chaos Engineering for E-Commerce Customers | [Our Story](adopters/organizations/containersolutions.md) |
|
||||
| [Infracloud Technologies](https://www.infracloud.io/) | Developing Resiliency Framework at Infracloud | [Our Story](adopters/organizations/infracloud.md) |
|
||||
| [IFS](https://www.ifs.com/) | Checking Resiliency with LitmusChaos at IFS | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1966428068) |
|
||||
| [Ericsson](https://www.ericsson.com/en) | Chaos Engineering with Open Source LitmusChaos | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1985348431) |
|
||||
| [OutSystems](https://www.outsystems.com/) | Chaos Engineering for Low-Code Platform | [Our Story](adopters/organizations/outsystems.md) |
|
||||
|
||||
### Cloud-Native Solutions & Service Providers
|
||||
|
||||
The companies listed here provide solutions around cloud-native technologies to other organizations/clients and are often involved in their implementation/offer services.
|
||||
They use LitmusChaos as the tool of choice for carrying out chaos experiments in a client environment or in some cases use it as a building block of a larger bespoke software/devops platform.
|
||||
They use LitmusChaos as the tool of choice for carrying out chaos experiments in a client environment or in some cases use it as a building block of a larger bespoke software/devops platform.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
|[Klanik](https://www.klanik.com)|Chaos Engineering as part of SRE practice|[Our Story](adopters/organizations/klanik.md)|
|
||||
| [Neudesic](https://www.neudesic.com/) | Chaos Engineering | [Our Story](adopters/organizations/neudesic.md) |
|
||||
|[WeScale](https://www.wescale.fr)|[Chaos Engineering](https://blog.wescale.fr/2020/03/19/le-guide-de-chaos-engineering-partie-2/)|[Our Story](adopters/organizations/wescale.md)|
|
||||
|[Wipro](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Wipro AppAnywhere](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Our Story](adopters/organizations/wipro.md)|
|
||||
|[HCL Cloud Native Labs](https://www.hcltech.com/)|SRE Enablement Service|[Our Story(TBA)]|
|
||||
|[CI&T](https://ciandt.com/us/en-us)|Chaos Engineering Implementation|[Our Story](adopters/organizations/ci&t.md)|
|
||||
| Organization | Usecase | Details |
|
||||
| :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------- |
|
||||
| [Klanik](https://www.klanik.com) | Chaos Engineering as part of SRE practice | [Our Story](adopters/organizations/klanik.md) |
|
||||
| [Neudesic](https://www.neudesic.com/) | Chaos Engineering | [Our Story](adopters/organizations/neudesic.md) |
|
||||
| [WeScale](https://www.wescale.fr) | [Chaos Engineering](https://blog.wescale.fr/2020/03/19/le-guide-de-chaos-engineering-partie-2/) | [Our Story](adopters/organizations/wescale.md) |
|
||||
| [Wipro](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo) | [Wipro AppAnywhere](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo) | [Our Story](adopters/organizations/wipro.md) |
|
||||
| [HCL Cloud Native Labs](https://www.hcltech.com/) | SRE Enablement Service | [Our Story(TBA)] |
|
||||
| [CI&T](https://ciandt.com/us/en-us) | Chaos Engineering Implementation | [Our Story](adopters/organizations/ci&t.md) |
|
||||
|
||||
### Cloud-Native OSS Projects
|
||||
|
||||
The projects listed here, in most cases use LitmusChaos for testing the resilience of the respective opensource framework/platform
|
||||
(in a manual or automated fashion) or in other cases integrate with it via a plugin/service to provide add resilience validation capability to their
|
||||
existing functions.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
|[Keptn](https://keptn.sh)|[Chaos Engineering integration in CD](https://www.youtube.com/watch?v=aa5SzQmv4EQ)|[Our Story](https://medium.com/keptn/part-2-evaluating-application-resiliency-with-keptn-and-litmuschaos-use-case-and-demo-f43b264a2294)|
|
||||
|[KubeFlare](https://github.com/raspbernetes)|Resilience of microservices on ARM64 (Raspberry Pi) based clusters|[Our Story](adopters/organizations/raspbernetes.md)|
|
||||
|[OpenEBS](https://openebs.io/)|[Openebs-CI](https://openebs.ci/)|[Our Story](adopters/organizations/openebs.md)|
|
||||
|[Pravega](https://pravega.io/)|To inject faults while exercising quality tests on our product|[Our Story](adopters/organizations/pravega.md)|
|
||||
|[Red Hat](https://www.redhat.com/en)|[Chaos Engineering with Kraken](https://github.com/cloud-bulldozer/kraken)|[Our Story](adopters/organizations/redhat_kraken.md)|
|
||||
|[Iter8](https://iter8.tools)|[SLO validation with chaos injection](https://iter8.tools/0.7/tutorials/deployments/slo-validation-chaos/)|To Be Added|
|
||||
|[CNF Test Suite](https://github.com/cncf/cnf-testsuite)|To validate the resilience of Cloud Native Network Functions (CNFs)|[Our Story](adopters/organizations/cnftestsuite.md)|
|
||||
|[APACHE APISIX](https://apisix.apache.org/)|Practicing Chaos Engineering using Litmus in the Apache APISIX Ingress.|[Our Story](adopters/organizations/apisix.md)|
|
||||
### Cloud-Native OSS Projects
|
||||
|
||||
The projects listed here, in most cases use LitmusChaos for testing the resilience of the respective opensource framework/platform
|
||||
(in a manual or automated fashion) or in other cases integrate with it via a plugin/service to provide add resilience validation capability to their
|
||||
existing functions.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Keptn](https://keptn.sh) | [Chaos Engineering integration in CD](https://www.youtube.com/watch?v=aa5SzQmv4EQ) | [Our Story](https://medium.com/keptn/part-2-evaluating-application-resiliency-with-keptn-and-litmuschaos-use-case-and-demo-f43b264a2294) |
|
||||
| [KubeFlare](https://github.com/raspbernetes) | Resilience of microservices on ARM64 (Raspberry Pi) based clusters | [Our Story](adopters/organizations/raspbernetes.md) |
|
||||
| [OpenEBS](https://openebs.io/) | [Openebs-CI](https://openebs.ci/) | [Our Story](adopters/organizations/openebs.md) |
|
||||
| [Pravega](https://pravega.io/) | To inject faults while exercising quality tests on our product | [Our Story](adopters/organizations/pravega.md) |
|
||||
| [Red Hat](https://www.redhat.com/en) | [Chaos Engineering with Kraken](https://github.com/cloud-bulldozer/kraken) | [Our Story](adopters/organizations/redhat_kraken.md) |
|
||||
| [Iter8](https://iter8.tools) | [SLO validation with chaos injection](https://iter8.tools/0.7/tutorials/deployments/slo-validation-chaos/) | To Be Added |
|
||||
| [CNF Test Suite](https://github.com/cncf/cnf-testsuite) | To validate the resilience of Cloud Native Network Functions (CNFs) | [Our Story](adopters/organizations/cnftestsuite.md) |
|
||||
| [APACHE APISIX](https://apisix.apache.org/) | Practicing Chaos Engineering using Litmus in the Apache APISIX Ingress. | [Our Story](adopters/organizations/apisix.md) |
|
||||
|
|
|
@ -2,45 +2,131 @@
|
|||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official email address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing or otherwise unacceptable behavior may be reported by contacting the project team at support@mayadata.io. The project team will review and investigate all complaints, and will respond in a way that it seems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
sayan.mondal@harness.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
|
|
@ -8,9 +8,9 @@ Thanks for your interest in contributing to Litmus and help improve the project!
|
|||
|
||||
## Where to Begin!
|
||||
|
||||
If you have any queries or requests about Litmus please [create an issue](https://github.com/litmuschaos/litmus/issues/new) on GitHub. If you want to comment or ask questions to the contributors start by [joining our community](http://slack.litmuschaos.io) and drop your questions in the #litmus channel.
|
||||
If you have any queries or requests about Litmus please [create an issue](https://github.com/litmuschaos/litmus/issues/new) on GitHub. If you want to comment or ask questions to the contributors start by [joining our community](http://slack.litmuschaos.io) and drop your questions in the **#litmus** channel.
|
||||
|
||||
If you want to do code contributions but you are fairly new to the tech stack we are using! Check out the [Development Guide](https://github.com/litmuschaos/litmus/wiki/Litmus-Portal-Development-Guide) to get a reference and help get started.
|
||||
If you want to do code contributions but you are fairly new to the tech stack we are using! Check out the [Local Development Guide](https://github.com/litmuschaos/litmus/wiki/ChaosCenter-Development-Guide) and [Development Best Practices](https://github.com/litmuschaos/litmus/wiki/Development-Best-Practices) to get a reference and help get started.
|
||||
|
||||
We welcome contributions of all kinds
|
||||
|
||||
|
@ -59,6 +59,7 @@ To submit any kinds of improvements, please consider the following:
|
|||
- If you are making a change to the user interface (UI), include a screenshot of the UI changes.
|
||||
- Follow the relevant coding style guidelines
|
||||
- For backend contributions, popular ones are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting_ and _style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style).
|
||||
- If you are making any changes in backend, make sure you have run and tested the code locally, the reviewers might ask for relevant screenshots in the comments.
|
||||
- For frontend contributions, we follow the [Airbnb style guide](https://airbnb.io/javascript/react/)
|
||||
- Your branch may be merged once all configured checks pass, including:
|
||||
- The branch has passed tests in CI.
|
||||
|
@ -66,6 +67,14 @@ To submit any kinds of improvements, please consider the following:
|
|||
|
||||
If you are new to Go, consider reading [Effective Go](https://golang.org/doc/effective_go.html) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) for guidance on writing idiomatic Go code.
|
||||
|
||||
### Generating/Updating Mocks for `chaoscenter/graphql/server`
|
||||
|
||||
To generate new mocks or update existing mocks:
|
||||
|
||||
- Follow the instructions to install [mockery](https://vektra.github.io/mockery/latest/installation/).
|
||||
- If generating mocks for existing interface simply run `mockery`.
|
||||
- If generating mocks for new interface update [`.mockery.yaml`](././chaoscenter/graphql/server/.mockery.yaml) and run `mockery`.
|
||||
|
||||
---
|
||||
|
||||
## Pull Request Checklist :
|
||||
|
@ -87,7 +96,10 @@ You can choose from a list of sub-dependent repos to contribute to, a few highli
|
|||
- [Chaos-workflows](https://github.com/litmuschaos/chaos-workflows)
|
||||
- [Test-tools](https://github.com/litmuschaos/test-tools)
|
||||
- [Litmus-go](https://github.com/litmuschaos/litmus-go)
|
||||
- [website-litmuschaos](https://github.com/litmuschaos/website-litmuschaos)
|
||||
- [Litmus-website](https://github.com/litmuschaos/litmus-website-2)
|
||||
- [Litmusctl](https://github.com/litmuschaos/litmusctl)
|
||||
- [Litmus-docs](https://github.com/litmuschaos/litmus-docs)
|
||||
- [backstage-plugin](https://github.com/litmuschaos/backstage-plugin)
|
||||
|
||||
## Community
|
||||
|
||||
|
|
143
GOVERNANCE.md
143
GOVERNANCE.md
|
@ -1,98 +1,38 @@
|
|||
# LitmusChaos Project Governance
|
||||
|
||||
This document outlines the governance structure for the LitmusChaos project, a CNCF Incubating project. It describes the roles, responsibilities, decision-making processes, and mechanisms for community involvement.
|
||||
|
||||
We abide by the [Code of Conduct](./CODE_OF_CONDUCT.md) for all the projects maintained under the LitmusChaos Organization.
|
||||
|
||||
For specific guidance on practical contribution steps for any LitmusChaos sub-project please
|
||||
see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide and the sub-project specific contributing guides
|
||||
see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide and the sub-project specific contributing guides
|
||||
in the respective GitHub repositories.
|
||||
|
||||
## Maintainership
|
||||
## Roles and Membership
|
||||
|
||||
There are different types of maintainers, with different responsibilities, but
|
||||
all maintainers have 3 things in common:
|
||||
Roles and their responsibilities are detailed in the [Community Membership](./community-roles.md) document.
|
||||
|
||||
1) They share responsibility in the project's success.
|
||||
2) They have made a long-term, recurring time investment to improve the project.
|
||||
3) They spend that time doing whatever needs to be done, not necessarily what
|
||||
is the most interesting or fun.
|
||||
The list of current maintainers and their organizational affiliations is maintained in the [MAINTAINERS.md](./MAINTAINERS.md) file.
|
||||
|
||||
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||
or the reliability of a release process. But those things distinguish a great
|
||||
project from a good one.
|
||||
## Conflict Resolution and Voting
|
||||
|
||||
## Reviewers
|
||||
Most issues within the project are resolved by consensus. When consensus cannot be reached, a voting process is initiated. All decisions are documented publicly, either in GitHub or in meeting notes.
|
||||
|
||||
A reviewer is a core role within the project.
|
||||
They share in reviewing issues and pull requests and their LGTM counts towards the
|
||||
required LGTM count to merge a code change into the project.
|
||||
### Voting Process
|
||||
|
||||
Reviewers are part of the organization but do not have write access.
|
||||
Becoming a reviewer is a core aspect in the journey to becoming a maintainer.
|
||||
|
||||
## Adding maintainers
|
||||
|
||||
Maintainers are first and foremost contributors that have shown they are
|
||||
committed to the long term success of a project. Contributors wanting to become
|
||||
maintainers are expected to be deeply involved in contributing code, pull
|
||||
request review, and triage of issues in the project for more than three months.
|
||||
|
||||
Just contributing does not make you a maintainer, it is about building trust
|
||||
with the current maintainers of the project and being a person that they can
|
||||
depend on and trust to make decisions in the best interest of the project.
|
||||
|
||||
Periodically, the existing maintainers curate a list of contributors that have
|
||||
shown regular activity on the project over the prior months. From this list,
|
||||
candidates are selected and proposed as maintainers.
|
||||
|
||||
After a candidate has been proposed as maintainer via a Pull Request by any of
|
||||
the existing maintainers, the other maintainers are given five business days
|
||||
to discuss the candidate, raise objections and cast their vote.
|
||||
The Votes take place via the pull request comment. Candidates must be approved by at
|
||||
least 66% of the current maintainers by adding their vote on the mailing list.
|
||||
The reviewer role has the same process but only requires 33% of current maintainers.
|
||||
Only maintainers of the repository that the candidate is proposed for are allowed to
|
||||
vote. The candidate becomes a maintainer once the pull request is merged.
|
||||
|
||||
## Adding sub-projects
|
||||
|
||||
Similar to adding maintainers, new sub projects can be added to LitmusChaos
|
||||
GitHub organization as long as they adhere to the LitmusChaos vision and mission.
|
||||
New projects are discussed in either the Contributor Meeting or the Community
|
||||
slack and requires at least 1 maintainer approval.
|
||||
|
||||
If a project is approved, a maintainer will add the project to the LitmusChaos
|
||||
GitHub organization, and make an announcement on a public forum.
|
||||
|
||||
## Stepping down policy
|
||||
|
||||
Life priorities, interests, and passions can change. If you're a maintainer but
|
||||
feel you must remove yourself from the list, inform other maintainers that you
|
||||
intend to step down, and if possible, help find someone to pick up your work.
|
||||
At the very least, ensure your work can be continued where you left off.
|
||||
|
||||
After you've informed other maintainers, create a pull request to remove
|
||||
yourself from the MAINTAINERS file.
|
||||
|
||||
## Removal of inactive maintainers
|
||||
|
||||
Similar to the procedure for adding new maintainers, existing maintainers can
|
||||
be removed from the list if they do not show significant activity on the
|
||||
project. Periodically, the maintainers review the list of maintainers and their
|
||||
activity over the last three months.
|
||||
|
||||
If a maintainer has shown insufficient activity over this period, a neutral
|
||||
person will contact the maintainer to ask if they want to continue being
|
||||
a maintainer. If the maintainer decides to step down as a maintainer, they
|
||||
open a pull request to be removed from the MAINTAINERS file.
|
||||
- **Threshold:** A vote passes with a simple majority.
|
||||
- **Quorum:** At least 30% of maintainers must participate in the vote.
|
||||
- **Voting Method:** Votes are cast by adding +1 or -1 to the associated GitHub issue or PR.
|
||||
- **Binding Votes:** Each maintainer has one binding vote. Non-binding votes from the community are encouraged.
|
||||
- **Organizational Limit:** No single organization can cast more than 40% of the eligible votes. Organizations with more than 40% of maintainers must designate voting members.
|
||||
- **Duration:** Voting remains open for one week.
|
||||
|
||||
## How are decisions made?
|
||||
|
||||
LitmusChaos is an open-source project with an open design philosophy. This means
|
||||
that the repository is the source of truth for EVERY aspect of the project,
|
||||
including its philosophy, design, road map, and APIs. *If it's part of the
|
||||
project, it's in the repo. If it's in the repo, it's part of the project.*
|
||||
including its philosophy, design, road map, and APIs. _If it's part of the
|
||||
project, it's in the repo. If it's in the repo, it's part of the project._
|
||||
|
||||
As a result, all decisions can be expressed as changes to the repository. An
|
||||
implementation change is a change to the source code. An API change is a change
|
||||
|
@ -101,12 +41,39 @@ manifesto, and so on.
|
|||
|
||||
All decisions affecting LitmusChaos, big and small, follow the same 3 steps:
|
||||
|
||||
* Step 1: Open a pull request. Anyone can do this.
|
||||
- Step 1: Open a pull request. Anyone can do this.
|
||||
- Step 2: Discuss the pull request. Anyone can do this.
|
||||
- Step 3: Merge or refuse the pull request. Who does this depends on the nature
|
||||
of the pull request and which areas of the project it affects.
|
||||
|
||||
* Step 2: Discuss the pull request. Anyone can do this.
|
||||
## Decision-Making Process
|
||||
|
||||
* Step 3: Merge or refuse the pull request. Who does this depends on the nature
|
||||
of the pull request and which areas of the project it affects.
|
||||
Most decisions are made through consensus. If consensus cannot be reached, maintainers may initiate a vote.
|
||||
|
||||
### Voting
|
||||
|
||||
- **Threshold:** A vote passes with a simple majority.
|
||||
- **Quorum:** At least 30% of maintainers must participate in the vote.
|
||||
- **Method:** Votes are cast using +1 (approve) or -1 (reject) in the relevant GitHub PR or issue.
|
||||
- **Duration:** Voting remains open for one week.
|
||||
|
||||
## Community Support and Transparency
|
||||
|
||||
LitmusChaos aims for full transparency and inclusion in all governance activities. All decisions are made publicly and documented in the GitHub repositories or public meetings.
|
||||
|
||||
### Recurring Public Meetings
|
||||
|
||||
- #### Maintainers and Contributors Meeting
|
||||
|
||||
Covers technical issues, future milestones, and roadmaps. Also focused on governance, membership, and the future direction of the project.
|
||||
|
||||
- #### Community Meeting
|
||||
|
||||
Engages end users and the community with project updates, user presentations, and open discussions.
|
||||
|
||||
- #### Meeting Calendar
|
||||
|
||||
Please fill [this invite form](https://forms.gle/AsuXB2hbTG2TyD2d9) to be added to the calendar
|
||||
|
||||
## Helping contributors with the DCO
|
||||
|
||||
|
@ -130,10 +97,12 @@ When you add someone's DCO, please also add your own to keep a log.
|
|||
Yes. Nobody should ever push to master directly. All changes should be
|
||||
made through a pull request.
|
||||
|
||||
## Conflict Resolution
|
||||
## Adding sub-projects
|
||||
|
||||
If you have a technical dispute that you feel has reached an impasse with a
|
||||
subset of the community, any contributor may open an issue, specifically
|
||||
calling for a resolution vote of the current maintainers to resolve the dispute.
|
||||
The same voting quorums required (2/3) for adding and removing maintainers
|
||||
will apply to conflict resolution.
|
||||
Similar to adding maintainers, new sub projects can be added to LitmusChaos
|
||||
GitHub organization as long as they adhere to the LitmusChaos vision and mission.
|
||||
New projects are discussed in either the Contributor Meeting or the Community
|
||||
slack and requires at least 1 maintainer approval.
|
||||
|
||||
If a project is approved, a maintainer will add the project to the LitmusChaos
|
||||
GitHub organization, and make an announcement on a public forum.
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# LitmusChaos Maintainers
|
||||
|
||||
- [GOVERNANCE.md](./GOVERNANCE.md) describes the LitmusChaos governance.
|
||||
- [community-roles.md](./community-roles.md) describes the responsibilities and requirements on the project roles.
|
||||
|
||||
### Component-Wise Code Owners & Primary Reviewers
|
||||
|
||||
Area |Components |Source |Maintainers |Reviewers|
|
||||
|
@ -7,24 +12,25 @@ control-plane |chaos-dashboard |frontend, component-library
|
|||
execution-plane |subscriber, event-tracker |cluster-agents |@gdsoumya, @imrajdas, @SarthakJain26 |@amityt, @Jonsy13, @ispeakc0de, @Adarshkumar14 |
|
||||
execution-plane |litmus-core |chaos-operator, chaos-runner, elves, chaos-exporter |@ksatchit, @ispeakc0de, @chandankumar4 |@uditgaurav, @neelanjan |
|
||||
chaos-experiments|experiment-lib, chaoshub |litmus-go, test-tools, chaos-charts |@uditgaurav, @ispeakc0de, @ksatchit, @Vr00mm| @neelanjan00, @Adarshkumar14, @avaakash |
|
||||
chaos-plugins |cli, plugin infra |litmusctl |@Saranya-jena, @SarthakJain26 |@Jonsy13, @ajeshbaby, @imrajdas |
|
||||
chaos-plugins |cli, plugin infra, developer portals |litmusctl, backstage-plugin |@Saranya-jena, @SarthakJain26, @namkyu1999 |@Jonsy13, @ajeshbaby, @imrajdas |
|
||||
chaos-sdk |go/python/ansible sdk |litmus-go,litmus-python,litmus-ansible |@oumkale, @ispeakc0de, @ksatchit |@neelanjan00, @avaakash, @uditgaurav |
|
||||
e2e |e2e-suite, e2e-dashboard |litmus-e2e |@uditgaurav, @Jonsy13 |@neelanjan00, @S-ayanide, @avaakash |
|
||||
integrations |CI/CD plugins, wrappers |chaos-ci-lib, gitlab-templates, github-actions |@uditgaurav, @ksatchit |@ispeakc0de, @Adarshkumar14 |
|
||||
helm-charts |control-plane, agent, experiments|litmus-helm |@Jasstkn, @ispeakc0de, @imrajdas, @Jonsy13 |@ksatchit, @uditgaurav |
|
||||
documentation |platform-docs, experiment-docs |litmus-docs, mkdocs |@neelanjan00, @umamukkara, @ispeakc0de |@ksatchit, @ajeshbaby, @amityt, @uditgaurav |
|
||||
websites |project website, chaoshub, documentation |litmus-website, charthub, litmus-docs |@umamukkara, @arkajyotiMukherjee, @S-ayanide |@SahilKr24, @hrishavjha, @ajeshbaby |
|
||||
documentation |platform-docs, experiment-docs |litmus-docs, mkdocs |@neelanjan00, @umamukkara, @ispeakc0de |@ksatchit, @ajeshbaby, @amityt, @uditgaurav |websites |project website, chaoshub, documentation |litmus-website, charthub, litmus-docs |@umamukkara, @arkajyotiMukherjee, @S-ayanide |@SahilKr24, @hrishavjha, @ajeshbaby |
|
||||
websites |project website, chaoshub, documentation |litmus-website, charthub, litmus-docs |@SahilKr24, @hrishavjha, @ajeshbaby |@umamukkara, @S-ayanide |
|
||||
|
||||
### Consolidated Maintainers List
|
||||
### Consolidated Maintainers List
|
||||
|
||||
```
|
||||
"Amit Kumar Das",@amityt,amit.das@harness.io
|
||||
"Arkojyoti Mukherjee",@arkajyotiMukherjee,arko@harness.io
|
||||
"Chandan Kumar",@chandankumar4,chandan.kumar@zopsmart.com
|
||||
"Chandan Kumar",@chandankumar4,ckamtaprasad@msystechnologies.com
|
||||
"Karthik Satchitanand",@ksatchit,karthik.s@harness.io
|
||||
"Maria Kotlyarevskaya",@Jasstkn,jasssstkn@yahoo.com
|
||||
"Namkyu Park",namkyu1999,lak9348@gmail.com
|
||||
"Neelanjan Manna",@neelanjan00,neelanjan.manna@harness.io
|
||||
"Oum Nivrati Kale",@oumkale,imkaleoum@gmail.com
|
||||
"Oum Nivrati Kale",@oumkale,oumk@jfrog.com
|
||||
"Raj Das",@imrajdas,rajbabu.das@harness.io
|
||||
"Rémi Ziolkowski",@Vr00mm,remi.ziolkowski-ext@pole-emploi.fr
|
||||
"Soumya Ghosh Dastidar",@gdsoumya,gdsoumya@gmail.com
|
||||
|
@ -35,6 +41,9 @@ websites |project website, chaoshub, documentation |litmus-website, cha
|
|||
"Udit Gaurav",@uditgaurav,udit.gaurav@harness.io
|
||||
"Vedant Shrotria",@Jonsy13,vedant.shrotria@harness.io
|
||||
"Uma Mukkara",@umamukkara,umasankar.mukkara@harness.io
|
||||
"Sahil KR",@SahilKr24,sahil.kumar@harness.io
|
||||
"Ajesh Baby",@ajeshbaby,ajesh.baby@harness.io
|
||||
"Hrishav Kumar",@hrishavjha,hrishav.kumar@harness.io
|
||||
```
|
||||
|
||||
### Consolidated Reviewers List
|
||||
|
@ -42,9 +51,6 @@ websites |project website, chaoshub, documentation |litmus-website, cha
|
|||
```
|
||||
"Adarsh Kumar",@Adarshkumar14,adarsh.kumar@harness.io
|
||||
"Akash Srivastava",@avaakash,akash.srivastava@harness.io
|
||||
"Ajesh Baby",@ajeshbaby,ajesh.baby@harness.io
|
||||
"Sahil Kumar",@SahilKr24,sahil.kumar@harness.io
|
||||
"Hrishav Kumar Jha",@hrishavjha,hrishav.kumar@harness.io
|
||||
```
|
||||
|
||||
### Emeritus Maintainers
|
||||
|
@ -60,4 +66,4 @@ websites |project website, chaoshub, documentation |litmus-website, cha
|
|||
"Amit Bhatt",@amitbhatt818,amit.bhatt@mayadata.io,MayaData
|
||||
"Ishan Gupta",@ishangupta-ds,ishan@chaosnative.com,ChaosNative
|
||||
"Rahul M Chheda",@rahulchheda,rahul.chheda1997@gmail.com,Independent
|
||||
```
|
||||
```
|
|
@ -13,6 +13,11 @@ This document serves as a comprehensive record of mentees, mentors, issues, and
|
|||
| LFX Mentorship | March 1st - May 31st, 2023 | [NamKyu Park](https://github.com/namkyu1999) | [Sayan Mondal](https://github.com/S-ayanide), [Amit Kumar Das](https://github.com/amityt) | https://github.com/litmuschaos/litmus/issues/3892 | [Blog](https://dev.to/namkyu1999/my-lfx-mentorship-journey-the-best-starting-point-for-contributing-to-open-source-4f35), [CNCF blog](https://www.cncf.io/blog/2023/05/30/my-lfx-mentorship-journey-the-best-starting-point-for-contributing-to-open-source/) |
|
||||
| LFX Mentorship | June 1st - August 31st, 2023 | [Soham Ratnaparkhi](https://github.com/SohamRatnaparkhi) | [Amit Kumar Das](https://github.com/amityt), [Arkajyoti Mukherjee](https://github.com/arkajyotiMukherjee) | https://github.com/litmuschaos/litmus/issues/3970 | -- |
|
||||
| LFX Mentorship | June 1st - August 31st, 2023 | [Nagesh Bansal](https://github.com/Nageshbansal) | [Shubham Chaudhary](https://github.com/ispeakc0de), [Vansh Bhatia](https://github.com/vanshBhatia-A4k9) | https://github.com/litmuschaos/litmus/issues/3969 | [Blog](https://dev.to/nageshbansal/exploring-litmusctl-a-comprehensive-guide-170k) |
|
||||
| LFX Mentorship | September 1st - November 30th, 2023 | [Magnim Thibaut Freedisch Batale](https://github.com/Freedisch) | [Saranya Jena](https://github.com/Saranya-jena), [Sayan Mondal](https://github.com/S-ayanide) | https://github.com/litmuschaos/litmus/issues/4102 | [Blog](https://dev.to/freedisch_10/embarking-on-a-professional-growth-adventure-insights-from-my-lfx-mentorship-program-at-litmuschaos-5cbc) |
|
||||
| LFX Mentorship | September 1st - November 30th, 2023 | [Deep Poharkar](https://github.com/deep-poharkar) | [Sarthak Jain](https://github.com/SarthakJain26), [Neelanjan Manna](https://github.com/neelanjan00) | https://github.com/litmuschaos/litmus/issues/4101 | -- |
|
||||
| LFX Mentorship | March 1st - April 31st, 2024 | [Aryan Bhokare](https://github.com/aryan-bhokare) | [Saranya Jena](https://github.com/Saranya-jena), [Hrishav Kumar](https://github.com/hrishavjha), [Sahil Kumar](https://github.com/SahilKr24) | https://github.com/litmuschaos/litmus/issues/4407 | --
|
||||
| LFX Mentorship | March 1st - April 31st, 2024 | [Shivam Purohit](https://github.com/shivam-Purohit) | [Sarthak Jain](https://github.com/SarthakJain26), [Vedant Shrotria](https://github.com/jonsy13), [Nagesh Bansal](https://github.com/Nageshbansal) | https://github.com/litmuschaos/litmus/issues/4405 | --
|
||||
| LFX Mentorship | March 1st - April 31st, 2024 | [M R DHANUSH](https://github.com/Dhanush0369) | [Raj Babu Das](https://github.com/imrajdas), [Shubham Chaudhary](https://github.com/ispeakc0de), [NamKyu Park](https://github.com/namkyu1999) | https://github.com/litmuschaos/litmus/issues/4406 | -- |
|
||||
|
||||
> Refer to the [CNCF Mentoring](https://github.com/cncf/mentoring) repository for more details.
|
||||
|
||||
|
|
37
README.md
37
README.md
|
@ -9,9 +9,10 @@
|
|||
[](https://github.com/litmuschaos/litmus/stargazers)
|
||||
[](https://github.com/litmuschaos/litmus/issues)
|
||||
[](https://twitter.com/LitmusChaos)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/3202)
|
||||
[](https://www.bestpractices.dev/projects/3202)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Flitmuschaos%2Flitmus?ref=badge_shield)
|
||||
[](https://www.youtube.com/channel/UCa57PMqmz_j0wnteRa9nCaw)
|
||||
[](https://gurubase.io/g/litmuschaos)
|
||||
<br><br><br><br>
|
||||
|
||||
#### *Read this in [other languages](translations/TRANSLATIONS.md).*
|
||||
|
@ -26,7 +27,7 @@ inducing chaos tests in a controlled way. Developers & SREs can practice Chaos E
|
|||
Chaos Engineering principles & community collaborated. It is 100% open source & a CNCF project.
|
||||
|
||||
LitmusChaos takes a cloud-native approach to create, manage and monitor chaos. The platform itself runs as a set of microservices and uses Kubernetes
|
||||
custom resources to define the chaos intent, as well as the steady state hypothesis.
|
||||
custom resources (CRs) to define the chaos intent, as well as the steady state hypothesis.
|
||||
|
||||
At a high-level, Litmus comprises of:
|
||||
|
||||
|
@ -55,8 +56,6 @@ ChaosExperiment CRs are hosted on <a href="https://hub.litmuschaos.io" target="_
|
|||
application developers or vendors share their chaos experiments so that their users can use them to increase the resilience of the applications
|
||||
in production.
|
||||
|
||||

|
||||
|
||||
## Use cases
|
||||
|
||||
- **For Developers**: To run chaos experiments during application development as an extension of unit testing or integration testing.
|
||||
|
@ -87,7 +86,19 @@ Feel free to reach out if you have any queries,concerns, or feature requests
|
|||
|
||||
- To join our [Slack Community](https://slack.litmuschaos.io/) and meet our community members, put forward your questions & opinions, join the #litmus channel on the [Kubernetes Slack](https://slack.k8s.io/).
|
||||
### Community Meetings
|
||||
The Litmus community meets on the third wednesday of every month at 10:00PM IST/6:30 PM CEST/9:30 AM PST.
|
||||
|
||||
1. Community Meetings
|
||||
These will be hosted every 3rd Wednesday of every month at 5:30 PM GMT /6:30 PM CEST /10 PM IST
|
||||
The community meetings will involve discussing community updates, sharing updates on new features/releases and discussing user/adopter stories. Everyone in the community is invited for the same to participate in the LitmusChaos community meetings.
|
||||
|
||||
|
||||
2. Contributor Meetings
|
||||
These will be hosted every second & last Thursday of every month at 2:30 PM GMT /3:30 PM CEST /7 PM IST
|
||||
The contributor meetings are only meant to discuss technical and non-technical contributions to LitmusChaos. Maintainers, present Contributors and aspiring contributors are invited to participate in the LitmusChaos contributor meetings to discuss issues, fixes, enhancements and future contributions
|
||||
|
||||
|
||||
Fill out the [LitmusChaos Meetings invite form](https://forms.gle/xYZyZ2gTWMqz7xSs7) to get your Calendar invite!
|
||||
|
||||
|
||||
- [Sync Up Meeting Link](https://harness-io.zoom.us/j/95100368978?pwd=b2VrdCtaakE5U3dhOElFMUJOaXVOUT09)
|
||||
- [Sync Up Agenda & Meeting Notes](https://hackmd.io/a4Zu_sH4TZGeih-xCimi3Q)
|
||||
|
@ -95,9 +106,18 @@ The Litmus community meets on the third wednesday of every month at 10:00PM IST/
|
|||
|
||||
### Videos
|
||||
|
||||
- [What if Your System Experiences an Outage? Let's Build a Resilient Systems with Chaos Engineering](https://www.youtube.com/watch?v=3mjGEh905u4&t=1s) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Enhancing Cyber Resilience Through Zero Trust Chaos Experiments in Cloud Native Environments](https://youtu.be/BelNIk4Bkng) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [LitmusChaos, with Karthik Satchitanand](https://www.youtube.com/watch?v=ks2R57hhFZk&t=503s) @ [The Kubernetes Podcast from Google](https://www.youtube.com/@TheKubernetesPodcast)
|
||||
- [Cultural Shifts: Fostering a Chaos First Mindset in Platform Engineering](https://www.youtube.com/watch?v=WUXFKxgZRsk) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Fire in the Cloud: Bringing Managed Services Under the Ambit of Cloud-Native Chaos Engineering](https://www.youtube.com/watch?v=xCDQp5E3VUs) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Security Controls for Safe Chaos Experimentation](https://www.youtube.com/watch?v=whCkvLKAw74) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Chaos Engineering For Hybrid Targets With LitmusChaos](https://www.youtube.com/watch?v=BZL-ngvbpbU&t=751s) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Cloud Native Live: Litmus Chaos Engine and a microservices demo app](https://youtu.be/hOghvd9qCzI)
|
||||
- [Chaos Engineering hands-on - An SRE ideating Chaos Experiments and using LitmusChaos | July 2022](https://youtu.be/_x_7SiesjF0)
|
||||
- [Achieve Digital Product Resiliency with Chaos Engineering](https://youtu.be/PQrmBHgk0ps)
|
||||
- [Case Study: Bringing Chaos Engineering to the Cloud Native Developers](https://youtu.be/KSl-oKk6TPA) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Cloud Native Chaos Engineering with LitmusChaos](https://www.youtube.com/watch?v=ItUUqejdXr0) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [How to create Chaos Experiments with Litmus | Litmus Chaos tutorial](https://youtu.be/mwu5eLgUKq4) @ [Is it Observable](https://www.youtube.com/c/IsitObservable)
|
||||
- [Cloud Native Chaos Engineering Preview With LitmusChaos](https://youtu.be/pMWqhS-F3tQ)
|
||||
- [Get started with Chaos Engineering with Litmus](https://youtu.be/5CI8d-SKBfc) @ [Containers from the Couch](https://www.youtube.com/c/ContainersfromtheCouch)
|
||||
|
@ -119,7 +139,6 @@ The Litmus community meets on the third wednesday of every month at 10:00PM IST/
|
|||
|
||||
Community Blogs:
|
||||
|
||||
- Daniyal Rayn: [Do I need Chaos Engineering on my environment? Trust me you need it!](https://maveric-systems.com/blog/do-i-need-chaos-engineering-on-my-environment-trust-me-you-need-it/)
|
||||
- LiveWyer: [LitmusChaos Showcase: Chaos Experiments in a Helm Chart Test Suite](https://livewyer.io/blog/2021/03/22/litmuschaos-showcase-chaos-experiments-in-a-helm-chart-test-suite/)
|
||||
- Jessica Cherry: [Test Kubernetes cluster failures and experiments in your terminal](https://opensource.com/article/21/6/kubernetes-litmus-chaos)
|
||||
- Yang Chuansheng(KubeSphere): [KubeSphere 部署 Litmus 至 Kubernetes 开启混沌实验](https://kubesphere.io/zh/blogs/litmus-kubesphere/)
|
||||
|
@ -128,8 +147,6 @@ Community Blogs:
|
|||
- Akram Riahi(WeScale):[Chaos Engineering : Litmus sous tous les angles](https://blog.wescale.fr/2021/03/11/chaos-engineering-litmus-sous-tous-les-angles/)
|
||||
- Prashanto Priyanshu(LensKart):[Lenskart’s approach to Chaos Engineering-Part 2](https://blog.lenskart.com/lenskarts-approach-to-chaos-engineering-part-2-6290e4f3a74e)
|
||||
- DevsDay.ru(Russian):[LitmusChaos at Kubecon EU '21](https://devsday.ru/blog/details/40746)
|
||||
- Ryan Pei(Armory): [LitmusChaos in your Spinnaker Pipeline](https://www.armory.io/blog/litmuschaos-in-your-spinnaker-pipeline/)
|
||||
- David Gildeh(Zebrium): [Using Autonomous Monitoring with Litmus Chaos Engine on Kubernetes](https://www.zebrium.com/blog/using-autonomous-monitoring-with-litmus-chaos-engine-on-kubernetes)
|
||||
|
||||
|
||||
## Adopters
|
||||
|
@ -146,7 +163,7 @@ Litmus is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENS
|
|||
|
||||
Litmus Chaos is part of the CNCF Projects.
|
||||
|
||||
[](https://landscape.cncf.io/?selected=litmus)
|
||||
[](https://landscape.cncf.io/?selected=litmus)
|
||||
|
||||
## Important Links
|
||||
|
||||
|
@ -155,5 +172,5 @@ Litmus Chaos is part of the CNCF Projects.
|
|||
</a>
|
||||
<br>
|
||||
<a href="https://landscape.cncf.io/?selected=litmus">
|
||||
CNCF Landscape <img src="https://landscape.cncf.io/images/left-logo.svg" alt="Litmus on CNCF Landscape" height="15">
|
||||
CNCF Landscape <img src="https://landscape.cncf.io/images/cncf-landscape-horizontal-color.svg" alt="Litmus on CNCF Landscape" height="15">
|
||||
</a>
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
## RELEASE GUIDELINES
|
||||
# Versioning and Release Guidelines
|
||||
|
||||
This document details the versioning, release plan and release guidelines for LitmusChaos. Stability
|
||||
is a top goal for this project, and we hope that this document and the processes
|
||||
it entails will help to achieve that. It covers the release timelines, tracking, process, versioning
|
||||
numbering, support horizons, and API stability.
|
||||
|
||||
If you rely on LitmusChaos, it would be good to spend time understanding the
|
||||
areas of the API that are and are not supported and how they impact your
|
||||
project in the future.
|
||||
|
||||
This document will be considered a living document. Scheduled releases, supported timelines, and API stability guarantees will be updated here as they
|
||||
change.
|
||||
|
||||
If there is something that you require or this document leaves out, please
|
||||
reach out by [filing an issue](https://github.com/litmuschaos/litmus/issues).
|
||||
|
||||
- There is a scheduled release on the 15th of every month on the following repositories:
|
||||
- [Litmus](https://github.com/litmuschaos/litmus)
|
||||
|
@ -7,8 +22,6 @@
|
|||
- [Chaos-Exporter](https://github.com/litmuschaos/chaos-exporter)
|
||||
- [Chaos-Charts](https://github.com/litmuschaos/chaos-charts)
|
||||
|
||||
- Repositories use release version according to the [Semantic Versioning](https://semver.org/)
|
||||
|
||||
- Docker images with release tags are pushed upon creation of a github release
|
||||
|
||||
- Following are the docker images:
|
||||
|
@ -19,8 +32,6 @@
|
|||
|
||||
- The chaos chart bundles are created by publishing the github releases for the [chaos-charts](https://github.com/litmuschaos/chaos-charts) repo. This is picked by the chaos [charthub](https://hub.litmuschaos.io) for user download.
|
||||
|
||||
- Tracking of releases is done on Github [project board](https://github.com/litmuschaos/litmus/projects)
|
||||
|
||||
- The release flow consists of the following steps:
|
||||
|
||||
- Sprint Planning based on backlogs & feature requests from the community
|
||||
|
@ -32,6 +43,157 @@
|
|||
- Doc sanity tests
|
||||
- Litmus release with change log
|
||||
|
||||
## Releases
|
||||
|
||||
Releases of LitmusChaos will be versioned using dotted triples, similar to
|
||||
[Semantic Version](http://semver.org/). For the purposes of this document, we
|
||||
will refer to the respective components of this triple as
|
||||
`<major>.<minor>.<patch>`. The version number may have additional information,
|
||||
such as alpha, beta and release candidate qualifications. Such releases will be
|
||||
considered "pre-releases".
|
||||
|
||||
### Major and Minor Releases
|
||||
|
||||
Major and minor releases of LitmusChaos will be made from master. Releases of
|
||||
LitmusChaos will be marked with GPG signed tags and announced at
|
||||
https://github.com/litmuschaos/litmus/releases. The tag will be of the
|
||||
format `<major>.<minor>.<patch>` and should be made with the command `git tag
|
||||
-s <major>.<minor>.<patch>`.
|
||||
|
||||
After a minor release, a branch will be created, with the format
|
||||
`release-<major>.<minor>.x` from the minor tag. All further patch releases will
|
||||
be done from that branch. For example, once we release `1.0.0`, a branch
|
||||
`release-1.0.x` will be created from that tag. All future patch releases will be
|
||||
done against that branch.
|
||||
|
||||
### Pre-releases
|
||||
|
||||
Pre-releases, such as alphas, betas and release candidates will be conducted
|
||||
from their source branch. For major and minor releases, these releases will be
|
||||
done from main. For patch releases, these pre-releases should be done within
|
||||
the corresponding release branch.
|
||||
|
||||
While pre-releases are done to assist in the stabilization process, no
|
||||
guarantees are provided.
|
||||
|
||||
### Upgrade Path
|
||||
|
||||
The upgrade path for LitmusChaos is such that the 0.0.x patch releases are
|
||||
always backward compatible with its major and minor version. Minor (0.x.0)
|
||||
version will always be compatible with the previous minor release. i.e. 1.2.0
|
||||
is backwards compatible with 1.1.0 and 1.1.0 is compatible with 1.0.0. There is
|
||||
no compatibility guarantees for upgrades that span multiple, _minor_ releases.
|
||||
For example, 1.0.0 to 1.2.0 is not supported. One should first upgrade to 1.1,
|
||||
then 1.2.
|
||||
|
||||
There are no compatibility guarantees with upgrades to _major_ versions. For
|
||||
example, upgrading from 1.0.0 to 2.0.0 may require resources to migrated or
|
||||
integrations to change. Each major version will be supported for at least 1
|
||||
year with bug fixes and security patches.
|
||||
|
||||
### Next Release
|
||||
|
||||
The activity for the next release will be tracked in the
|
||||
[project board](https://github.com/litmuschaos/litmus/projects). If your
|
||||
issue or PR is not present in the project board, please reach out to the maintainers or discuss the same on the #litmus-dev slack channel to create the milestone or add an issue or PR to an existing milestone.
|
||||
|
||||
### Support Horizon
|
||||
|
||||
Support horizons will be defined corresponding to a release branch, identified
|
||||
by `<major>.<minor>`. Release branches will be in one of several states:
|
||||
|
||||
- __*Next*__: The next planned release branch.
|
||||
- __*Active*__: The release is a stable branch which is currently supported and accepting patches.
|
||||
- __*Extended*__: The release branch is only accepting security patches.
|
||||
- __*LTS*__: The release is a long term stable branch which is currently supported and accepting patches.
|
||||
- __*End of Life*__: The release branch is no longer supported and no new patches will be accepted.
|
||||
|
||||
Releases will be supported at least one year after a _minor_ release. This means that
|
||||
we will accept bug reports and backports to release branches until the end of
|
||||
life date. If no new _minor_ release has been made, that release will be
|
||||
considered supported until 6 months after the next _minor_ is released or one year,
|
||||
whichever is longer. Additionally, releases may have an extended security support
|
||||
period after the end of the active period to accept security backports. This
|
||||
timeframe will be decided by maintainers before the end of the active status.
|
||||
|
||||
Long term stable (_LTS_) releases will be supported for at least three years after
|
||||
their initial _minor_ release. These branches will accept bug reports and
|
||||
backports until the end of life date. They may also accept a wider range of
|
||||
patches than non-_LTS_ releases to support the longer term maintainability of the
|
||||
branch, including library dependency, toolchain (including Go) and other version updates
|
||||
which are needed to ensure each release is built with fully supported dependencies and
|
||||
remains usable by LitmusChaos clients. _LTS_ releases can also accept feature backports
|
||||
to support new Kubernetes releases. The default action has to be reject it though,
|
||||
for long-term stability. This is still negotiable when the feature is a hard dependency
|
||||
for a new release of Kubernetes. There should be at least a 6-month overlap between
|
||||
the end of life of an _LTS_ release and the initial release of a new _LTS_ release.
|
||||
Up to 6 months before the announced end of life of an _LTS_ branch, the branch may
|
||||
convert to a regular _Active_ release with stricter backport criteria.
|
||||
|
||||
The current state is available in the following tables:
|
||||
|
||||
| Release | Status | Start | End of Life |
|
||||
| --------- | ------------- | ------------------ | ------------------- |
|
||||
| [0.x {0.6 - 0.9}](https://github.com/litmuschaos/litmus/releases/tag/0.9.0) | End of Life | Sept 13, 2019 | Jun 15, 2020 |
|
||||
| [1.x {1.0 - 1.13}](https://github.com/litmuschaos/litmus/releases/tag/1.13.8) | End of Life | Jan 8, 2020 | - |
|
||||
| [2.0 beta {2.0 beta 0 to 2.0 beta 9}](https://github.com/litmuschaos/litmus/releases/tag/2.0.0-Beta9) | End of Life | Mar 05, 2021 | July 15, 2021 |
|
||||
| [2.x](https://github.com/litmuschaos/litmus/releases/tag/2.14.0) | End of Life | Aug 9, 2021 | September 5, 2023 |
|
||||
| [3.x beta](https://github.com/litmuschaos/litmus/releases/tag/3.0.0-beta12) | End of Life | Nov 16, 2022 | Nov 15, 2023 |
|
||||
| [3.x](https://github.com/litmuschaos/litmus/releases/tag/3.6.1) | Active | Oct 04, 2023 | active(release of 4.0 + 6 months), |
|
||||
| [4.0](https://github.com/litmuschaos/litmus/blob/master/ROADMAP.md) | Next | TBD | TBD |
|
||||
|
||||
> **_NOTE_** LitmusChaos v3.x will end of life at the same time as v4.x reaches full stability. Due to
|
||||
> [Minimal Version Selection](https://go.dev/ref/mod#minimal-version-selection) used
|
||||
> by Go modules, 3.x must be supported until EOL of all 3.x releases. Once 3.x is in
|
||||
> extended support, it will continue to accept security patches in addition to client
|
||||
> changes relevant for package importers.
|
||||
|
||||
### Kubernetes Support
|
||||
|
||||
The Kubernetes version matrix represents the versions of LitmusChaos which are
|
||||
recommended for a Kubernetes release. Any actively supported version of
|
||||
LitmusChaos may receive patches to fix bugs encountered in any version of
|
||||
Kubernetes, however, our recommendation is based on which versions have been
|
||||
the most thoroughly tested. See the [Kubernetes test grid](https://testgrid.k8s.io/sig-node-LitmusChaos)
|
||||
for the list of actively tested versions. Kubernetes only supports n-3 minor
|
||||
release versions and LitmusChaos will ensure there is always a supported version
|
||||
of LitmusChaos for every supported version of Kubernetes.
|
||||
|
||||
| Kubernetes Version | LitmusChaos Version |
|
||||
|---------------------|---------------------|
|
||||
| 1.26 | 1.x, 2.x, 3.x |
|
||||
| 1.27 | 3.x |
|
||||
| 1.28 | 3.x |
|
||||
| 1.29 | 3.x |
|
||||
| 1.30 | 3.x, 4.0(wip) |
|
||||
|
||||
|
||||
## Public API Stability
|
||||
|
||||
The following table provides an overview of the components covered by
|
||||
LitmusChaos versions:
|
||||
|
||||
|
||||
| Component | Status | Stabilized Version | Links |
|
||||
|------------------|----------|--------------------|---------------|
|
||||
| GraphQL API | Stable | 1.0 | [graphql API]() |
|
||||
| Go client API | Stable | 2.0 | [godoc]() |
|
||||
|
||||
|
||||
From the version stated in the above table, that component must adhere to the
|
||||
stability constraints expected in release versions.
|
||||
|
||||
Unless explicitly stated here, components that are called out as unstable or
|
||||
not covered may change in a future minor version. Breaking changes to
|
||||
"unstable" components will be avoided in patch versions.
|
||||
|
||||
Go client API stability includes the `client`, `defaults` and `version` package
|
||||
as well as all packages under `pkg`, `core`, `api` and `protobuf`.
|
||||
All packages under `cmd`, `contrib`, `integration`, and `internal` are not
|
||||
considered part of the stable client API.
|
||||
|
||||
|
||||
|
||||
### Release Checklist
|
||||
|
||||
* [ ] Release branch creation on litmus component repos
|
||||
|
|
57
ROADMAP.md
57
ROADMAP.md
|
@ -11,47 +11,62 @@ This document captures only the high level roadmap items. For the detailed backl
|
|||
- Per-experiment minimal RBAC permissions definition
|
||||
- Creation of 'scenarios' involving multiple faults via Argo-based Chaos Workflows (with examples for microservices apps like podtato-head and sock-shop)
|
||||
- Cross-Cloud Control Plane (Litmus Portal) to perform chaos against remote clusters
|
||||
- Helm3 charts for LitmusChaos (control plane and experiments)
|
||||
- Helm charts for LitmusChaos control plane
|
||||
- Helm Chart for LitmusChaos execution Plane
|
||||
- Support for admin mode (centralized chaos management) as well as namespaced mode (multi-tenant clusters)
|
||||
- Continuous chaos via flexible schedules, with support to halt/resume or (manual/conditional) abort experiments
|
||||
- Provide complete workflow termination/abort capability
|
||||
- Generation of observability data via Prometheus metrics and Kubernetes chaos events for experiments
|
||||
- Steady-State hypothesis validation before, during and after chaos injection via different probe types
|
||||
- Support for Docker, Containerd & CRI-O runtime
|
||||
- Support for scheduling policies (nodeSelector, tolerations) and resource definitions for chaos pods
|
||||
- ChaosHub refactor for 2.x user flow
|
||||
- Support for ARM64 nodes
|
||||
- Minimized role permissions for Chaos Service Accounts
|
||||
- Scaffolding scripts (SDK) to help bootstrap a new chaos experiment in Go, Python, Ansible
|
||||
- Support orchestration of non-native chaos libraries via the BYOC (Bring-Your-Own-Chaos) model
|
||||
- Support for OpenShift platform
|
||||
- Workflow YAML linter addition
|
||||
- Integration tests & e2e framework creation for control plane components and chaos experiments
|
||||
- Documentation (usage guide for chaos operator, resources & developer guide for new experiment creation)
|
||||
- Improved documentation and tutorials for Litmus Portal based execution flow
|
||||
- Add architecture details & design resources
|
||||
- Define community sync up cadence and structure
|
||||
|
||||
------
|
||||
|
||||
### In-Progress (Under Active Development)
|
||||
### In-Progress (Under Design OR Active Development)
|
||||
|
||||
- Support for all ChaosEngine schema elements within workflow wizard
|
||||
- Workflow YAML linter addition
|
||||
- Minimized role permissions for Chaos Service Accounts
|
||||
- Chaos-center users account to chaosService account map
|
||||
- Provide complete workflow termination/abort capability
|
||||
- Cross-hub experiment support within a Chaos Workflow
|
||||
- Helm Chart for Chaos Execution Plane
|
||||
- Enhanced CRD schema for ChaosEngine to support advanced CommandProbe configuration
|
||||
- Support for S3 artifact sink (helps performance/benchmark runs)
|
||||
- ChaosHub refactor for 2.x user flow
|
||||
- Chaos experiments against virtual machines and cloud infrastructure (AWS, GCP, Azure, VMWare, Baremetal)
|
||||
- Improved documentation and tutorials for Litmus Portal based execution flow
|
||||
- Off the shelf chaos-integrated monitoring dashboards for application chaos categories
|
||||
- Support for user defined chaos experiment result definition
|
||||
- Increased fault injection types (IOChaos, HTTPChaos, JVMChaos)
|
||||
- Special Interest Groups (SIGs) around specific areas in the project to take the roadmap forward
|
||||
- Native Chaos Workflows with redesigned subscriber to improve resource delegation, enabling seamless and efficient execution of chaos workflows within Kubernetes clusters.
|
||||
- Introduce transient runners to improve resource efficiency during chaos experiments by dynamically creating and cleaning up chaos runner instances.
|
||||
- Implement Kubernetes connectors to enable streamlined integration with Kubernetes clusters, providing simplified authentication and configuration management.
|
||||
- Integrate with tools like K8sGPT to generate insightful reports that identify potential weaknesses in your Kubernetes environment before executing chaos experiments.
|
||||
- Add Terraform support for defining and executing chaos experiments on infrastructure components, enabling infrastructure-as-code-based chaos engineering.
|
||||
- Add SDK support for Python and Java, with potential extensions to other programming languages based on community interest.
|
||||
- Include in-product documentation, such as tooltips, to improve user experience and ease of adoption.
|
||||
- Implement the litmus-java-sdk with a targeted v1.0.0 release by Q1.
|
||||
- Integrate distributed tracing by adding attributes or events to spans, and create an OpenTelemetry demo showcasing chaos engineering observability.
|
||||
- Enhance the exporter to function as an OpenTelemetry collector, providing compatibility with existing observability pipelines.
|
||||
- Add support for DocumentDB by replacing certain MongoDB operations, improving flexibility for database chaos.
|
||||
- Upgrade Kubernetes SDK from version 1.21 to 1.26 to stay aligned with the latest Kubernetes features and enhancements.
|
||||
- Refactor the chaos charts to:
|
||||
- Replace latest tags with specific, versioned image tags.
|
||||
- Consolidate multiple images into a single optimized image.
|
||||
- Update GraphQL and authentication API documentation for improved clarity and user guidance.
|
||||
- Add comprehensive unit and fuzz tests to enhance code reliability and robustness.
|
||||
- Implement out-of-the-box Slack integration for better collaboration and monitoring during chaos experiments.
|
||||
|
||||
------
|
||||
|
||||
### Backlog
|
||||
|
||||
- Pre-defined chaos workflows to inject chaos during application benchmark runs
|
||||
- Support for cloudevents compliant chaos events
|
||||
- Improved application Chaos Suites for various CNCF projects
|
||||
- Validation support for all ChaosEngine schema elements within workflow wizard
|
||||
- Chaos-center users account to chaosService account map
|
||||
- Cross-hub experiment support within a Chaos Workflow
|
||||
- Enhanced CRD schema for ChaosEngine to support advanced CommandProbe configuration
|
||||
- Support for S3 artifact sink (helps performance/benchmark runs)
|
||||
- Chaos experiments against virtual machines and cloud infrastructure (AWS, GCP, Azure, VMWare, Baremetal)
|
||||
- Off the shelf chaos-integrated monitoring dashboards for application chaos categories
|
||||
- Support for user defined chaos experiment result definition
|
||||
- Increased fault injection types (IOChaos, HTTPChaos, JVMChaos)
|
||||
- Special Interest Groups (SIGs) around specific areas in the project to take the roadmap forward
|
||||
|
|
89
SECURITY.md
89
SECURITY.md
|
@ -1,11 +1,33 @@
|
|||
# SECURITY
|
||||
|
||||
This page borrows parts of its contents from https://kubernetes.io/security/
|
||||
|
||||
## Report a Vulnerability
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We are extremely grateful for security researchers and users that report vulnerabilities to the LitmusChaos Open Source Community. All reports are thoroughly investigated by a set of community members.
|
||||
|
||||
To report a litmuschaos vulnerability, either:
|
||||
|
||||
1. Report it on Github directly:
|
||||
|
||||
Navigate to the security tab on the repository
|
||||

|
||||
|
||||
Click on 'Advisories'
|
||||

|
||||
|
||||
Click on 'Report a vulnerability'
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
2. Send an email to `litmuschaos@gmail.com` detailing the issue and steps
|
||||
to reproduce.
|
||||
|
||||
The reporter(s) can expect a response within 24 hours acknowledging
|
||||
the issue was received. If a response is not received within 24 hours, please
|
||||
reach out to any committer directly to confirm receipt of the issue.
|
||||
|
||||
|
||||
To make a report, submit your vulnerability to all security contacts of LitmusChaos [listed below](#security-contacts). This allows triage and handling of the vulnerability with standardized response times.
|
||||
|
||||
### When Should I Report a Vulnerability?
|
||||
|
@ -20,6 +42,63 @@ To make a report, submit your vulnerability to all security contacts of LitmusCh
|
|||
- You need help applying security-related updates
|
||||
- Your issue is not security-related
|
||||
|
||||
|
||||
## Review Process
|
||||
|
||||
Once a committer has confirmed the relevance of the report, a draft security
|
||||
advisory will be created on Github. The draft advisory will be used to discuss
|
||||
the issue with committers, the reporter(s), and litmuschaos's security advisors.
|
||||
If the reporter(s) wishes to participate in this discussion, then provide
|
||||
reporter Github username(s) to be invited to the discussion. If the reporter(s)
|
||||
does not wish to participate directly in the discussion, then the reporter(s)
|
||||
can request to be updated regularly via email.
|
||||
|
||||
If the vulnerability is accepted, a timeline for developing a patch, public
|
||||
disclosure, and patch release will be determined. If there is an embargo period
|
||||
on public disclosure before the patch release, an announcment will be sent to
|
||||
the security announce mailing list announcing the scope of the vulnerability, the date of availability of the
|
||||
patch release, and the date of public disclosure. The reporter(s) are expected
|
||||
to participate in the discussion of the timeline and abide by agreed upon dates
|
||||
for public disclosure.
|
||||
|
||||
## Security Vulnerability Response
|
||||
|
||||
Each report is acknowledged and analyzed by the security contacts within 5 working days. This will set off the [Security Release Process](#process).
|
||||
|
||||
Any vulnerability information shared with the LitmusChaos security contacts stays within LitmusChaos project and will not be disseminated to other projects unless it is necessary to get the issue fixed.
|
||||
|
||||
## Public Disclosure Timing
|
||||
|
||||
A public disclosure date is negotiated by the LitmusChaos Security Committee and the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for vendor coordination. The timeframe for disclosure is from immediate (especially if it is already publicly known) to a few weeks. For a vulnerability with a straightforward mitigation, we expect report date to disclosure date to be on the order of 7 days. The LitmusChaos Security Committee holds the final say when setting a disclosure date.
|
||||
|
||||
## Process
|
||||
|
||||
If you find a security-related bug in LitmusChaos, we kindly ask you for responsible disclosure and for giving us appropriate time to react, analyze, and develop a fix to mitigate the found security vulnerability. The security contact will investigate the issue within 5 working days.
|
||||
|
||||
The team will react promptly to fix the security issue and its workaround/fix will be published on our release notes.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
See the [litmuschaos release page]()
|
||||
for information on supported versions of litmuschaos. Any `Extended` or `Active`
|
||||
release branch may receive security updates. For any security issues discovered
|
||||
on older versions, non-core packages, or dependencies, please inform committers
|
||||
using the same security mailing list as for reporting vulnerabilities.
|
||||
|
||||
## Joining the security announce mailing list
|
||||
|
||||
Any organization or individual who directly uses litmuschaos and non-core
|
||||
packages in production or in a security critical application is eligible to join
|
||||
the security announce mailing list. Indirect users who use litmuschaos through a
|
||||
vendor are not expected to join, but should request their vendor join. To join
|
||||
the mailing list, the individual or organization must be sponsored by either a
|
||||
litmuschaos committer or security advisor as well as have a record of properly
|
||||
handling non-public security information. If a sponsor cannot be found,
|
||||
sponsorship may be requested at `litmuschaos@gmail.com`. Sponsorship should not
|
||||
be requested via public channels since membership of the security announce list
|
||||
is not public.
|
||||
|
||||
|
||||
## Security Vulnerability Response
|
||||
|
||||
Each report is acknowledged and analyzed by the security contacts within 5 working days. This will set off the [Security Release Process](#process).
|
||||
|
@ -40,6 +119,6 @@ The team will react promptly to fix the security issue and its workaround/fix wi
|
|||
|
||||
Defined below are the security contacts for this repository. In case you identify any security issue, please reach out to all of the security contacts.
|
||||
|
||||
- @ksatchit (karthik satchitanand, karthik@chaosnative.com)
|
||||
- @rajdas98 (raj babu das, raj@chaosnative.com)
|
||||
- @ksatchit (karthik satchitanand, karthik.s@harness.io)
|
||||
- @rajdas98 (raj babu das, raj.das@harness.io)
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Amadeus
|
||||
[Amadeus](https://amadeus.com/) technology powers the global travel and tourism industry.
|
||||
|
||||
From airlines to search engines, travel agencies to hotels, the world's top travel brands rely on Amadeus to help create exceptional traveler experiences.
|
||||
|
||||
## How do we use Litmus.
|
||||
We are using Litmus for the following 3 topics:
|
||||
- **Identify weakness** by injecting a wide variety of disruptions to catch bugs and gaps in the stability of our applications
|
||||
- **Build confidence in the resiliency** by introducing disruptions that activate our resiliency mechanisms to ensure they are working as expected.
|
||||
- **Validate fixes** by recreating specific conditions and disruptions, we can reproduce complex production incidents and validate the fixes deployed to resolve them.
|
||||
|
||||
## Benefits in using Litmus.
|
||||
|
||||
We are finding the following benefits in Litmus
|
||||
- **Open Source**: Allows us to contribute new features and fix bugs based on feedback from our Chaos users.
|
||||
- **QA-Friendly**: Through the UI and YAML-based configuration, it allows QA profiles with limited SRE knowledge to easily create their own experiments.
|
||||
- **Extensibility**: As Chaos Scenarios are based on ArgoWorkflow, it allows for the inclusion of custom steps, such as updating a configuration before/after the experiments.
|
||||
- **Variety of Disruption Types**: Which satisfies our current Chaos users.
|
|
@ -0,0 +1,13 @@
|
|||
## Emirates NBD
|
||||
|
||||
[Emirates NBD](https://www.emiratesnbd.com) is Dubai's government-owned bank and is one of the largest banking groups in the Middle East in terms of assets.
|
||||
|
||||
### **Why do we use Litmus.**
|
||||
|
||||
Resilience is a key aspect in creating fault-tolerant environments, and leveraging tools like Litmus has been instrumental in automating resilience testing. Litmus has enabled us to simulate real-time chaos scenarios, allowing us to thoroughly verify the robustness of both our infrastructure and applications.
|
||||
|
||||
### **How do we use Litmus.**
|
||||
|
||||
We began with a proof of concept (POC) on a playground cluster. While we explored other tools during this process, Litmus stood out significantly, not only in its capabilities but also due to its excellent user interface. Although we faced a few challenges during the initial setup of Litmus on OpenShift, the team provided timely support, helping us overcome these obstacles and successfully complete the POC.
|
||||
|
||||
Now, we've successfully deployed Litmus in a non-production cluster environment, and our SRE team is in the process of transitioning from manual chaos testing to automated chaos tests. This shift will enable us to schedule, automate, and efficiently track the outcomes of these tests, enhancing the resilience of our systems.
|
|
@ -0,0 +1,30 @@
|
|||
## OutSystems
|
||||
|
||||
[OutSystems](https://www.outsystems.com/) is a low-code development platform which provides tools for companies to develop, deploy and manage omnichannel enterprise applications. OutSystems was founded in 2001 in Lisbon, Portugal. In June 2018 OutSystems secured a $360M round of funding from KKR and Goldman Sachs and reached the status of Unicorn.
|
||||
|
||||
### **Leveraging Litmus Chaos Engineering in Kubernetes Infrastructure:**
|
||||
|
||||
We have a Kubernetes-based infrastructure pivotal to our operations, where reliability and resilience are paramount. Recognizing the need for robust testing methodologies, we turned to Litmus Chaos Engineering to fortify our systems against potential failures and to ensure seamless operations even under adverse conditions.
|
||||
|
||||
### **Why do we use Litmus:**
|
||||
|
||||
Litmus emerged as our tool of choice due to its comprehensive suite of chaos engineering capabilities tailored specifically for Kubernetes environments. Its versatility in orchestrating controlled chaos experiments aligns perfectly with our commitment to enhancing system reliability while maintaining agility.
|
||||
|
||||
### **Use Case and Implementation:**
|
||||
|
||||
We have seamlessly integrated Litmus Chaos Engineering into various stages of our development and deployment pipeline, spanning from development and testing to staging and production environments. Leveraging Litmus, we meticulously craft and execute chaos experiments, meticulously observing how our infrastructure behaves under stress, and ensuring it meets our predefined Service Level Objectives (SLOs) and Service Level Indicators (SLIs).
|
||||
|
||||
### **Achievements:**
|
||||
|
||||
Our journey with Litmus Chaos Engineering has been marked by significant milestones:
|
||||
|
||||
- Successful deployment of Chaos Center and Litmus Delegate, empowering us with centralized chaos management capabilities.
|
||||
- Establishment of secure access to Chaos Center through HTTPS, coupled with domain customization for enhanced usability.
|
||||
- Implementation of WAF ACL to restrict access to Chaos Center, ensuring secure interactions.
|
||||
- Integration of Azure SSO for streamlined user management and authentication.
|
||||
- Seamless connectivity between Chaos Center and target nodes, facilitating efficient chaos experimentation.
|
||||
- Execution of numerous successful experiments, validating the resilience and scalability of our infrastructure.
|
||||
|
||||
### **Next Steps:**
|
||||
|
||||
As we continue to harness the power of Litmus Chaos Engineering, we remain committed to expanding our chaos engineering initiatives, further refining our chaos experiments, and continually enhancing the resilience of our Kubernetes infrastructure.
|
|
@ -0,0 +1,32 @@
|
|||
## PokerBaazi
|
||||
|
||||
[PokerBaazi](https://www.pokerbaazi.com/) is India's biggest online poker platform providing an unparalleled world-class experience. Home Grown and 8 years of calling it our own, today, we have a strong and loyal user base of 40 LAC+ Indians.
|
||||
|
||||
### **Applications/Workloads or Infra that are being subjected to chaos by Litmus.**
|
||||
|
||||
At PokerBaazi, we leverage Litmus Chaos to subject critical components of our infrastructure to controlled chaos experiments. These include:
|
||||
|
||||
- Microservices Infrastructure: Our backend is designed as a microservices architecture, running on Kubernetes. We conduct experiments on inter-service communication, API latencies, and service resilience during node failures or resource constraints.
|
||||
- Load Balancers and Networking: We simulate disruptions in networking, such as packet drops or DNS failures, to ensure our applications maintain connectivity and continue serving users.
|
||||
- Application Workloads: High-demand applications like our gaming engine and payment/promotions api's are put under stress to evaluate their fault tolerance and recovery mechanisms during peak loads or unexpected outages.
|
||||
|
||||
### **Why do we use Litmus.**
|
||||
|
||||
We chose Litmus Chaos for several compelling reasons:
|
||||
|
||||
- Kubernetes-Native Integration: Since our infrastructure is heavily Kubernetes-based, Litmus seamlessly integrates with our stack, making it a natural fit.
|
||||
- Ease of Use and Open-Source: Litmus offers a user-friendly interface along with robust documentation, allowing our teams to adopt it quickly without steep learning curves.
|
||||
- Custom Experiment Support: The ability to create tailored chaos experiments aligned with our specific workloads ensures we can target critical failure scenarios unique to our ecosystem.
|
||||
- Community Support and Scalability: Being an open-source project with an active community, Litmus evolves rapidly, allowing us to leverage the latest chaos engineering methodologies and tools.
|
||||
|
||||
Litmus has been instrumental in identifying hidden weaknesses in our system, such as unexpected dependencies or cascading failures. This has enabled us to proactively address potential issues, enhance system resilience, and meet our uptime commitments.
|
||||
|
||||
### **Where are we using Litmus.**
|
||||
|
||||
We use Litmus Chaos in various environments to ensure robust testing at every stage of development:
|
||||
|
||||
- Development: Initial chaos experiments are conducted in isolated dev environments to identify basic resilience issues and ensure service fault tolerance during early-stage development.
|
||||
- Staging/Pre-Production: In staging, we run more comprehensive chaos scenarios simulating real-world failures, such as pod crashes, resource exhaustion, or external API downtime, to ensure the production-like environment is resilient.
|
||||
- Production: Selected, low-risk chaos experiments are conducted in production under strict supervision to verify real-time system robustness and validate SLAs in live conditions.
|
||||
|
||||
Litmus Chaos has transformed our approach to building and maintaining a highly resilient gaming platform, allowing us to deliver exceptional user experiences while preparing for the unexpected.
|
|
@ -0,0 +1,11 @@
|
|||
# Wingie Enuygun Company
|
||||
[Wingie Enuygun Company](https://www.wingie.com/) is a leading travel and technology company providing seamless travel solutions across various platforms.
|
||||
|
||||
## Why do we use Litmus
|
||||
We use Litmus to identify bottlenecks in our systems, detect issues early, and foresee potential errors. This allows us to take proactive measures and maintain the resilience and performance of our infrastructure.
|
||||
|
||||
## How do we use Litmus
|
||||
Litmus is integrated into our QA cycles, where it plays a crucial role in catching bugs and verifying the overall resilience of our systems.
|
||||
|
||||
## Benefits in using Litmus
|
||||
Litmus chaos experiments are straightforward to implement and can be easily customized or extended to meet our specific requirements, enabling us to effectively manage and optimize our systems at Wingie Enuygun.
|
|
@ -97,15 +97,21 @@ backend-services-checks:
|
|||
# && exit 1; \
|
||||
# fi
|
||||
|
||||
unit-tests:
|
||||
backend-unit-tests:
|
||||
@echo "------------------"
|
||||
@echo "--> Running unit tests"
|
||||
@echo "--> Running backend unit tests"
|
||||
@echo "------------------"
|
||||
@cd graphql-server && go test -cover ./...
|
||||
# @cd authentication && go test -v ./...
|
||||
# @cd cluster-agents/subscriber && go test -v ./...
|
||||
@cd graphql/server && go test -cover ./...
|
||||
@cd authentication && go test -v ./...
|
||||
@#cd cluster-agents/subscriber && go test -v ./...
|
||||
# @cd cluster-agents/event-tracker && go test -v ./...
|
||||
|
||||
web-unit-tests:
|
||||
@echo "------------------"
|
||||
@echo "--> Running frontend unit tests"
|
||||
@echo "------------------"
|
||||
@cd web && yarn -s && yarn test --coverage
|
||||
|
||||
.PHONY: docker.buildx
|
||||
docker.buildx:
|
||||
@echo "------------------------------"
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# [LitmusChaos 3.0.0](https://docs.litmuschaos.io/)
|
||||
|
||||
## Installation steps for Litmus 3.0.0
|
||||
|
||||
### Mongo installation via Helm - Bitnami Mongo
|
||||
|
||||
```shell
|
||||
helm repo add bitnami https://charts.bitnami.com/bitnami
|
||||
```
|
||||
|
||||
#### Mongo Values
|
||||
|
||||
```shell
|
||||
auth:
|
||||
enabled: true
|
||||
rootPassword: "1234"
|
||||
# -- existingSecret Existing secret with MongoDB(®) credentials (keys: `mongodb-passwords`, `mongodb-root-password`, `mongodb-metrics-password`, ` mongodb-replica-set-key`)
|
||||
existingSecret: ""
|
||||
architecture: replicaset
|
||||
replicaCount: 3
|
||||
persistence:
|
||||
enabled: true
|
||||
volumePermissions:
|
||||
enabled: true
|
||||
metrics:
|
||||
enabled: false
|
||||
prometheusRule:
|
||||
enabled: false
|
||||
|
||||
# bitnami/mongodb is not yet supported on ARM.
|
||||
# Using unofficial tools to build bitnami/mongodb (arm64 support)
|
||||
# more info: https://github.com/ZCube/bitnami-compat
|
||||
#image:
|
||||
# registry: ghcr.io/zcube
|
||||
# repository: bitnami-compat/mongodb
|
||||
# tag: 6.0.5
|
||||
```
|
||||
|
||||
```shell
|
||||
helm install my-release bitnami/mongodb --values mongo-values.yml -n <NAMESPACE> --create-namespace
|
||||
```
|
||||
|
||||
### Apply the Manifest
|
||||
|
||||
Applying the manifest file will install all the required service account configuration and ChaosCenter.
|
||||
|
||||
```shell
|
||||
kubectl apply -f https://litmuschaos.github.io/litmus/3.0.0-beta13/litmus-3.0.0.yaml
|
||||
```
|
|
@ -0,0 +1,123 @@
|
|||
# README for Setting Up API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This guide details the steps to set up and generate API documentation for your project using Swagger and GoSwagger. Swagger is used to create an OpenAPI specification file (`swagger.yaml`), and GoSwagger serves this specification file on a local server.
|
||||
.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before beginning, ensure that you have the following installed:
|
||||
|
||||
- Go programming language environment
|
||||
- `swaggo/swag` library
|
||||
- `goswagger.io` tool
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Install Swagger
|
||||
|
||||
First, you need to install `swag`, a tool for generating Swagger 2.0 documents for Go applications. Use the following command to install it:
|
||||
|
||||
```bash
|
||||
go get -u github.com/swaggo/swag/cmd/swag
|
||||
```
|
||||
|
||||
For more details, visit the [swag GitHub repository](https://github.com/swaggo/swag).
|
||||
|
||||
### Step 2: Install GoSwagger
|
||||
|
||||
Next, install GoSwagger, which will serve your Swagger specification file:
|
||||
|
||||
```bash
|
||||
go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
```
|
||||
|
||||
For additional information, refer to the [GoSwagger website](https://goswagger.io/).
|
||||
|
||||
## Setting Up Documentation
|
||||
|
||||
### Step 1: Annotate Your API
|
||||
|
||||
You need to annotate your Go functions to define the API specifications. These annotations are used by Swagger to generate documentation.
|
||||
|
||||
#### Example Annotation:
|
||||
|
||||
```go
|
||||
// DexLogin godoc
|
||||
//
|
||||
// @Description DexRouter creates all the required routes for OAuth purposes.
|
||||
// @Tags DexRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Response{}
|
||||
// @Router /dex/login [get]
|
||||
//
|
||||
// DexLogin handles and redirects to DexServer to proceed with OAuth
|
||||
func DexLogin() gin.HandlerFunc {
|
||||
// ... function implementation ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Formatting:
|
||||
|
||||
After adding the annotations run this command to fix and update the annotation formatting
|
||||
|
||||
```bash
|
||||
swag fmt .
|
||||
```
|
||||
|
||||
### Step 3: Define Response Structures in `doc.go`
|
||||
|
||||
In your handler folder, create or update a file named `doc.go`. Here, define the structures for your responses and errors.
|
||||
|
||||
#### Example Structures:
|
||||
|
||||
```go
|
||||
package handler
|
||||
|
||||
// LoginResponse represents the response structure for login.
|
||||
type LoginResponse struct {
|
||||
accessToken string
|
||||
projectID string
|
||||
projectRole string
|
||||
expiresIn string
|
||||
}
|
||||
|
||||
// ErrServerError represents an error structure for server errors.
|
||||
type ErrServerError struct {
|
||||
Code int `json:"code" example:"500"`
|
||||
Message string `json:"message" example:"Unexpected server error"`
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Generate Swagger Documentation
|
||||
|
||||
After annotating your API and defining your responses, run the following command in your project root to generate the `swagger.yaml` file:
|
||||
|
||||
```bash
|
||||
swag init --parseDependency true
|
||||
```
|
||||
|
||||
This command scans your project and creates a Swagger specification from your annotations.
|
||||
|
||||
### Step 5: Serve the Swagger Specification
|
||||
|
||||
Finally, use GoSwagger to serve your Swagger specification file. This allows you to view your API documentation in a web browser. By default the API docs will be generated with Redocly.
|
||||
|
||||
```bash
|
||||
swagger serve swagger.yaml
|
||||
```
|
||||
|
||||
To run the orginal swagger format
|
||||
|
||||
```bash
|
||||
swagger serve -F=swagger swagger.yaml
|
||||
```
|
||||
|
||||
This command starts a local server hosting your API documentation.
|
||||
|
||||
## Conclusion
|
||||
|
||||
With these steps, you should now have a fully functional API documentation setup using Swagger and GoSwagger. Remember to regularly update your annotations and regenerate the Swagger file to keep your documentation in sync with your API.
|
|
@ -1,5 +1,5 @@
|
|||
# BUILD STAGE
|
||||
FROM golang:1.20 AS builder
|
||||
FROM golang:1.22 AS builder
|
||||
|
||||
ARG TARGETOS=linux
|
||||
ARG TARGETARCH
|
||||
|
@ -15,13 +15,18 @@ RUN go env
|
|||
RUN CGO_ENABLED=0 go build -o /output/server -v ./api/
|
||||
|
||||
# Packaging stage
|
||||
# Image source: https://github.com/litmuschaos/test-tools/blob/master/custom/hardened-alpine/infra/Dockerfile
|
||||
# The base image is non-root (have litmus user) with default litmus directory.
|
||||
FROM litmuschaos/infra-alpine
|
||||
# Use RedHat UBI minimal image as base
|
||||
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5
|
||||
|
||||
LABEL maintainer="LitmusChaos"
|
||||
|
||||
COPY --from=builder /output/server /litmus
|
||||
ENV APP_DIR="/litmus"
|
||||
|
||||
COPY --from=builder /output/server $APP_DIR/
|
||||
RUN chown 65534:0 $APP_DIR/server && chmod 755 $APP_DIR/server
|
||||
|
||||
WORKDIR $APP_DIR
|
||||
USER 65534
|
||||
|
||||
CMD ["./server"]
|
||||
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Response string
|
||||
}
|
||||
|
||||
type ApiTokenResponse struct {
|
||||
UserID string
|
||||
Name string
|
||||
Token string
|
||||
ExpiresAt int64
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
type Role string
|
||||
type UserResponse struct {
|
||||
ID string `bson:"_id,omitempty" json:"userID"`
|
||||
Username string `bson:"username,omitempty" json:"username"`
|
||||
Password string `bson:"password,omitempty" json:"password,omitempty"`
|
||||
Email string `bson:"email,omitempty" json:"email,omitempty"`
|
||||
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||
Role Role `bson:"role,omitempty" json:"role"`
|
||||
DeactivatedAt *int64 `bson:"deactivated_at,omitempty" json:"deactivatedAt,omitempty"`
|
||||
}
|
||||
|
||||
type CapabilitiesResponse struct {
|
||||
Dex struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
} `json:"dex"`
|
||||
}
|
||||
|
||||
type MessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type NewApiToken struct {
|
||||
accessToken string
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
accessToken string
|
||||
projectID string
|
||||
projectRole string
|
||||
expiresIn string
|
||||
}
|
||||
|
||||
// HTTPError example
|
||||
|
||||
func NewError(ctx *gin.Context, status int, err error) {
|
||||
er := HTTPError{
|
||||
Code: status,
|
||||
Message: err.Error(),
|
||||
}
|
||||
ctx.JSON(status, er)
|
||||
}
|
||||
|
||||
type HTTPError struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"status bad request"`
|
||||
}
|
||||
|
||||
type ErrServerError struct {
|
||||
Code int `json:"code" example:"500"`
|
||||
Message string `json:"message" example:"The authorization server encountered an unexpected condition that prevented it from fulfilling the request"`
|
||||
}
|
||||
type ErrInvalidCredentials struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"Invalid Credentials"`
|
||||
}
|
||||
|
||||
type ErrInvalidRequest struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed"`
|
||||
}
|
||||
|
||||
type ErrOldPassword struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"The old and new passwords can't be same"`
|
||||
}
|
||||
|
||||
type ErrUnauthorized struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"The user does not have requested authorization to access this resource"`
|
||||
}
|
||||
|
||||
type ErrUserExists struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"This username is already assigned to another user"`
|
||||
}
|
||||
|
||||
type ErrUserNotFound struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"user does not exist"`
|
||||
}
|
||||
|
||||
type ErrUserDeactivated struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"your account has been deactivated"`
|
||||
}
|
||||
|
||||
type ErrStrictPasswordPolicyViolation struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"Please ensure the password is atleast 8 characters and atmost 16 characters long and has atleast 1 digit, 1 lowercase alphabet, 1 uppercase alphabet and 1 special character"`
|
||||
}
|
||||
|
||||
type ErrStrictUsernamePolicyViolation struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"The username should be atleast 3 characters long and atmost 16 characters long."`
|
||||
}
|
||||
|
||||
type ErrEmptyProjectName struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"Project name can't be empty"`
|
||||
}
|
||||
|
||||
type ErrInvalidRole struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"Role is invalid"`
|
||||
}
|
||||
|
||||
type ErrProjectNotFound struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"This project does not exist"`
|
||||
}
|
||||
|
||||
type ErrInvalidEmail struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"Email address is invalid"`
|
||||
}
|
||||
|
||||
type ErrProjectNotFoundstruct struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"project does not exist"`
|
||||
}
|
||||
|
||||
type ReadinessAPIStatus struct {
|
||||
DataBase string `json:"database"`
|
||||
Collections string `json:"collections"`
|
||||
}
|
||||
|
||||
type APIStatus struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type UserWithProject struct {
|
||||
Data entities.UserWithProject `json:"data"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Data entities.Project `json:"data"`
|
||||
}
|
||||
|
||||
type Projects struct {
|
||||
Data []*entities.Project `json:"data"`
|
||||
}
|
||||
|
||||
type ListProjectResponse struct {
|
||||
Data entities.ListProjectResponse `json:"data"`
|
||||
}
|
||||
|
||||
type ProjectStats struct {
|
||||
Data []*entities.ProjectStats `json:"data"`
|
||||
}
|
||||
|
||||
type Members struct {
|
||||
Data []*entities.Member `json:"data"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Data entities.Member `json:"data"`
|
||||
}
|
||||
|
||||
type ListInvitationResponse struct {
|
||||
Data []entities.ListInvitationResponse `json:"data"`
|
||||
}
|
||||
|
||||
type ProjectRole struct {
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type ProjectIDWithMessage struct {
|
||||
Message string `json:"message"`
|
||||
ProjectID string `json:"projectID"`
|
||||
}
|
|
@ -20,6 +20,11 @@ func (s *ServerGrpc) ValidateRequest(ctx context.Context,
|
|||
return &protos.ValidationResponse{Error: err.Error(), IsValid: false}, err
|
||||
}
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
|
||||
if claims["uid"] == nil {
|
||||
return &protos.ValidationResponse{Error: "token is invalid", IsValid: false}, err
|
||||
}
|
||||
|
||||
uid := claims["uid"].(string)
|
||||
err = validations.RbacValidator(uid, inputRequest.ProjectId,
|
||||
inputRequest.RequiredRoles, inputRequest.Invitation, s.ApplicationService)
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package grpc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/grpc"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestGetProjectById(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
projectID string
|
||||
mockGetProjectResponse *entities.Project
|
||||
mockGetProjectError error
|
||||
mockFindUsersResponse *[]entities.User
|
||||
mockFindUsersError error
|
||||
expectedResponse *protos.GetProjectByIdResponse
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "PositiveTestProjectExists",
|
||||
projectID: "project-id",
|
||||
mockGetProjectResponse: &entities.Project{
|
||||
ID: "project-id",
|
||||
Name: "test-project",
|
||||
Members: []*entities.Member{
|
||||
{
|
||||
UserID: "user-1",
|
||||
Invitation: entities.PendingInvitation,
|
||||
JoinedAt: 1234567890,
|
||||
},
|
||||
},
|
||||
},
|
||||
mockGetProjectError: nil,
|
||||
mockFindUsersResponse: &[]entities.User{
|
||||
{
|
||||
ID: "user-1",
|
||||
Email: "user1@email.com",
|
||||
Username: "user1",
|
||||
},
|
||||
},
|
||||
mockFindUsersError: nil,
|
||||
expectedResponse: &protos.GetProjectByIdResponse{
|
||||
Id: "project-id",
|
||||
Name: "test-project",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "NegativeTestProjectDoesNotExist",
|
||||
projectID: "non-existing-project-id",
|
||||
mockGetProjectResponse: nil,
|
||||
mockGetProjectError: errors.New("project not found"),
|
||||
mockFindUsersResponse: nil,
|
||||
mockFindUsersError: nil,
|
||||
expectedResponse: nil,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
s := &grpc.ServerGrpc{
|
||||
ApplicationService: &mocks.MockedApplicationService{},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mockService := s.ApplicationService.(*mocks.MockedApplicationService)
|
||||
mockService.On("GetProjectByProjectID", tc.projectID).Return(tc.mockGetProjectResponse, tc.mockGetProjectError)
|
||||
if tc.mockFindUsersResponse != nil {
|
||||
mockService.On("FindUsersByUID", mock.Anything).Return(tc.mockFindUsersResponse, tc.mockFindUsersError)
|
||||
}
|
||||
|
||||
req := &protos.GetProjectByIdRequest{
|
||||
ProjectID: tc.projectID,
|
||||
}
|
||||
|
||||
resp, err := s.GetProjectById(ctx, req)
|
||||
|
||||
if tc.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, tc.expectedResponse.Name, resp.Name)
|
||||
}
|
||||
mockService.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserById(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userID string
|
||||
mockServiceResponse *entities.User
|
||||
mockServiceError error
|
||||
expectedResponse *protos.GetUserByIdResponse
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "PositiveTestUserExists",
|
||||
userID: "user-id",
|
||||
mockServiceResponse: &entities.User{
|
||||
ID: "user-id",
|
||||
Name: "test-user",
|
||||
Username: "username",
|
||||
DeactivatedAt: nil,
|
||||
Role: "admin",
|
||||
Email: "user@email.com",
|
||||
},
|
||||
mockServiceError: nil,
|
||||
expectedResponse: &protos.GetUserByIdResponse{
|
||||
Id: "user-id",
|
||||
Name: "test-user",
|
||||
Email: "user@email.com",
|
||||
DeactivatedAt: "nil",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "NegativeTestUserDoesNotExist",
|
||||
userID: "non-existing-user-id",
|
||||
mockServiceResponse: nil,
|
||||
mockServiceError: errors.New("user not found"),
|
||||
expectedResponse: nil,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
s := &grpc.ServerGrpc{
|
||||
ApplicationService: &mocks.MockedApplicationService{},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mockService := s.ApplicationService.(*mocks.MockedApplicationService)
|
||||
mockService.On("GetUser", tc.userID).Return(tc.mockServiceResponse, tc.mockServiceError)
|
||||
|
||||
req := &protos.GetUserByIdRequest{
|
||||
UserID: tc.userID,
|
||||
}
|
||||
|
||||
resp, err := s.GetUserById(ctx, req)
|
||||
|
||||
if tc.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, tc.expectedResponse.Name, resp.Name)
|
||||
assert.Equal(t, tc.expectedResponse.Email, resp.Email)
|
||||
}
|
||||
|
||||
mockService.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
)
|
||||
|
||||
// GetCapabilities godoc
|
||||
//
|
||||
// @Summary Get capabilities of Auth Server.
|
||||
// @Description Returns capabilities that can be leveraged by frontend services to toggle certain features.
|
||||
// @Tags CapabilitiesRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.CapabilitiesResponse{}
|
||||
// @Router /capabilities [get]
|
||||
//
|
||||
// GetCapabilities returns the capabilities of the Auth Server.
|
||||
func GetCapabilities() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
capabilities := new(response.CapabilitiesResponse)
|
||||
capabilities.Dex.Enabled = utils.DexEnabled
|
||||
c.JSON(200, capabilities)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCapabilities(t *testing.T) {
|
||||
|
||||
testcases := []struct {
|
||||
Name string
|
||||
DexEnabled bool
|
||||
}{
|
||||
{
|
||||
Name: "Dex Enabled",
|
||||
DexEnabled: true,
|
||||
},
|
||||
{
|
||||
Name: "Dex Disabled",
|
||||
DexEnabled: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
test := test
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
utils.DexEnabled = test.DexEnabled
|
||||
|
||||
rest.GetCapabilities()(ctx)
|
||||
capa := response.CapabilitiesResponse{}
|
||||
err := json.Unmarshal(w.Body.Bytes(), &capa)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, test.DexEnabled, capa.Dex.Enabled)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -34,6 +34,16 @@ func oAuthDexConfig() (*oauth2.Config, *oidc.IDTokenVerifier, error) {
|
|||
}, provider.Verifier(&oidc.Config{ClientID: utils.DexClientID}), nil
|
||||
}
|
||||
|
||||
// DexLogin godoc
|
||||
//
|
||||
// @Description DexRouter creates all the required routes for OAuth purposes. .
|
||||
// @Tags DexRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Response{}
|
||||
// @Router /dex/login [get]
|
||||
//
|
||||
// DexLogin handles and redirects to DexServer to proceed with OAuth
|
||||
func DexLogin() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -55,6 +65,16 @@ func DexLogin() gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// DexCallback godoc
|
||||
//
|
||||
// @Description DexRouter creates all the required routes for OAuth purposes. .
|
||||
// @Tags DexRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Response{}
|
||||
// @Router /dex/callback [get]
|
||||
//
|
||||
// DexCallback is the handler that creates/logs in the user from Dex and provides JWT to frontend via a redirect
|
||||
func DexCallback(userService services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -118,7 +138,15 @@ func DexCallback(userService services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
jwtToken, err := userService.GetSignedJWT(signedInUser)
|
||||
|
||||
salt, err := userService.GetConfig("salt")
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
jwtToken, err := userService.GetSignedJWT(signedInUser, salt.Value)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDexLogin(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
|
||||
rest.DexLogin()(ctx)
|
||||
|
||||
assert.Equal(t, 500, w.Code)
|
||||
}
|
|
@ -3,7 +3,7 @@ package rest
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -20,54 +20,69 @@ func contains(s []string, str string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
type ReadinessAPIStatus struct {
|
||||
DataBase string `json:"database"`
|
||||
Collections string `json:"collections"`
|
||||
}
|
||||
|
||||
// Status will request users list and return, if successful,
|
||||
// an http code 200
|
||||
// Status godoc
|
||||
//
|
||||
// @Description Status will request users list and return, if successful, a http code 200.
|
||||
// @Tags MiscRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.APIStatus{}
|
||||
// @Router /status [get]
|
||||
//
|
||||
// Status will request users list and return, if successful, a http code 200
|
||||
func Status(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
_, err := service.GetUsers()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, entities.APIStatus{Status: "down"})
|
||||
c.JSON(http.StatusInternalServerError, response.APIStatus{Status: "down"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, entities.APIStatus{Status: "up"})
|
||||
c.JSON(http.StatusOK, response.APIStatus{Status: "up"})
|
||||
}
|
||||
}
|
||||
|
||||
// Readiness godoc
|
||||
//
|
||||
// @Description Return list of tags.
|
||||
// @Tags MiscRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ReadinessAPIStatus{}
|
||||
// @Router /readiness [get]
|
||||
//
|
||||
// Readiness will return the status of the database and collections
|
||||
func Readiness(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var (
|
||||
db_flag = "up"
|
||||
col_flag = "up"
|
||||
dbFlag = "up"
|
||||
colFlag = "up"
|
||||
)
|
||||
|
||||
dbs, err := service.ListDataBase()
|
||||
if !contains(dbs, "auth") {
|
||||
db_flag = "down"
|
||||
dbFlag = "down"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, ReadinessAPIStatus{"down", "unknown"})
|
||||
c.JSON(http.StatusInternalServerError, response.ReadinessAPIStatus{DataBase: "down", Collections: "unknown"})
|
||||
return
|
||||
}
|
||||
|
||||
cols, err := service.ListCollection()
|
||||
if !contains(cols, "project") || !contains(cols, "users") {
|
||||
col_flag = "down"
|
||||
colFlag = "down"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, ReadinessAPIStatus{db_flag, "down"})
|
||||
c.JSON(http.StatusInternalServerError, response.ReadinessAPIStatus{DataBase: dbFlag, Collections: "down"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ReadinessAPIStatus{db_flag, col_flag})
|
||||
c.JSON(http.StatusOK, response.ReadinessAPIStatus{DataBase: dbFlag, Collections: colFlag})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
|
||||
t.Run("Success with valid data", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
users := []entities.User{}
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
mockService.On("GetUsers").Return(&users, nil)
|
||||
rest.Status(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
t.Run("Failed with invalid request", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
users := []entities.User{}
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
mockService.On("GetUsers").Return(&users, errors.New("Failed"))
|
||||
rest.Status(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestReadiness(t *testing.T) {
|
||||
t.Run("Success with valid data", func(t *testing.T) {
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
|
||||
mockService.On("ListDataBase").Return([]string{"auth", "otherDB"}, nil)
|
||||
mockService.On("ListCollection").Return([]string{"project", "users", "otherCollection"}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
rest.Readiness(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
t.Run("Failed with invalid data", func(t *testing.T) {
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
|
||||
mockService.On("ListDataBase").Return([]string{"auth", "otherDB"}, errors.New("Failed"))
|
||||
mockService.On("ListCollection").Return([]string{"project", "users", "otherCollection"}, errors.New("Failed"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
rest.Readiness(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/types"
|
||||
projectUtils "github.com/litmuschaos/litmus/chaoscenter/authentication/api/utils"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
|
@ -17,13 +21,30 @@ import (
|
|||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
// GetUserWithProject godoc
|
||||
//
|
||||
// @Summary Get user with project.
|
||||
// @Description Return users who have a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Param username path string true "Username"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserWithProject{}
|
||||
// @Router /get_user_with_project/:username [get]
|
||||
//
|
||||
// GetUserWithProject returns user and project details based on username
|
||||
func GetUserWithProject(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
username := c.Param("username")
|
||||
|
||||
// Validating logged in user
|
||||
if c.MustGet("username").(string) != username {
|
||||
// Validating logged-in user
|
||||
// Must be either requesting info from the logged-in user
|
||||
// or any user if it has the admin role
|
||||
role := c.MustGet("role").(string)
|
||||
if c.MustGet("username").(string) != username && role != string(entities.RoleAdmin) {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
|
@ -37,33 +58,56 @@ func GetUserWithProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
outputUser := user.GetUserWithProject()
|
||||
request := projectUtils.GetProjectFilters(c)
|
||||
request.UserID = user.ID
|
||||
|
||||
projects, err := service.GetProjectsByUserID(outputUser.ID, false)
|
||||
res, err := service.GetProjectsByUserID(request)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
outputUser.Projects = projects
|
||||
outputUser := entities.UserWithProject{
|
||||
Username: user.Username,
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
Projects: res.Projects,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": outputUser})
|
||||
c.JSON(http.StatusOK, response.UserWithProject{Data: outputUser})
|
||||
}
|
||||
}
|
||||
|
||||
// GetProject godoc
|
||||
//
|
||||
// @Summary Get user with project.
|
||||
// @Description Return a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Param project_id path string true "Project ID"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Project{}
|
||||
// @Router /get_project/:project_id [get]
|
||||
//
|
||||
// GetProject queries the project with a given projectID from the database
|
||||
func GetProject(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
projectID := c.Param("project_id")
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
||||
err := validations.RbacValidator(c.MustGet("uid").(string), projectID,
|
||||
validations.MutationRbacRules["getProject"], string(entities.AcceptedInvitation), service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
if userRole != string(entities.RoleAdmin) {
|
||||
err := validations.RbacValidator(c.MustGet("uid").(string), projectID,
|
||||
validations.MutationRbacRules["getProject"], string(entities.AcceptedInvitation), service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
project, err := service.GetProjectByProjectID(projectID)
|
||||
|
@ -73,19 +117,32 @@ func GetProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": project})
|
||||
c.JSON(http.StatusOK, response.Project{Data: *project})
|
||||
}
|
||||
}
|
||||
|
||||
// GetProjectsByUserID godoc
|
||||
//
|
||||
// @Summary Get stats of a project.
|
||||
// @Description Return stats of a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ListProjectResponse{}
|
||||
// @Router /list_projects [get]
|
||||
//
|
||||
// GetProjectsByUserID queries the project with a given userID from the database and returns it in the appropriate format
|
||||
func GetProjectsByUserID(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uID := c.MustGet("uid").(string)
|
||||
projects, err := service.GetProjectsByUserID(uID, false)
|
||||
if projects == nil {
|
||||
request := projectUtils.GetProjectFilters(c)
|
||||
|
||||
res, err := service.GetProjectsByUserID(request)
|
||||
if res == nil || (res.TotalNumberOfProjects != nil && *res.TotalNumberOfProjects == 0) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "No projects found",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -93,11 +150,22 @@ func GetProjectsByUserID(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": projects})
|
||||
c.JSON(http.StatusOK, response.ListProjectResponse{Data: *res})
|
||||
}
|
||||
}
|
||||
|
||||
// GetProjectStats is used to retrive stats related to projects in the DB
|
||||
// GetProjectStats godoc
|
||||
//
|
||||
// @Summary Get stats of a project.
|
||||
// @Description Return stats of a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ProjectStats{}
|
||||
// @Router /get_projects_stats [get]
|
||||
//
|
||||
// GetProjectStats is used to retrieve stats related to projects in the DB
|
||||
func GetProjectStats(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
role := c.MustGet("role").(string)
|
||||
|
@ -117,20 +185,70 @@ func GetProjectStats(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": project})
|
||||
c.JSON(http.StatusOK, response.ProjectStats{Data: project})
|
||||
}
|
||||
}
|
||||
|
||||
// GetActiveProjectMembers godoc
|
||||
//
|
||||
// @Summary Get active project members.
|
||||
// @Description Return list of active project members.
|
||||
// @Tags ProjectRouter
|
||||
// @Param state path string true "State"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Members{}
|
||||
// @Router /get_project_members/:project_id/:state [get]
|
||||
//
|
||||
// GetActiveProjectMembers returns the list of active project members
|
||||
func GetActiveProjectMembers(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
projectID := c.Param("project_id")
|
||||
state := c.Param("state")
|
||||
role := c.MustGet("role").(string)
|
||||
if role != string(entities.RoleAdmin) {
|
||||
err := validations.RbacValidator(c.MustGet("uid").(string), projectID,
|
||||
validations.MutationRbacRules["getProject"], string(entities.AcceptedInvitation), service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
members, err := service.GetProjectMembers(projectID, state)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": members})
|
||||
c.JSON(http.StatusOK, response.Members{Data: members})
|
||||
}
|
||||
}
|
||||
|
||||
// GetActiveProjectOwners godoc
|
||||
//
|
||||
// @Summary Get active project Owners.
|
||||
// @Description Return list of active project owners.
|
||||
// @Tags ProjectRouter
|
||||
// @Param state path string true "State"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Members{}
|
||||
// @Router /get_project_owners/:project_id/:state [get]
|
||||
//
|
||||
// GetActiveProjectOwners returns the list of active project owners
|
||||
func GetActiveProjectOwners(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
projectID := c.Param("project_id")
|
||||
owners, err := service.GetProjectOwners(projectID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.Members{Data: owners})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,12 +267,24 @@ func getInvitation(service services.ApplicationService, member entities.MemberIn
|
|||
return "", nil
|
||||
}
|
||||
|
||||
// ListInvitations godoc
|
||||
//
|
||||
// @Summary List invitations.
|
||||
// @Description Return list of invitations.
|
||||
// @Tags ProjectRouter
|
||||
// @Param invitation_state path string true "Invitation State"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ListInvitationResponse{}
|
||||
// @Router /list_invitations_with_filters/:invitation_state [get]
|
||||
//
|
||||
// ListInvitations returns the Invitation status
|
||||
func ListInvitations(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uID := c.MustGet("uid").(string)
|
||||
invitationState := c.Param("invitation_state")
|
||||
var response []entities.ListInvitationResponse
|
||||
var res []entities.ListInvitationResponse
|
||||
projects, err := service.ListInvitations(uID, entities.Invitation(invitationState))
|
||||
if err != nil {
|
||||
log.Errorf("Error while fetching invitations: %v", err)
|
||||
|
@ -173,12 +303,24 @@ func ListInvitations(service services.ApplicationService) gin.HandlerFunc {
|
|||
inviteRes.InvitationRole = member.Role
|
||||
}
|
||||
}
|
||||
response = append(response, inviteRes)
|
||||
res = append(res, inviteRes)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": response})
|
||||
c.JSON(http.StatusOK, response.ListInvitationResponse{Data: res})
|
||||
}
|
||||
}
|
||||
|
||||
// CreateProject godoc
|
||||
//
|
||||
// @Summary Create project.
|
||||
// @Description Create a new project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Project{}
|
||||
// @Router /create_project [post]
|
||||
//
|
||||
// CreateProject is used to create a new project
|
||||
func CreateProject(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userRequest entities.CreateProjectInput
|
||||
|
@ -188,13 +330,37 @@ func CreateProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
userRequest.UserID = c.MustGet("uid").(string)
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, userRequest.UserID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
// checking if project name is empty
|
||||
if userRequest.ProjectName == "" {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrEmptyProjectName], presenter.CreateErrorResponse(utils.ErrEmptyProjectName))
|
||||
return
|
||||
}
|
||||
|
||||
userRequest.UserID = c.MustGet("uid").(string)
|
||||
if userRequest.Description == nil {
|
||||
// If description is not provided, set it to an empty string
|
||||
emptyDescription := ""
|
||||
userRequest.Description = &emptyDescription
|
||||
}
|
||||
|
||||
if userRequest.Tags == nil {
|
||||
// If tags are not provided, set it to an empty slice
|
||||
emptyTags := make([]*string, 0)
|
||||
userRequest.Tags = emptyTags
|
||||
}
|
||||
|
||||
user, err := service.GetUser(userRequest.UserID)
|
||||
if err != nil {
|
||||
|
@ -219,18 +385,22 @@ func CreateProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
// Adding user as project owner in project's member list
|
||||
newMember := &entities.Member{
|
||||
UserID: user.ID,
|
||||
Username: user.Name,
|
||||
Email: user.Email,
|
||||
Role: entities.RoleOwner,
|
||||
Invitation: entities.AcceptedInvitation,
|
||||
JoinedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
var members []*entities.Member
|
||||
members = append(members, newMember)
|
||||
state := "active"
|
||||
state := string(types.MemberStateActive)
|
||||
newProject := &entities.Project{
|
||||
ID: pID,
|
||||
Name: userRequest.ProjectName,
|
||||
Members: members,
|
||||
State: &state,
|
||||
ID: pID,
|
||||
Name: userRequest.ProjectName,
|
||||
Members: members,
|
||||
State: &state,
|
||||
Description: userRequest.Description,
|
||||
Tags: userRequest.Tags,
|
||||
Audit: entities.Audit{
|
||||
IsRemoved: false,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
|
@ -255,14 +425,28 @@ func CreateProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": newProject.GetProjectOutput()})
|
||||
c.JSON(http.StatusOK, response.Project{Data: *newProject.GetProjectOutput()})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// SendInvitation sends an invitation to a new user and
|
||||
// returns an error if the member is already part of the project
|
||||
// SendInvitation godoc
|
||||
//
|
||||
// @Summary Send invitation.
|
||||
// @Description Send invitation to a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidRole
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Member{}
|
||||
// @Router /send_invitation [post]
|
||||
//
|
||||
// SendInvitation sends an invitation to a new user and returns an error if the member is already part of the project
|
||||
func SendInvitation(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var member entities.MemberInput
|
||||
|
@ -281,15 +465,28 @@ func SendInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, c.MustGet("uid").(string))
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
// Validating member role
|
||||
if member.Role == nil || (*member.Role != entities.RoleEditor && *member.Role != entities.RoleViewer) {
|
||||
if member.Role == nil || (*member.Role != entities.RoleExecutor && *member.Role != entities.RoleViewer && *member.Role != entities.RoleOwner) {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRole], presenter.CreateErrorResponse(utils.ErrInvalidRole))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := service.GetUser(member.UserID)
|
||||
|
||||
if err == mongo.ErrNoDocuments {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserNotFound], presenter.CreateErrorResponse(utils.ErrUserNotFound))
|
||||
return
|
||||
} else if err != nil {
|
||||
|
@ -299,7 +496,7 @@ func SendInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
|
||||
invitation, err := getInvitation(service, member)
|
||||
if err == mongo.ErrNoDocuments {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrProjectNotFound], presenter.CreateErrorResponse(utils.ErrProjectNotFound))
|
||||
return
|
||||
} else if err != nil {
|
||||
|
@ -339,7 +536,7 @@ func SendInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": entities.Member{
|
||||
c.JSON(http.StatusOK, response.Member{Data: entities.Member{
|
||||
UserID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
|
@ -351,6 +548,19 @@ func SendInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// AcceptInvitation godoc
|
||||
//
|
||||
// @Summary Accept invitation.
|
||||
// @Description Accept invitation to a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /accept_invitation [post]
|
||||
//
|
||||
// AcceptInvitation is used to accept an invitation
|
||||
func AcceptInvitation(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -362,6 +572,18 @@ func AcceptInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, c.MustGet("uid").(string))
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = validations.RbacValidator(c.MustGet("uid").(string), member.ProjectID,
|
||||
validations.MutationRbacRules["acceptInvitation"],
|
||||
string(entities.PendingInvitation),
|
||||
|
@ -380,12 +602,23 @@ func AcceptInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Successful",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "Successful"})
|
||||
}
|
||||
}
|
||||
|
||||
// DeclineInvitation godoc
|
||||
//
|
||||
// @Summary Decline invitation.
|
||||
// @Description Decline invitation to a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /decline_invitation [post]
|
||||
//
|
||||
// DeclineInvitation is used to decline an invitation
|
||||
func DeclineInvitation(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -397,6 +630,18 @@ func DeclineInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, c.MustGet("uid").(string))
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = validations.RbacValidator(c.MustGet("uid").(string), member.ProjectID,
|
||||
validations.MutationRbacRules["declineInvitation"],
|
||||
string(entities.PendingInvitation),
|
||||
|
@ -415,12 +660,23 @@ func DeclineInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Successful",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successful"})
|
||||
}
|
||||
}
|
||||
|
||||
// LeaveProject godoc
|
||||
//
|
||||
// @Summary Leave project.
|
||||
// @Description Leave project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /leave_project [post]
|
||||
//
|
||||
// LeaveProject is used to leave a project
|
||||
func LeaveProject(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -432,6 +688,32 @@ func LeaveProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
if member.Role != nil && *member.Role == entities.RoleOwner {
|
||||
owners, err := service.GetProjectOwners(member.ProjectID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if len(owners) == 1 {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], gin.H{"message": "Cannot leave project. There must be at least one owner."})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, c.MustGet("uid").(string))
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = validations.RbacValidator(c.MustGet("uid").(string), member.ProjectID,
|
||||
validations.MutationRbacRules["leaveProject"],
|
||||
string(entities.AcceptedInvitation),
|
||||
|
@ -450,12 +732,23 @@ func LeaveProject(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Successful",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successful"})
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveInvitation godoc
|
||||
//
|
||||
// @Summary Remove invitation.
|
||||
// @Description Remove invitation of a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /remove_invitation [post]
|
||||
//
|
||||
// RemoveInvitation removes member or cancels invitation
|
||||
func RemoveInvitation(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -471,6 +764,18 @@ func RemoveInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, c.MustGet("uid").(string))
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = validations.RbacValidator(c.MustGet("uid").(string), member.ProjectID,
|
||||
validations.MutationRbacRules["removeInvitation"],
|
||||
string(entities.AcceptedInvitation),
|
||||
|
@ -482,6 +787,12 @@ func RemoveInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
uid := c.MustGet("uid").(string)
|
||||
if uid == member.UserID {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "User cannot remove invitation of themselves use Leave Project."})
|
||||
return
|
||||
}
|
||||
|
||||
invitation, err := getInvitation(service, member)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -507,12 +818,23 @@ func RemoveInvitation(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Successful",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successful"})
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateProjectName godoc
|
||||
//
|
||||
// @Summary Update project name.
|
||||
// @Description Return updated project name.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /update_project_name [post]
|
||||
//
|
||||
// UpdateProjectName is used to update a project's name
|
||||
func UpdateProjectName(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -524,6 +846,18 @@ func UpdateProjectName(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, c.MustGet("uid").(string))
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = validations.RbacValidator(c.MustGet("uid").(string),
|
||||
userRequest.ProjectID,
|
||||
validations.MutationRbacRules["updateProjectName"],
|
||||
|
@ -557,12 +891,82 @@ func UpdateProjectName(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Successful",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successful"})
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateMemberRole godoc
|
||||
//
|
||||
// @Summary Update member role.
|
||||
// @Description Return updated member role.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /update_member_role [post]
|
||||
//
|
||||
// UpdateMemberRole is used to update a member role in the project
|
||||
func UpdateMemberRole(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var member entities.MemberInput
|
||||
err := c.BindJSON(&member)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
// Validating member role
|
||||
if member.Role == nil || (*member.Role != entities.RoleExecutor && *member.Role != entities.RoleViewer && *member.Role != entities.RoleOwner) {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRole], presenter.CreateErrorResponse(utils.ErrInvalidRole))
|
||||
return
|
||||
}
|
||||
|
||||
err = validations.RbacValidator(c.MustGet("uid").(string),
|
||||
member.ProjectID,
|
||||
validations.MutationRbacRules["updateMemberRole"],
|
||||
string(entities.AcceptedInvitation),
|
||||
service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
uid := c.MustGet("uid").(string)
|
||||
if uid == member.UserID {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"message": "User cannot change their own role."})
|
||||
return
|
||||
}
|
||||
|
||||
err = service.UpdateMemberRole(member.ProjectID, member.UserID, member.Role)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successful"})
|
||||
}
|
||||
}
|
||||
|
||||
// GetOwnerProjects godoc
|
||||
//
|
||||
// @Summary Get projects owner.
|
||||
// @Description Return owner of projects.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Projects{}
|
||||
// @Router /get_owner_projects [get]
|
||||
//
|
||||
// GetOwnerProjects returns an array of projects in which user is an owner
|
||||
func GetOwnerProjects(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -574,13 +978,24 @@ func GetOwnerProjects(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": res,
|
||||
})
|
||||
c.JSON(http.StatusOK, response.Projects{Data: res})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// GetProjectRole godoc
|
||||
//
|
||||
// @Summary Get project Role.
|
||||
// @Description Return role of a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Param project_id path int true "Project ID"
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrProjectNotFound
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ProjectRole{}
|
||||
// @Router /get_project_role/:project_id [get]
|
||||
//
|
||||
// GetProjectRole returns the role of a user in the project
|
||||
func GetProjectRole(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -590,7 +1005,7 @@ func GetProjectRole(service services.ApplicationService) gin.HandlerFunc {
|
|||
res, err := service.GetProjectRole(projectID, uid)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
if err == mongo.ErrNoDocuments {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrProjectNotFound], presenter.CreateErrorResponse(utils.ErrProjectNotFound))
|
||||
return
|
||||
}
|
||||
|
@ -601,9 +1016,46 @@ func GetProjectRole(service services.ApplicationService) gin.HandlerFunc {
|
|||
if res != nil {
|
||||
role = string(*res)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"role": role,
|
||||
})
|
||||
c.JSON(http.StatusOK, response.ProjectRole{Role: role})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteProject godoc
|
||||
//
|
||||
// @Description Delete a project.
|
||||
// @Tags ProjectRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrProjectNotFound
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /delete_project/:project_id [post]
|
||||
//
|
||||
// DeleteProject is used to delete a project.
|
||||
func DeleteProject(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
projectID := c.Param("project_id")
|
||||
|
||||
err := validations.RbacValidator(c.MustGet("uid").(string),
|
||||
projectID,
|
||||
validations.MutationRbacRules["deleteProject"],
|
||||
string(entities.AcceptedInvitation),
|
||||
service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
err = service.DeleteProject(projectID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "Successfully deleted project."})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func TestGetUserWithProject(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("Failed to retrieve user with projects", func(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
username := "testUser"
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Params = gin.Params{
|
||||
{"username", username},
|
||||
}
|
||||
c.Set("username", username)
|
||||
c.Set("role", string(entities.RoleUser))
|
||||
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUID",
|
||||
}
|
||||
|
||||
service.On("FindUserByUsername", "testUser").Return(user, errors.New("failed"))
|
||||
service.On("GetProjectsByUserID", request).Return(response, errors.New("failed"))
|
||||
|
||||
rest.GetUserWithProject(service)(c)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
})
|
||||
|
||||
t.Run("Successfully retrieve user with projects", func(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
username := "testUser1"
|
||||
f := httptest.NewRecorder()
|
||||
c := GetTestGinContext(f)
|
||||
c.Params = gin.Params{
|
||||
{"username", username},
|
||||
}
|
||||
c.Set("username", username)
|
||||
c.Set("role", string(entities.RoleUser))
|
||||
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser1",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service.On("FindUserByUsername", "testUser1").Return(user, nil)
|
||||
service.On("GetProjectsByUserID", request).Return(response, nil)
|
||||
|
||||
rest.GetUserWithProject(service)(c)
|
||||
|
||||
assert.Equal(t, http.StatusOK, f.Code)
|
||||
})
|
||||
|
||||
t.Run("Successfully retrieve user with projects if logged user has admin role", func(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
username := "testUser"
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Params = gin.Params{
|
||||
{"username", username},
|
||||
}
|
||||
c.Set("username", "adminusername")
|
||||
c.Set("role", string(entities.RoleAdmin))
|
||||
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
Role: entities.RoleAdmin,
|
||||
}
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service.On("FindUserByUsername", "testUser").Return(user, nil)
|
||||
service.On("GetProjectsByUserID", request).Return(response, nil)
|
||||
|
||||
rest.GetUserWithProject(service)(c)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProjectsByUserID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("Failed with invalid data", func(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", "testUserID")
|
||||
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUserID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service := new(mocks.MockedApplicationService)
|
||||
service.On("GetProjectsByUserID", request).Return(response, errors.New("Failed"))
|
||||
rest.GetProjectsByUserID(service)(ctx)
|
||||
assert.Equal(t, utils.ErrorStatusCodes[utils.ErrServerError], w.Code)
|
||||
})
|
||||
|
||||
t.Run("Successful retrieve of project", func(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", "testUserID")
|
||||
projects := []*entities.Project{
|
||||
{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
},
|
||||
}
|
||||
|
||||
response := &entities.ListProjectResponse{
|
||||
Projects: projects,
|
||||
}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUserID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service := new(mocks.MockedApplicationService)
|
||||
service.On("GetProjectsByUserID", request).Return(response, nil)
|
||||
rest.GetProjectsByUserID(service)(ctx)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGetProject(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
t.Run("unauthorized request to Project", func(t *testing.T) {
|
||||
projectID := "testProjectID"
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", projectID)
|
||||
ctx.Set("role", string(entities.RoleUser))
|
||||
service := new(mocks.MockedApplicationService)
|
||||
project := &entities.Project{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
|
||||
service.On("GetProjectByProjectID", projectID).Return(project, errors.New("Failed"))
|
||||
service.On("GetUser", projectID).Return(user, errors.New("Failed"))
|
||||
rest.GetProject(service)(ctx)
|
||||
|
||||
assert.Equal(t, utils.ErrorStatusCodes[utils.ErrUnauthorized], w.Code)
|
||||
})
|
||||
|
||||
t.Run("Successful to find Project", func(t *testing.T) {
|
||||
projectID := "testUserID"
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", projectID)
|
||||
ctx.Set("role", string(entities.RoleAdmin))
|
||||
service := new(mocks.MockedApplicationService)
|
||||
project := &entities.Project{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testUserID",
|
||||
Name: "Test User",
|
||||
}
|
||||
projects := []*entities.Project{
|
||||
{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
},
|
||||
}
|
||||
expectedFilter := primitive.D{
|
||||
primitive.E{
|
||||
Key: "_id",
|
||||
Value: "",
|
||||
},
|
||||
primitive.E{
|
||||
Key: "members",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$elemMatch",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "user_id",
|
||||
Value: "testUserID",
|
||||
},
|
||||
primitive.E{
|
||||
Key: "role",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$in",
|
||||
Value: []string{"Owner", "Viewer", "Executor"},
|
||||
},
|
||||
},
|
||||
},
|
||||
primitive.E{
|
||||
Key: "invitation",
|
||||
Value: "Accepted",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
service.On("GetProjectByProjectID", "").Return(project, nil)
|
||||
service.On("GetUser", projectID).Return(user, nil)
|
||||
service.On("GetProjects", expectedFilter).Return(projects, nil)
|
||||
rest.GetProject(service)(ctx)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
})
|
||||
|
||||
}
|
|
@ -1,13 +1,17 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/validations"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
@ -17,6 +21,23 @@ import (
|
|||
|
||||
const BearerSchema = "Bearer "
|
||||
|
||||
// CreateUser godoc
|
||||
//
|
||||
// @Description Create new user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidEmail
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 401 {object} response.ErrStrictUsernamePolicyViolation
|
||||
// @Failure 401 {object} response.ErrUserExists
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /create_user [post]
|
||||
//
|
||||
// CreateUser creates a new user
|
||||
func CreateUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
@ -44,10 +65,17 @@ func CreateUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
//username validation
|
||||
err = utils.ValidateStrictUsername(userRequest.Username)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrStrictUsernamePolicyViolation], presenter.CreateErrorResponse(utils.ErrStrictUsernamePolicyViolation))
|
||||
return
|
||||
}
|
||||
|
||||
// Assigning UID to user
|
||||
uID := uuid.Must(uuid.NewRandom()).String()
|
||||
userRequest.ID = uID
|
||||
userRequest.IsInitialLogin = true
|
||||
|
||||
// Generating password hash
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(userRequest.Password), utils.PasswordEncryptionCost)
|
||||
|
@ -70,7 +98,7 @@ func CreateUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
userRequest.CreatedAt = createdAt
|
||||
|
||||
userResponse, err := service.CreateUser(&userRequest)
|
||||
if err == utils.ErrUserExists {
|
||||
if errors.Is(err, utils.ErrUserExists) {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserExists], presenter.CreateErrorResponse(utils.ErrUserExists))
|
||||
return
|
||||
|
@ -84,6 +112,21 @@ func CreateUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(http.StatusOK, userResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUser godoc
|
||||
//
|
||||
// @Description Update users details.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 401 {object} response.ErrStrictUsernamePolicyViolation
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /update/details [post]
|
||||
//
|
||||
// UpdateUser updates the user details
|
||||
func UpdateUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userRequest entities.UserDetails
|
||||
|
@ -96,28 +139,52 @@ func UpdateUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
|
||||
uid := c.MustGet("uid").(string)
|
||||
userRequest.ID = uid
|
||||
initialLogin, err := CheckInitialLogin(service, uid)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if password is updated
|
||||
if userRequest.Password != "" {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(userRequest.Password), utils.PasswordEncryptionCost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
userRequest.Password = string(hashedPassword)
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = service.UpdateUser(&userRequest)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "User details updated successfully"})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "User details updated successfully"})
|
||||
}
|
||||
}
|
||||
|
||||
// GetUser godoc
|
||||
//
|
||||
// @Description Get user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /get_user/:uid [get]
|
||||
//
|
||||
// GetUser returns the user that matches the uid passed in parameter
|
||||
func GetUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := c.Param("uid")
|
||||
|
||||
// Validating logged-in user
|
||||
// Must be either requesting info from the logged-in user
|
||||
// or any user if it has the admin role
|
||||
role := c.MustGet("role").(string)
|
||||
if c.MustGet("uid").(string) != uid && role != string(entities.RoleAdmin) {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := service.GetUser(uid)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -128,6 +195,18 @@ func GetUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// FetchUsers godoc
|
||||
//
|
||||
// @Description Fetch users.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /users [get]
|
||||
//
|
||||
// FetchUsers fetches all the users
|
||||
func FetchUsers(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
@ -146,6 +225,18 @@ func FetchUsers(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// InviteUsers godoc
|
||||
//
|
||||
// @Description Invite users.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /invite_users/:project_id [get]
|
||||
//
|
||||
// InviteUsers invites users to the project
|
||||
func InviteUsers(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
projectID := c.Param("project_id")
|
||||
|
@ -153,13 +244,22 @@ func InviteUsers(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
err := validations.RbacValidator(c.MustGet("uid").(string), projectID,
|
||||
validations.MutationRbacRules["sendInvitation"], string(entities.AcceptedInvitation), service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
projectMembers, err := service.GetProjectMembers(projectID, "all")
|
||||
|
||||
var uids []string
|
||||
var userIds []string
|
||||
for _, k := range projectMembers {
|
||||
uids = append(uids, k.UserID)
|
||||
userIds = append(userIds, k.UserID)
|
||||
}
|
||||
users, err := service.InviteUsers(uids)
|
||||
users, err := service.InviteUsers(userIds)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
|
@ -169,6 +269,21 @@ func InviteUsers(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// LoginUser godoc
|
||||
//
|
||||
// @Description User Login.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Failure 400 {object} response.ErrUserDeactivated
|
||||
// @Failure 401 {object} response.ErrInvalidCredentials
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.LoginResponse{}
|
||||
// @Router /login [post]
|
||||
//
|
||||
// LoginUser returns the token for the user if the credentials are valid
|
||||
func LoginUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userRequest entities.User
|
||||
|
@ -188,13 +303,13 @@ func LoginUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
user, err := service.FindUserByUsername(userRequest.Username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserNotFound], presenter.CreateErrorResponse(utils.ErrUserNotFound))
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if user is deactivated
|
||||
if user.DeactivatedAt != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserDeactivated], presenter.CreateErrorResponse(utils.ErrUserDeactivated))
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -206,7 +321,13 @@ func LoginUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
token, err := service.GetSignedJWT(user)
|
||||
salt, err := service.GetConfig("salt")
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
token, err := service.GetSignedJWT(user, salt.Value)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
|
@ -219,7 +340,7 @@ func LoginUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
|
||||
if len(ownerProjects) > 0 {
|
||||
defaultProject = ownerProjects[0].ID
|
||||
} else {
|
||||
} else if !user.IsInitialLogin {
|
||||
// Adding user as project owner in project's member list
|
||||
newMember := &entities.Member{
|
||||
UserID: user.ID,
|
||||
|
@ -271,6 +392,17 @@ func LoginUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// LogoutUser godoc
|
||||
//
|
||||
// @Description Revokes the token passed in the Authorization header.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /logout [post]
|
||||
//
|
||||
// LogoutUser revokes the token passed in the Authorization header
|
||||
func LogoutUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -287,12 +419,24 @@ func LogoutUser(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "successfully logged out",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successfully logged out"})
|
||||
}
|
||||
}
|
||||
|
||||
// UpdatePassword godoc
|
||||
//
|
||||
// @Description Update user password.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 400 {object} response.ErrOldPassword
|
||||
// @Failure 401 {object} response.ErrInvalidCredentials
|
||||
// @Success 200 {object} response.ProjectIDWithMessage{}
|
||||
// @Router /update/password [post]
|
||||
//
|
||||
// UpdatePassword updates the user password
|
||||
func UpdatePassword(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userPasswordRequest entities.UserPassword
|
||||
|
@ -303,30 +447,105 @@ func UpdatePassword(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
username := c.MustGet("username").(string)
|
||||
|
||||
// Fetching userDetails
|
||||
user, err := service.FindUserByUsername(username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserNotFound], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
userPasswordRequest.Username = username
|
||||
if utils.StrictPasswordPolicy {
|
||||
if userPasswordRequest.NewPassword != "" {
|
||||
err := utils.ValidateStrictPassword(userPasswordRequest.NewPassword)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation], presenter.CreateErrorResponse(utils.ErrStrictPasswordPolicyViolation))
|
||||
return
|
||||
}
|
||||
}
|
||||
if userPasswordRequest.NewPassword == "" {
|
||||
} else {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
err = service.UpdatePassword(&userPasswordRequest, true)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
if strings.Contains(err.Error(), "old and new passwords can't be same") {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrOldPassword], presenter.CreateErrorResponse(utils.ErrOldPassword))
|
||||
} else if strings.Contains(err.Error(), "invalid credentials") {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
} else {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
}
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "password has been updated successfully",
|
||||
})
|
||||
|
||||
var defaultProject string
|
||||
ownerProjects, err := service.GetOwnerProjectIDs(c, user.ID)
|
||||
|
||||
if len(ownerProjects) > 0 {
|
||||
defaultProject = ownerProjects[0].ID
|
||||
} else {
|
||||
// Adding user as project owner in project's member list
|
||||
newMember := &entities.Member{
|
||||
UserID: user.ID,
|
||||
Role: entities.RoleOwner,
|
||||
Invitation: entities.AcceptedInvitation,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
JoinedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
var members []*entities.Member
|
||||
members = append(members, newMember)
|
||||
state := "active"
|
||||
newProject := &entities.Project{
|
||||
ID: uuid.Must(uuid.NewRandom()).String(),
|
||||
Name: user.Username + "-project",
|
||||
Members: members,
|
||||
State: &state,
|
||||
Audit: entities.Audit{
|
||||
IsRemoved: false,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
CreatedBy: entities.UserDetailResponse{
|
||||
Username: user.Username,
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
UpdatedBy: entities.UserDetailResponse{
|
||||
Username: user.Username,
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := service.CreateProject(newProject)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defaultProject = newProject.ID
|
||||
}
|
||||
c.JSON(http.StatusOK, response.ProjectIDWithMessage{Message: "password has been updated successfully", ProjectID: defaultProject})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ResetPassword godoc
|
||||
//
|
||||
// @Description Reset user password.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /reset/password [post]
|
||||
//
|
||||
// ResetPassword resets the user password
|
||||
func ResetPassword(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
@ -347,7 +566,20 @@ func ResetPassword(service services.ApplicationService) gin.HandlerFunc {
|
|||
var adminUser entities.User
|
||||
adminUser.Username = c.MustGet("username").(string)
|
||||
adminUser.ID = uid
|
||||
if utils.StrictPasswordPolicy {
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, uid)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
if userPasswordRequest.NewPassword != "" {
|
||||
err := utils.ValidateStrictPassword(userPasswordRequest.NewPassword)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation], presenter.CreateErrorResponse(utils.ErrStrictPasswordPolicyViolation))
|
||||
|
@ -371,16 +603,41 @@ func ResetPassword(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "password has been reset successfully",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "password has been reset successfully"})
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserState godoc
|
||||
//
|
||||
// @Description Updates the user state.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /update/state [post]
|
||||
//
|
||||
// UpdateUserState updates the user state
|
||||
func UpdateUserState(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
userRole := c.MustGet("role").(string)
|
||||
var adminUser entities.User
|
||||
adminUser.Username = c.MustGet("username").(string)
|
||||
adminUser.ID = c.MustGet("uid").(string)
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, adminUser.ID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
if entities.Role(userRole) != entities.RoleAdmin {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
|
@ -388,7 +645,7 @@ func UpdateUserState(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
|
||||
var userRequest entities.UpdateUserState
|
||||
err := c.BindJSON(&userRequest)
|
||||
err = c.BindJSON(&userRequest)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
|
@ -399,10 +656,6 @@ func UpdateUserState(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
var adminUser entities.User
|
||||
adminUser.Username = c.MustGet("username").(string)
|
||||
adminUser.ID = c.MustGet("uid").(string)
|
||||
|
||||
// Checking if loggedIn user is admin
|
||||
err = service.IsAdministrator(&adminUser)
|
||||
if err != nil {
|
||||
|
@ -419,12 +672,22 @@ func UpdateUserState(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "user's state updated successfully",
|
||||
})
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "user's state updated successfully"})
|
||||
}
|
||||
}
|
||||
|
||||
// CreateApiToken godoc
|
||||
//
|
||||
// @Description Creates a new api token for the user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.NewApiToken{}
|
||||
// @Router /create_token [post]
|
||||
//
|
||||
// CreateApiToken creates a new api token for the user
|
||||
func CreateApiToken(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -436,6 +699,27 @@ func CreateApiToken(service services.ApplicationService) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// Validating logged-in user
|
||||
// Requesting info must be from the logged-in user
|
||||
if c.MustGet("uid").(string) != apiTokenRequest.UserID {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, apiTokenRequest.UserID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if user exists
|
||||
user, err := service.GetUser(apiTokenRequest.UserID)
|
||||
if err != nil {
|
||||
|
@ -457,10 +741,30 @@ func CreateApiToken(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// GetApiTokens godoc
|
||||
//
|
||||
// @Description Returns all the api tokens for the user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ApiTokenResponse{}
|
||||
// @Router /token/:uid [post]
|
||||
//
|
||||
// GetApiTokens returns all the api tokens for the user
|
||||
func GetApiTokens(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := c.Param("uid")
|
||||
|
||||
// Validating logged-in user
|
||||
// Requesting info must be from the logged-in user
|
||||
if c.MustGet("uid").(string) != uid {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
apiTokens, err := service.GetApiTokensByUserID(uid)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -473,6 +777,17 @@ func GetApiTokens(service services.ApplicationService) gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// DeleteApiToken godoc
|
||||
//
|
||||
// @Description Delete api token for the user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /remove_token [post]
|
||||
//
|
||||
// DeleteApiToken deletes the api token for the user
|
||||
func DeleteApiToken(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
@ -483,6 +798,28 @@ func DeleteApiToken(service services.ApplicationService) gin.HandlerFunc {
|
|||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
// Validating logged-in user
|
||||
// Requesting info must be from the logged-in user
|
||||
if c.MustGet("uid").(string) != deleteApiTokenRequest.UserID {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, deleteApiTokenRequest.UserID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
token := deleteApiTokenRequest.Token
|
||||
err = service.DeleteApiToken(token)
|
||||
if err != nil {
|
||||
|
@ -494,10 +831,8 @@ func DeleteApiToken(service services.ApplicationService) gin.HandlerFunc {
|
|||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "api token deleted successfully",
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "api token deleted successfully"})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,840 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// TestMain is the entry point for testing
|
||||
func TestMain(m *testing.M) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
log.SetOutput(io.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func GetTestGinContext(w *httptest.ResponseRecorder) *gin.Context {
|
||||
ctx, _ := gin.CreateTestContext(w)
|
||||
ctx.Request = &http.Request{
|
||||
Header: make(http.Header),
|
||||
URL: &url.URL{},
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.User
|
||||
mockRole string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "successfully",
|
||||
inputBody: &entities.User{
|
||||
Username: "newUser",
|
||||
Password: "ValidPassword@1",
|
||||
Email: "newuser@example.com",
|
||||
Name: "John Doe",
|
||||
Role: entities.RoleUser,
|
||||
},
|
||||
mockRole: "admin",
|
||||
given: func() {
|
||||
service.On("CreateUser", mock.AnythingOfType("*entities.User")).Return(&entities.User{
|
||||
ID: "newUserId",
|
||||
Username: "newUser",
|
||||
Email: "newuser@example.com",
|
||||
Name: "John Doe",
|
||||
Role: entities.RoleUser,
|
||||
}, nil).Once()
|
||||
},
|
||||
expectedCode: 200,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("role", tc.mockRole)
|
||||
if tc.inputBody != nil {
|
||||
b, err := json.Marshal(tc.inputBody)
|
||||
if err != nil {
|
||||
t.Fatalf("could not marshal input body: %v", err)
|
||||
}
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/users", bytes.NewBuffer(b))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
tc.given()
|
||||
|
||||
rest.CreateUser(service)(c)
|
||||
assert.Equal(t, tc.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
inputBody *entities.UserDetails
|
||||
given func()
|
||||
expectedCode int
|
||||
expectedMsg string
|
||||
}{
|
||||
{
|
||||
name: "Successful update details",
|
||||
uid: "testUID",
|
||||
inputBody: &entities.UserDetails{Email: "test@email.com", Name: "Test"},
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("UpdateUser", mock.AnythingOfType("*entities.UserDetails")).Return(nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedMsg: "User details updated successfully",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("uid", tt.uid)
|
||||
|
||||
if tt.inputBody != nil {
|
||||
b, _ := json.Marshal(tt.inputBody)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/path", bytes.NewBuffer(b))
|
||||
}
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.UpdateUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
var response map[string]string
|
||||
json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.Equal(t, tt.expectedMsg, response["message"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
role string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully retrieve user",
|
||||
uid: "testUID",
|
||||
role: "user",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = gin.Params{
|
||||
{"uid", tt.uid},
|
||||
}
|
||||
c.Set("uid", tt.uid)
|
||||
c.Set("role", tt.role)
|
||||
tt.given()
|
||||
|
||||
rest.GetUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
if w.Code == http.StatusOK {
|
||||
var user entities.User
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &user)
|
||||
assert.Equal(t, tt.uid, user.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchUsers(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
role string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully retrieve users by admin",
|
||||
role: "admin",
|
||||
given: func() {
|
||||
users := &[]entities.User{
|
||||
{
|
||||
ID: "testUID1",
|
||||
Username: "testUser1",
|
||||
Email: "test1@example.com",
|
||||
},
|
||||
{
|
||||
ID: "testUID2",
|
||||
Username: "testUser2",
|
||||
Email: "test2@example.com",
|
||||
},
|
||||
}
|
||||
service.On("GetUsers").Return(users, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Non-admin tries to retrieve users",
|
||||
role: "user",
|
||||
given: func() {},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("role", tt.role)
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.FetchUsers(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInviteUsers(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
projectID string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully invite users",
|
||||
projectID: "testProjectID",
|
||||
given: func() {
|
||||
projectMembers := []*entities.Member{
|
||||
{UserID: "user1ID"},
|
||||
{UserID: "user2ID"},
|
||||
}
|
||||
|
||||
service.On("GetProjectMembers", "testProjectID", "all").Return(projectMembers, nil)
|
||||
uids := []string{"user1ID", "user2ID"}
|
||||
users := &[]entities.User{
|
||||
{ID: "user1ID", Username: "user1"},
|
||||
{ID: "user2ID", Username: "user2"},
|
||||
}
|
||||
service.On("InviteUsers", uids).Return(users, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("uid", tt.projectID)
|
||||
c.Params = gin.Params{
|
||||
{"project_id", tt.projectID},
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testUserID",
|
||||
Name: "Test User",
|
||||
}
|
||||
project := &entities.Project{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
projects := []*entities.Project{
|
||||
{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
},
|
||||
}
|
||||
expectedFilter := primitive.D{
|
||||
primitive.E{
|
||||
Key: "_id",
|
||||
Value: tt.projectID,
|
||||
},
|
||||
primitive.E{
|
||||
Key: "members",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$elemMatch",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "user_id",
|
||||
Value: tt.projectID,
|
||||
},
|
||||
primitive.E{
|
||||
Key: "role",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$in",
|
||||
Value: []string{"Owner"},
|
||||
},
|
||||
},
|
||||
},
|
||||
primitive.E{
|
||||
Key: "invitation",
|
||||
Value: "Accepted",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tt.given()
|
||||
|
||||
service.On("GetProjectByProjectID", "").Return(project, nil)
|
||||
service.On("GetUser", tt.projectID).Return(user, nil)
|
||||
service.On("GetProjects", expectedFilter).Return(projects, nil)
|
||||
rest.InviteUsers(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogoutUser(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenToken string
|
||||
given func()
|
||||
expectedCode int
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "Successfully logout",
|
||||
givenToken: "Bearer testToken",
|
||||
given: func() {
|
||||
service.On("RevokeToken", "testToken").Return(nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedOutput: `{"message":"successfully logged out"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
|
||||
c.Request.Header.Set("Authorization", tt.givenToken)
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.LogoutUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
assert.Equal(t, tt.expectedOutput, w.Body.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input entities.User
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully login user",
|
||||
input: entities.User{
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
},
|
||||
given: func() {
|
||||
userFromDB := &entities.User{
|
||||
ID: "testUserID",
|
||||
Username: "testUser",
|
||||
Password: "hashedPassword",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
service.On("GetConfig", "salt").Return(&authConfig.AuthConfig{}, nil)
|
||||
service.On("FindUserByUsername", "testUser").Return(userFromDB, nil)
|
||||
service.On("CheckPasswordHash", "hashedPassword", "testPassword").Return(nil)
|
||||
service.On("UpdateUserByQuery", mock.Anything, mock.Anything).Return(nil)
|
||||
service.On("GetSignedJWT", userFromDB, mock.Anything).Return("someJWTToken", nil)
|
||||
project := &entities.Project{
|
||||
ID: "someProjectID",
|
||||
}
|
||||
service.On("GetOwnerProjectIDs", mock.Anything, "testUserID").Return([]*entities.Project{project}, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Invalid JSON body",
|
||||
given: func() {},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "Missing Username or Password",
|
||||
given: func() {},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "User not found",
|
||||
given: func() {
|
||||
service.On("FindUserByUsername", "notFoundUser").Return(nil, errors.New("user not found"))
|
||||
},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUserNotFound],
|
||||
},
|
||||
{
|
||||
name: "User deactivated",
|
||||
given: func() {
|
||||
deactivatedUser := &entities.User{
|
||||
ID: "deactivatedUserID",
|
||||
Username: "deactivatedUser",
|
||||
Password: "hashedPassword",
|
||||
}
|
||||
service.On("FindUserByUsername", "deactivatedUser").Return(deactivatedUser, nil)
|
||||
},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUserDeactivated],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
body, _ := json.Marshal(tt.input)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.LoginUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePassword(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenBody string
|
||||
givenUsername string
|
||||
givenStrictPassword bool
|
||||
givenServiceResponse error
|
||||
expectedCode int
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "Successfully update password",
|
||||
givenBody: `{"oldPassword":"oldPass@123", "newPassword":"newPass@123"}`,
|
||||
givenUsername: "testUser",
|
||||
givenStrictPassword: false,
|
||||
givenServiceResponse: nil,
|
||||
expectedCode: http.StatusOK,
|
||||
expectedOutput: `{"message":"password has been updated successfully","projectID":"someProjectID"}`,
|
||||
},
|
||||
{
|
||||
name: "Invalid new password",
|
||||
givenBody: `{"oldPassword":"oldPass@123", "newPassword":"short"}`,
|
||||
givenUsername: "testUser",
|
||||
givenStrictPassword: false,
|
||||
givenServiceResponse: errors.New("invalid password"),
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation],
|
||||
expectedOutput: `{"error":"password_policy_violation","errorDescription":"Please ensure the password is atleast 8 characters long and atmost 16 characters long and has atleast 1 digit, 1 lowercase alphabet, 1 uppercase alphabet and 1 special character"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest(http.MethodPost, "/", strings.NewReader(tt.givenBody))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Set("username", tt.givenUsername)
|
||||
|
||||
userPassword := entities.UserPassword{
|
||||
Username: tt.givenUsername,
|
||||
OldPassword: "oldPass@123",
|
||||
NewPassword: "newPass@123",
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
userFromDB := &entities.User{
|
||||
ID: "testUserID",
|
||||
Username: "testUser",
|
||||
Password: "hashedPassword",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
service.On("FindUserByUsername", "testUser").Return(userFromDB, nil)
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("UpdatePassword", &userPassword, true).Return(tt.givenServiceResponse)
|
||||
project := &entities.Project{
|
||||
ID: "someProjectID",
|
||||
}
|
||||
service.On("GetOwnerProjectIDs", mock.Anything, "testUserID").Return([]*entities.Project{project}, nil)
|
||||
rest.UpdatePassword(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
assert.Equal(t, tt.expectedOutput, w.Body.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetPassword(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.UserPassword
|
||||
mockRole string
|
||||
mockUID string
|
||||
mockUsername string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Admin role",
|
||||
inputBody: &entities.UserPassword{
|
||||
Username: "testUser",
|
||||
OldPassword: "",
|
||||
NewPassword: "ValidPass@123",
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("IsAdministrator", mock.AnythingOfType("*entities.User")).Return(nil)
|
||||
service.On("UpdatePassword", mock.AnythingOfType("*entities.UserPassword"), false).Return(nil)
|
||||
},
|
||||
expectedCode: 200,
|
||||
},
|
||||
{
|
||||
name: "Non-admin role",
|
||||
inputBody: &entities.UserPassword{
|
||||
Username: "testUser",
|
||||
OldPassword: "",
|
||||
NewPassword: "validPass@123",
|
||||
},
|
||||
mockRole: "user",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "user",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
},
|
||||
{
|
||||
name: "Invalid Request Body",
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "Empty Username or Password",
|
||||
inputBody: &entities.UserPassword{},
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "Admin role wrong password",
|
||||
inputBody: &entities.UserPassword{
|
||||
Username: "testUser",
|
||||
OldPassword: "",
|
||||
NewPassword: "short",
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("IsAdministrator", mock.AnythingOfType("*entities.User")).Return(nil)
|
||||
service.On("UpdatePassword", mock.AnythingOfType("*entities.UserPassword"), false).Return(nil)
|
||||
},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.given != nil {
|
||||
tt.given()
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Request.Method = http.MethodPost
|
||||
bodyBytes, _ := json.Marshal(tt.inputBody)
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader([]byte(bodyBytes)))
|
||||
c.Set("role", tt.mockRole)
|
||||
c.Set("uid", tt.mockUID)
|
||||
c.Set("username", tt.mockUsername)
|
||||
|
||||
rest.ResetPassword(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserState(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
const (
|
||||
myTrue = 0 == 0
|
||||
myFalse = 0 != 0
|
||||
)
|
||||
deactivate := myFalse
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.UpdateUserState
|
||||
mockRole string
|
||||
mockUsername string
|
||||
mockUID string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "successfully",
|
||||
inputBody: &entities.UpdateUserState{
|
||||
Username: "adminUser",
|
||||
IsDeactivate: &deactivate,
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUsername: "adminUser",
|
||||
mockUID: "tetstUUIS",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "tetstUUIS",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "tetstUUIS").Return(user, nil)
|
||||
service.On("IsAdministrator", mock.AnythingOfType("*entities.User")).Return(nil)
|
||||
service.On("UpdateStateTransaction", mock.AnythingOfType("entities.UpdateUserState")).Return(nil)
|
||||
|
||||
},
|
||||
expectedCode: 200,
|
||||
},
|
||||
{
|
||||
name: "failed to desactivate",
|
||||
inputBody: &entities.UpdateUserState{
|
||||
Username: "adminUser",
|
||||
IsDeactivate: nil,
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUsername: "adminUser",
|
||||
mockUID: "tetstUUIS",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "failed to authorize",
|
||||
inputBody: &entities.UpdateUserState{
|
||||
IsDeactivate: &deactivate,
|
||||
},
|
||||
mockRole: "user",
|
||||
mockUsername: "adminUser",
|
||||
mockUID: "tetstUUIS",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.given != nil {
|
||||
tc.given()
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Request.Method = http.MethodPost
|
||||
bodyBytes, _ := json.Marshal(tc.inputBody)
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader([]byte(bodyBytes)))
|
||||
c.Set("role", tc.mockRole)
|
||||
c.Set("uid", tc.mockUID)
|
||||
c.Set("username", tc.mockUsername)
|
||||
|
||||
rest.UpdateUserState(service)(c)
|
||||
|
||||
assert.Equal(t, tc.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateApiToken(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.ApiTokenInput
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Valid Request",
|
||||
inputBody: &entities.ApiTokenInput{
|
||||
UserID: "testUserID",
|
||||
},
|
||||
given: func() {
|
||||
user := &entities.User{ID: "testUserID"}
|
||||
service.On("GetUser", "testUserID").Return(user, nil)
|
||||
service.On("CreateApiToken", user, mock.MatchedBy(func(input entities.ApiTokenInput) bool {
|
||||
return input.UserID == "testUserID"
|
||||
})).Return("sampleToken", nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
bodyBytes, _ := json.Marshal(tt.inputBody)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/api/token", bytes.NewReader(bodyBytes))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Set("uid", tt.inputBody.UserID)
|
||||
tt.given()
|
||||
|
||||
rest.CreateApiToken(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetApiTokens(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
given func()
|
||||
expectedCode int
|
||||
expectedToken []entities.ApiToken
|
||||
}{
|
||||
{
|
||||
name: "Valid Request",
|
||||
uid: "testUserID",
|
||||
given: func() {
|
||||
returnedTokens := []entities.ApiToken{
|
||||
{Token: "sampleToken1"},
|
||||
{Token: "sampleToken2"},
|
||||
}
|
||||
service.On("GetApiTokensByUserID", "testUserID").Return(returnedTokens, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedToken: []entities.ApiToken{
|
||||
{Token: "sampleToken1"},
|
||||
{Token: "sampleToken2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = []gin.Param{{Key: "uid", Value: tt.uid}}
|
||||
c.Set("uid", tt.uid)
|
||||
tt.given()
|
||||
|
||||
rest.GetApiTokens(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
if tt.expectedCode == http.StatusOK {
|
||||
var response map[string][]entities.ApiToken
|
||||
json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.Equal(t, tt.expectedToken, response["apiTokens"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteApiToken(t *testing.T) {
|
||||
// given
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
service := new(services.ApplicationService)
|
||||
// when
|
||||
rest.DeleteApiToken(*service)(ctx)
|
||||
// then
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
)
|
||||
|
||||
func CheckInitialLogin(applicationService services.ApplicationService, userID string) (bool, error) {
|
||||
user, err := applicationService.GetUser(userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if user.IsInitialLogin {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func AddSalt(service services.ApplicationService) error {
|
||||
// generate salt and add/update to user collection
|
||||
// pass the salt in the below func which will act as jwt secret
|
||||
getSalt, err := service.GetConfig("salt")
|
||||
if err != nil && err != mongo.ErrNoDocuments {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
if getSalt != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
salt, err := utils.RandomString(6)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
encodedSalt := base64.StdEncoding.EncodeToString([]byte(salt))
|
||||
|
||||
config := authConfig.AuthConfig{
|
||||
Key: "salt",
|
||||
Value: encodedSalt,
|
||||
}
|
||||
|
||||
err = service.CreateConfig(config)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -4,10 +4,16 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
grpcHandler "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/grpc"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
|
||||
grpcPresenter "github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/routes"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
|
@ -20,7 +26,6 @@ import (
|
|||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
|
@ -29,27 +34,27 @@ import (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
JwtSecret string `required:"true" split_words:"true"`
|
||||
AdminUsername string `required:"true" split_words:"true"`
|
||||
AdminPassword string `required:"true" split_words:"true"`
|
||||
DbServer string `required:"true" split_words:"true"`
|
||||
DbUser string `required:"true" split_words:"true"`
|
||||
DbPassword string `required:"true" split_words:"true"`
|
||||
AdminUsername string `required:"true" split_words:"true"`
|
||||
AdminPassword string `required:"true" split_words:"true"`
|
||||
DbServer string `required:"true" split_words:"true"`
|
||||
DbUser string `required:"true" split_words:"true"`
|
||||
DbPassword string `required:"true" split_words:"true"`
|
||||
AllowedOrigins []string `split_words:"true" default:"^(http://|https://|)litmuschaos.io(:[0-9]+|)?,^(http://|https://|)localhost(:[0-9]+|)"`
|
||||
}
|
||||
|
||||
var config Config
|
||||
|
||||
func init() {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
log.SetReportCaller(true)
|
||||
printVersion()
|
||||
|
||||
var c Config
|
||||
|
||||
err := envconfig.Process("", &c)
|
||||
err := envconfig.Process("", &config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// @title Chaoscenter API documentation
|
||||
func main() {
|
||||
// send logs to stderr, so we can use 'kubectl logs'
|
||||
_ = flag.Set("logtostderr", "true")
|
||||
|
@ -81,6 +86,12 @@ func main() {
|
|||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
// Creating AuthConfig Collection
|
||||
err = utils.CreateCollection(utils.AuthConfigCollection, db)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
// Creating RevokedToken Collection
|
||||
if err = utils.CreateCollection(utils.RevokedTokenCollection, db); err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
|
@ -107,13 +118,30 @@ func main() {
|
|||
apiTokenCollection := db.Collection(utils.ApiTokenCollection)
|
||||
apiTokenRepo := session.NewApiTokenRepo(apiTokenCollection)
|
||||
|
||||
authConfigCollection := db.Collection(utils.AuthConfigCollection)
|
||||
authConfigRepo := authConfig.NewAuthConfigRepo(authConfigCollection)
|
||||
|
||||
miscRepo := misc.NewRepo(db, client)
|
||||
|
||||
applicationService := services.NewService(userRepo, projectRepo, miscRepo, revokedTokenRepo, apiTokenRepo, db)
|
||||
applicationService := services.NewService(userRepo, projectRepo, miscRepo, revokedTokenRepo, apiTokenRepo, authConfigRepo, db)
|
||||
|
||||
err = response.AddSalt(applicationService)
|
||||
if err != nil {
|
||||
log.Fatal("couldn't create salt $s", err)
|
||||
}
|
||||
|
||||
validatedAdminSetup(applicationService)
|
||||
|
||||
go runGrpcServer(applicationService)
|
||||
if utils.EnableInternalTls {
|
||||
if utils.TlsCertPath != "" && utils.TlSKeyPath != "" {
|
||||
go runGrpcServerWithTLS(applicationService)
|
||||
} else {
|
||||
log.Fatalf("Failure to start chaoscenter authentication GRPC server due to empty TLS cert file path and TLS key path")
|
||||
}
|
||||
} else {
|
||||
go runGrpcServer(applicationService)
|
||||
}
|
||||
|
||||
runRestServer(applicationService)
|
||||
}
|
||||
|
||||
|
@ -129,10 +157,11 @@ func validatedAdminSetup(service services.ApplicationService) {
|
|||
password := string(hashedPassword)
|
||||
|
||||
adminUser := entities.User{
|
||||
ID: uID,
|
||||
Username: utils.AdminName,
|
||||
Password: password,
|
||||
Role: entities.RoleAdmin,
|
||||
ID: uID,
|
||||
Username: utils.AdminName,
|
||||
Password: password,
|
||||
Role: entities.RoleAdmin,
|
||||
IsInitialLogin: true,
|
||||
Audit: entities.Audit{
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
|
@ -157,29 +186,44 @@ func runRestServer(applicationService services.ApplicationService) {
|
|||
gin.SetMode(gin.ReleaseMode)
|
||||
gin.EnableJsonDecoderDisallowUnknownFields()
|
||||
app := gin.Default()
|
||||
app.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowHeaders: []string{"*"},
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
app.Use(middleware.ValidateCors(config.AllowedOrigins))
|
||||
// Enable dex routes only if passed via environment variables
|
||||
if utils.DexEnabled {
|
||||
routes.DexRouter(app, applicationService)
|
||||
}
|
||||
routes.CapabilitiesRouter(app)
|
||||
routes.MiscRouter(app, applicationService)
|
||||
routes.UserRouter(app, applicationService)
|
||||
routes.ProjectRouter(app, applicationService)
|
||||
|
||||
log.Infof("Listening and serving HTTP on %s", utils.Port)
|
||||
err := app.Run(utils.Port)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err)
|
||||
if utils.EnableInternalTls {
|
||||
if utils.TlsCertPath != "" && utils.TlSKeyPath != "" {
|
||||
conf := utils.GetTlsConfig()
|
||||
server := http.Server{
|
||||
Addr: ":" + utils.RestPort,
|
||||
Handler: app,
|
||||
TLSConfig: conf,
|
||||
}
|
||||
log.Infof("Listening and serving HTTPS on :%s", utils.RestPort)
|
||||
err := server.ListenAndServeTLS("", "")
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Failure to start chaoscenter authentication REST server due to empty TLS cert file path and TLS key path")
|
||||
}
|
||||
} else {
|
||||
log.Infof("Listening and serving HTTP on :%s", utils.RestPort)
|
||||
err := app.Run(":" + utils.RestPort)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runGrpcServer(applicationService services.ApplicationService) {
|
||||
// Starting gRPC server
|
||||
lis, err := net.Listen("tcp", utils.GrpcPort)
|
||||
lis, err := net.Listen("tcp", ":"+utils.GrpcPort)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication server due"+
|
||||
" to %s", err)
|
||||
|
@ -187,9 +231,37 @@ func runGrpcServer(applicationService services.ApplicationService) {
|
|||
grpcApplicationServer := grpcHandler.ServerGrpc{ApplicationService: applicationService}
|
||||
grpcServer := grpc.NewServer()
|
||||
grpcPresenter.RegisterAuthRpcServiceServer(grpcServer, &grpcApplicationServer)
|
||||
log.Infof("Listening and serving gRPC on %s", utils.GrpcPort)
|
||||
log.Infof("Listening and serving gRPC on :%s", utils.GrpcPort)
|
||||
err = grpcServer.Serve(lis)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication GRPC server due to %v", err)
|
||||
log.Fatalf("Failure to start chaoscenter authentication GRPC server due to %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runGrpcServerWithTLS(applicationService services.ApplicationService) {
|
||||
|
||||
// Starting gRPC server
|
||||
lis, err := net.Listen("tcp", ":"+utils.GrpcPort)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication server due to %s", err)
|
||||
}
|
||||
|
||||
// configuring TLS config based on provided certificates & keys
|
||||
conf := utils.GetTlsConfig()
|
||||
|
||||
// create tls credentials
|
||||
tlsCredentials := credentials.NewTLS(conf)
|
||||
|
||||
// create grpc server with tls credential
|
||||
grpcServer := grpc.NewServer(grpc.Creds(tlsCredentials))
|
||||
|
||||
grpcApplicationServer := grpcHandler.ServerGrpc{ApplicationService: applicationService}
|
||||
|
||||
grpcPresenter.RegisterAuthRpcServiceServer(grpcServer, &grpcApplicationServer)
|
||||
|
||||
log.Infof("Listening and serving gRPC on :%s with TLS", utils.GrpcPort)
|
||||
err = grpcServer.Serve(lis)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start chaoscenter authentication GRPC server due to %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
AllowedOrigin string = "Access-Control-Allow-Origin"
|
||||
AllowedMethods string = "Access-Control-Allow-Methods"
|
||||
AllowedHeaders string = "Access-Control-Allow-Headers"
|
||||
AllowedCredentials string = "Access-Control-Allow-Credentials"
|
||||
)
|
||||
|
||||
func ValidateCors(allowedOrigins []string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
origin := c.GetHeader("Origin")
|
||||
if origin == "" {
|
||||
origin = c.Request.Host
|
||||
}
|
||||
|
||||
validOrigin := false
|
||||
for _, allowedOrigin := range allowedOrigins {
|
||||
match, err := regexp.MatchString(allowedOrigin, origin)
|
||||
if err == nil && match {
|
||||
validOrigin = true
|
||||
c.Writer.Header().Set(AllowedOrigin, origin)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validOrigin {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Invalid origin",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set(AllowedMethods, strings.Join([]string{
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"OPTIONS",
|
||||
}, ","))
|
||||
c.Writer.Header().Set(AllowedHeaders, "*")
|
||||
c.Writer.Header().Set(AllowedCredentials, "true")
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package middleware_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJwtMiddleware(t *testing.T) {
|
||||
router := gin.Default()
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
router.GET("/status", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Access Granted"})
|
||||
})
|
||||
|
||||
createRequest := func(token string) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest("GET", "/status", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
w := httptest.NewRecorder()
|
||||
return w
|
||||
}
|
||||
|
||||
t.Run("Valid Token", func(t *testing.T) {
|
||||
wValid := createRequest("jwtstring")
|
||||
assert.Equal(t, http.StatusOK, wValid.Code)
|
||||
})
|
||||
|
||||
t.Run("Missing Authorization Header", func(t *testing.T) {
|
||||
wMissingHeader := httptest.NewRecorder()
|
||||
reqMissingHeader := httptest.NewRequest("GET", "/status", nil)
|
||||
router.ServeHTTP(wMissingHeader, reqMissingHeader)
|
||||
assert.Equal(t, http.StatusUnauthorized, wMissingHeader.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestJwtMiddleware_SetClaimsAndCallNextHandler(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
router.GET("/status", func(c *gin.Context) {
|
||||
username, _ := c.Get("username")
|
||||
uid, _ := c.Get("uid")
|
||||
role, _ := c.Get("role")
|
||||
assert.Equal(t, "testuser", username)
|
||||
assert.Equal(t, "12345", uid)
|
||||
assert.Equal(t, "admin", role)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Access Granted"})
|
||||
})
|
||||
|
||||
mockService.On("ValidateToken", "jwtstring").Return(&jwt.Token{
|
||||
Valid: true,
|
||||
Claims: jwt.MapClaims{
|
||||
"username": "testuser",
|
||||
"uid": "12345",
|
||||
"role": "admin",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/status", nil)
|
||||
req.Header.Set("Authorization", "Bearer jwtstring")
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestJwtMiddleware_TokenValidationFailure(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
router.GET("/status", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Access Granted"})
|
||||
})
|
||||
|
||||
mockService.On("ValidateToken", "invalidtoken").Return(&jwt.Token{
|
||||
Valid: false,
|
||||
}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/status", nil)
|
||||
req.Header.Set("Authorization", "Bearer invalidtoken")
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
|
||||
func TestJwtMiddleware_Error(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
dummyToken := &jwt.Token{
|
||||
Raw: "DummyToken",
|
||||
Method: jwt.SigningMethodHS256,
|
||||
Header: map[string]interface{}{"alg": "HS256"},
|
||||
Claims: jwt.MapClaims{"foo": "bar"},
|
||||
Signature: "",
|
||||
Valid: false,
|
||||
}
|
||||
|
||||
mockService.On("ValidateToken", "dummy").Return(dummyToken, errors.New("mock error"))
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("Authorization", "Bearer dummy")
|
||||
resp := httptest.NewRecorder()
|
||||
router.ServeHTTP(resp, req)
|
||||
|
||||
if status := resp.Code; status != utils.ErrorStatusCodes[utils.ErrUnauthorized] {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, utils.ErrorStatusCodes[utils.ErrUnauthorized])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// ServerGrpc is a mock type for the ServerGrpc type
|
||||
type MockAuthRpcServiceClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockAuthRpcServiceClient) ValidateRequest(ctx context.Context, in *protos.ValidationRequest, opts ...grpc.CallOption) (*protos.ValidationResponse, error) {
|
||||
args := m.Called(ctx, in, opts)
|
||||
if vr, ok := args.Get(0).(*protos.ValidationResponse); ok {
|
||||
return vr, args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockAuthRpcServiceClient) GetProjectById(ctx context.Context, in *protos.GetProjectByIdRequest, opts ...grpc.CallOption) (*protos.GetProjectByIdResponse, error) {
|
||||
args := m.Called(ctx, in, opts)
|
||||
if gpir, ok := args.Get(0).(*protos.GetProjectByIdResponse); ok {
|
||||
return gpir, args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockAuthRpcServiceClient) GetUserById(ctx context.Context, in *protos.GetUserByIdRequest, opts ...grpc.CallOption) (*protos.GetUserByIdResponse, error) {
|
||||
args := m.Called(ctx, in, opts)
|
||||
if guir, ok := args.Get(0).(*protos.GetUserByIdResponse); ok {
|
||||
return guir, args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type MockedApplicationService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) IsAdministrator(user *entities.User) error {
|
||||
args := m.Called(user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdatePassword(userPassword *entities.UserPassword, isAdminBeingReset bool) error {
|
||||
args := m.Called(userPassword, isAdminBeingReset)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) AddMember(projectID string, member *entities.Member) error {
|
||||
args := m.Called(projectID, member)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) LoginUser(user *entities.User) (*entities.User, error) {
|
||||
args := m.Called(user)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetUser(uid string) (*entities.User, error) {
|
||||
args := m.Called(uid)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetUsers() (*[]entities.User, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).(*[]entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) FindUsersByUID(uid []string) (*[]entities.User, error) {
|
||||
args := m.Called(uid)
|
||||
return args.Get(0).(*[]entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) FindUserByUsername(username string) (*entities.User, error) {
|
||||
args := m.Called(username)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CheckPasswordHash(hash, password string) error {
|
||||
args := m.Called(hash, password)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateUser(user *entities.User) (*entities.User, error) {
|
||||
args := m.Called(user)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateUser(user *entities.UserDetails) error {
|
||||
args := m.Called(user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateUserByQuery(filter bson.D, updateQuery bson.D) error {
|
||||
args := m.Called(filter, updateQuery)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateUserState(ctx context.Context, username string, isDeactivate bool, deactivateTime int64) error {
|
||||
args := m.Called(ctx, username, isDeactivate, deactivateTime)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) InviteUsers(invitedUsers []string) (*[]entities.User, error) {
|
||||
args := m.Called(invitedUsers)
|
||||
return args.Get(0).(*[]entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectByProjectID(projectID string) (*entities.Project, error) {
|
||||
args := m.Called(projectID)
|
||||
return args.Get(0).(*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjects(query bson.D) ([]*entities.Project, error) {
|
||||
args := m.Called(query)
|
||||
return args.Get(0).([]*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error) {
|
||||
args := m.Called(request)
|
||||
return args.Get(0).(*entities.ListProjectResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectStats() ([]*entities.ProjectStats, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]*entities.ProjectStats), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateProject(project *entities.Project) error {
|
||||
args := m.Called(project)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) RemoveInvitation(projectID, userID string, invitation entities.Invitation) error {
|
||||
args := m.Called(projectID, userID, invitation)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateInvite(projectID, userID string, invitation entities.Invitation, role *entities.MemberRole) error {
|
||||
args := m.Called(projectID, userID, invitation, role)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateProjectName(projectID, projectName string) error {
|
||||
args := m.Called(projectID, projectName)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateMemberRole(projectID, userID string, role *entities.MemberRole) error {
|
||||
args := m.Called(projectID, userID, role)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetAggregateProjects(pipeline mongo.Pipeline, opts *options.AggregateOptions) (*mongo.Cursor, error) {
|
||||
args := m.Called(pipeline, opts)
|
||||
return args.Get(0).(*mongo.Cursor), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateProjectState(ctx context.Context, userID string, deactivateTime int64, isDeactivate bool) error {
|
||||
args := m.Called(ctx, userID, deactivateTime, isDeactivate)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetOwnerProjectIDs(ctx context.Context, userID string) ([]*entities.Project, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectRole(projectID, userID string) (*entities.MemberRole, error) {
|
||||
args := m.Called(projectID, userID)
|
||||
return args.Get(0).(*entities.MemberRole), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectMembers(projectID, state string) ([]*entities.Member, error) {
|
||||
args := m.Called(projectID, state)
|
||||
return args.Get(0).([]*entities.Member), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectOwners(projectID string) ([]*entities.Member, error) {
|
||||
args := m.Called(projectID)
|
||||
return args.Get(0).([]*entities.Member), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ListInvitations(userID string, invitationState entities.Invitation) ([]*entities.Project, error) {
|
||||
args := m.Called(userID, invitationState)
|
||||
return args.Get(0).([]*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) RevokeToken(tokenString string) error {
|
||||
args := m.Called(tokenString)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ValidateToken(encodedToken string) (*jwt.Token, error) {
|
||||
args := m.Called(encodedToken)
|
||||
return args.Get(0).(*jwt.Token), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetSignedJWT(user *entities.User, jwtSecret string) (string, error) {
|
||||
args := m.Called(user, jwtSecret)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateApiToken(user *entities.User, request entities.ApiTokenInput) (string, error) {
|
||||
args := m.Called(user, request)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetApiTokensByUserID(userID string) ([]entities.ApiToken, error) {
|
||||
args := m.Called(userID)
|
||||
return args.Get(0).([]entities.ApiToken), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) DeleteApiToken(token string) error {
|
||||
args := m.Called(token)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ListCollection() ([]string, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ListDataBase() ([]string, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateStateTransaction(userRequest entities.UpdateUserState) error {
|
||||
args := m.Called(userRequest)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) RbacValidator(userID, resourceID string, rules []string, invitationStatus string) error {
|
||||
args := m.Called(userID, resourceID, rules, invitationStatus)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) DeleteProject(projectID string) error {
|
||||
args := m.Called(projectID)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateConfig(config authConfig.AuthConfig) error {
|
||||
args := m.Called(config)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetConfig(key string) (*authConfig.AuthConfig, error) {
|
||||
args := m.Called(key)
|
||||
return args.Get(0).(*authConfig.AuthConfig), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateConfig(ctx context.Context, key string, value interface{}) error {
|
||||
args := m.Called(ctx, key, value)
|
||||
return args.Error(0)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CapabilitiesRouter creates all the required routes for exposing capabilities.
|
||||
func CapabilitiesRouter(router *gin.Engine) {
|
||||
router.GET("/capabilities", rest.GetCapabilities())
|
||||
}
|
|
@ -13,6 +13,7 @@ func ProjectRouter(router *gin.Engine, service services.ApplicationService) {
|
|||
router.Use(middleware.JwtMiddleware(service))
|
||||
router.GET("/get_project/:project_id", rest.GetProject(service))
|
||||
router.GET("/get_project_members/:project_id/:state", rest.GetActiveProjectMembers(service))
|
||||
router.GET("/get_project_owners/:project_id", rest.GetActiveProjectOwners(service))
|
||||
router.GET("/get_user_with_project/:username", rest.GetUserWithProject(service))
|
||||
router.GET("/get_owner_projects", rest.GetOwnerProjects(service))
|
||||
router.GET("/get_project_role/:project_id", rest.GetProjectRole(service))
|
||||
|
@ -26,4 +27,6 @@ func ProjectRouter(router *gin.Engine, service services.ApplicationService) {
|
|||
router.POST("/remove_invitation", rest.RemoveInvitation(service))
|
||||
router.POST("/leave_project", rest.LeaveProject(service))
|
||||
router.POST("/update_project_name", rest.UpdateProjectName(service))
|
||||
router.POST("/update_member_role", rest.UpdateMemberRole(service))
|
||||
router.POST("/delete_project/:project_id", rest.DeleteProject(service))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package types
|
||||
|
||||
type MemberState string
|
||||
|
||||
const (
|
||||
MemberStateActive MemberState = "active"
|
||||
MemberStateInactive MemberState = "inactive"
|
||||
)
|
||||
|
||||
const (
|
||||
ProjectName = "projectName"
|
||||
SortField = "sortField"
|
||||
Ascending = "sortAscending"
|
||||
CreatedByMe = "createdByMe"
|
||||
Page = "page"
|
||||
Limit = "limit"
|
||||
)
|
|
@ -0,0 +1,208 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/types"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func GetProjectFilters(c *gin.Context) *entities.ListProjectRequest {
|
||||
var request entities.ListProjectRequest
|
||||
|
||||
uID, exists := c.Get("uid")
|
||||
if exists {
|
||||
request.UserID = uID.(string)
|
||||
}
|
||||
|
||||
// Initialize request.Filter and request.Sort if they are nil
|
||||
if request.Filter == nil {
|
||||
request.Filter = &entities.ListProjectInputFilter{}
|
||||
}
|
||||
if request.Sort == nil {
|
||||
request.Sort = &entities.SortInput{}
|
||||
}
|
||||
|
||||
// filters
|
||||
createdByMeStr := c.Query(types.CreatedByMe)
|
||||
if createdByMeStr != "" {
|
||||
createdByMe, err := strconv.ParseBool(createdByMeStr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
request.Filter.CreatedByMe = &createdByMe
|
||||
}
|
||||
|
||||
projectNameStr := c.Query(types.ProjectName)
|
||||
|
||||
if projectNameStr != "" {
|
||||
request.Filter.ProjectName = &projectNameStr
|
||||
}
|
||||
|
||||
// sorts
|
||||
var sortField entities.ProjectSortingField
|
||||
sortFieldStr := c.Query(types.SortField)
|
||||
|
||||
// Convert the string value to the appropriate type
|
||||
switch sortFieldStr {
|
||||
case "name":
|
||||
sortField = entities.ProjectSortingFieldName
|
||||
case "time":
|
||||
sortField = entities.ProjectSortingFieldTime
|
||||
default:
|
||||
sortField = entities.ProjectSortingFieldTime
|
||||
}
|
||||
|
||||
// Now assign the converted value to the sort field
|
||||
request.Sort.Field = &sortField
|
||||
|
||||
ascendingStr := c.Query(types.Ascending)
|
||||
if ascendingStr != "" {
|
||||
ascending, err := strconv.ParseBool(ascendingStr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
request.Sort.Ascending = &ascending
|
||||
}
|
||||
|
||||
// pagination
|
||||
// Extract page and limit from query parameters
|
||||
pageStr := c.Query(types.Page)
|
||||
limitStr := c.Query(types.Limit)
|
||||
|
||||
// Convert strings to integers
|
||||
page, err := strconv.Atoi(pageStr)
|
||||
if err != nil {
|
||||
// Handle error if conversion fails
|
||||
// For example, set a default value or return an error response
|
||||
page = 0 // Setting a default value of 1
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil {
|
||||
// Handle error if conversion fails
|
||||
// For example, set a default value or return an error response
|
||||
limit = 15 // Setting a default value of 15
|
||||
}
|
||||
|
||||
pagination := entities.Pagination{
|
||||
Page: page,
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
request.Pagination = &pagination
|
||||
|
||||
return &request
|
||||
}
|
||||
|
||||
func CreateMatchStage(userID string) bson.D {
|
||||
return bson.D{
|
||||
{"$match", bson.D{
|
||||
{"is_removed", false},
|
||||
{"members", bson.D{
|
||||
{"$elemMatch", bson.D{
|
||||
{"user_id", userID},
|
||||
{"invitation", bson.D{
|
||||
{"$nin", bson.A{
|
||||
string(entities.PendingInvitation),
|
||||
string(entities.DeclinedInvitation),
|
||||
string(entities.ExitedProject),
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateFilterStages(filter *entities.ListProjectInputFilter, userID string) []bson.D {
|
||||
var stages []bson.D
|
||||
|
||||
if filter == nil {
|
||||
return stages
|
||||
}
|
||||
|
||||
if filter.CreatedByMe != nil {
|
||||
if *filter.CreatedByMe {
|
||||
stages = append(stages, bson.D{
|
||||
{"$match", bson.D{
|
||||
{"created_by.user_id", bson.M{"$eq": userID}},
|
||||
}},
|
||||
})
|
||||
} else {
|
||||
stages = append(stages, bson.D{
|
||||
{"$match", bson.D{
|
||||
{"created_by.user_id", bson.M{"$ne": userID}},
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if filter.ProjectName != nil {
|
||||
stages = append(stages, bson.D{
|
||||
{"$match", bson.D{
|
||||
{"name", bson.D{
|
||||
{"$regex", primitive.Regex{Pattern: *filter.ProjectName, Options: "i"}},
|
||||
}},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
return stages
|
||||
}
|
||||
|
||||
func CreateSortStage(sort *entities.SortInput) bson.D {
|
||||
if sort == nil || sort.Field == nil {
|
||||
return bson.D{}
|
||||
}
|
||||
|
||||
var sortField string
|
||||
switch *sort.Field {
|
||||
case entities.ProjectSortingFieldTime:
|
||||
sortField = "updated_at"
|
||||
case entities.ProjectSortingFieldName:
|
||||
sortField = "name"
|
||||
default:
|
||||
sortField = "updated_at"
|
||||
}
|
||||
|
||||
sortDirection := -1
|
||||
if sort.Ascending != nil && *sort.Ascending {
|
||||
sortDirection = 1
|
||||
}
|
||||
|
||||
return bson.D{
|
||||
{"$sort", bson.D{
|
||||
{sortField, sortDirection},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePaginationStage(pagination *entities.Pagination) []bson.D {
|
||||
var stages []bson.D
|
||||
if pagination != nil {
|
||||
page := pagination.Page
|
||||
limit := pagination.Limit
|
||||
// upper limit of 50 to prevent exceeding max limit 16mb
|
||||
if pagination.Limit > 50 {
|
||||
limit = 50
|
||||
}
|
||||
stages = append(stages, bson.D{
|
||||
{"$skip", page * limit},
|
||||
})
|
||||
stages = append(stages, bson.D{
|
||||
{"$limit", limit},
|
||||
})
|
||||
} else {
|
||||
stages = append(stages, bson.D{
|
||||
{"$limit", 10},
|
||||
})
|
||||
}
|
||||
return stages
|
||||
}
|
|
@ -1,52 +1,69 @@
|
|||
module github.com/litmuschaos/litmus/chaoscenter/authentication
|
||||
|
||||
go 1.20
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/coreos/go-oidc/v3 v3.1.0
|
||||
github.com/gin-contrib/cors v1.3.1
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
go.mongodb.org/mongo-driver v1.5.3
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
|
||||
google.golang.org/grpc v1.32.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/swaggo/swag v1.16.3
|
||||
go.mongodb.org/mongo-driver v1.17.1
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
google.golang.org/grpc v1.66.2
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.34.28 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.9 // indirect
|
||||
github.com/klauspost/compress v1.9.5 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
@ -1,532 +1,194 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk=
|
||||
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
|
||||
github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
|
||||
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
|
||||
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
|
||||
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
|
||||
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
|
||||
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
|
||||
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
|
||||
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
|
||||
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
|
||||
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
|
||||
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
|
||||
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
|
||||
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
|
||||
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
|
||||
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
|
||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
||||
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
|
||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.mongodb.org/mongo-driver v1.5.3 h1:wWbFB6zaGHpzguF3f7tW94sVE8sFl3lHx8OZx/4OuFI=
|
||||
go.mongodb.org/mongo-driver v1.5.3/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
|
||||
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package authConfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
CreateConfig(config AuthConfig) error
|
||||
GetConfig(key string) (*AuthConfig, error)
|
||||
UpdateConfig(ctx context.Context, key string, value interface{}) error
|
||||
}
|
||||
|
||||
type repository struct {
|
||||
Collection *mongo.Collection
|
||||
}
|
||||
|
||||
func (r repository) CreateConfig(config AuthConfig) error {
|
||||
_, err := r.Collection.InsertOne(context.Background(), config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r repository) GetConfig(key string) (*AuthConfig, error) {
|
||||
results := r.Collection.FindOne(context.Background(), bson.D{
|
||||
{"key", key},
|
||||
})
|
||||
|
||||
var config AuthConfig
|
||||
err := results.Decode(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedValue, err := base64.URLEncoding.DecodeString(config.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Value = string(decodedValue)
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func (r repository) UpdateConfig(ctx context.Context, key string, value interface{}) error {
|
||||
query := bson.D{
|
||||
{"key", key},
|
||||
}
|
||||
update := bson.D{{"$set", bson.D{{
|
||||
"value", value}},
|
||||
}}
|
||||
_, err := r.Collection.UpdateOne(ctx, query, update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewAuthConfigRepo creates a new instance of this repository
|
||||
func NewAuthConfigRepo(collection *mongo.Collection) Repository {
|
||||
return &repository{
|
||||
Collection: collection,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package authConfig
|
||||
|
||||
type AuthConfig struct {
|
||||
Key string `bson:"key"`
|
||||
Value string `bson:"value"`
|
||||
}
|
|
@ -2,17 +2,23 @@ package entities
|
|||
|
||||
// Project contains the required fields to be stored in the database for a project
|
||||
type Project struct {
|
||||
Audit `bson:",inline"`
|
||||
ID string `bson:"_id" json:"projectID"`
|
||||
Name string `bson:"name" json:"name"`
|
||||
Members []*Member `bson:"members" json:"members"`
|
||||
State *string `bson:"state" json:"state"`
|
||||
Audit `bson:",inline"`
|
||||
ID string `bson:"_id" json:"projectID"`
|
||||
Name string `bson:"name" json:"name"`
|
||||
Members []*Member `bson:"members" json:"members"`
|
||||
State *string `bson:"state" json:"state"`
|
||||
Tags []*string `bson:"tags" json:"tags"`
|
||||
Description *string `bson:"description" json:"description"`
|
||||
}
|
||||
|
||||
type Owner struct {
|
||||
UserID string `bson:"user_id" json:"userID"`
|
||||
Username string `bson:"username" json:"username"`
|
||||
UserID string `bson:"user_id" json:"userID"`
|
||||
Username string `bson:"username" json:"username"`
|
||||
Invitation Invitation `bson:"invitation" json:"invitation"`
|
||||
JoinedAt int64 `bson:"joined_at" json:"joinedAt"`
|
||||
DeactivatedAt *int64 `bson:"deactivated_at,omitempty" json:"deactivatedAt,omitempty"`
|
||||
}
|
||||
|
||||
type MemberStat struct {
|
||||
Owner *[]Owner `bson:"owner" json:"owner"`
|
||||
Total int `bson:"total" json:"total"`
|
||||
|
@ -46,8 +52,14 @@ type ProjectInput struct {
|
|||
}
|
||||
|
||||
type CreateProjectInput struct {
|
||||
ProjectName string `bson:"project_name" json:"projectName"`
|
||||
UserID string `bson:"user_id" json:"userID"`
|
||||
ProjectName string `bson:"project_name" json:"projectName"`
|
||||
UserID string `bson:"user_id" json:"userID"`
|
||||
Description *string `bson:"description" json:"description"`
|
||||
Tags []*string `bson:"tags" json:"tags"`
|
||||
}
|
||||
|
||||
type DeleteProjectInput struct {
|
||||
ProjectID string `json:"projectID"`
|
||||
}
|
||||
|
||||
type MemberInput struct {
|
||||
|
@ -63,6 +75,50 @@ type ListInvitationResponse struct {
|
|||
InvitationRole MemberRole `json:"invitationRole"`
|
||||
}
|
||||
|
||||
type ListProjectResponse struct {
|
||||
Projects []*Project `json:"projects"`
|
||||
TotalNumberOfProjects *int64 `json:"totalNumberOfProjects"`
|
||||
}
|
||||
|
||||
type Pagination struct {
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
type ListProjectRequest struct {
|
||||
UserID string `json:"userID"`
|
||||
Sort *SortInput `json:"sort,omitempty"`
|
||||
Filter *ListProjectInputFilter `json:"filter,omitempty"`
|
||||
Pagination *Pagination `json:"pagination,omitempty"`
|
||||
}
|
||||
|
||||
type ListProjectInputFilter struct {
|
||||
ProjectName *string `json:"projectName"`
|
||||
CreatedByMe *bool `json:"createdByMe"`
|
||||
InvitedByOthers *bool `json:"invitedByOthers"`
|
||||
}
|
||||
|
||||
type ProjectSortingField string
|
||||
|
||||
const (
|
||||
ProjectSortingFieldName ProjectSortingField = "NAME"
|
||||
ProjectSortingFieldTime ProjectSortingField = "TIME"
|
||||
)
|
||||
|
||||
type SortInput struct {
|
||||
Field *ProjectSortingField `json:"field"`
|
||||
Ascending *bool `json:"ascending"`
|
||||
}
|
||||
|
||||
const (
|
||||
ProjectName = "projectName"
|
||||
SortField = "sortField"
|
||||
Ascending = "sortAscending"
|
||||
CreatedByMe = "createdByMe"
|
||||
Page = "page"
|
||||
Limit = "limit"
|
||||
)
|
||||
|
||||
// GetProjectOutput takes a Project struct as input and returns the graphQL model equivalent
|
||||
func (project *Project) GetProjectOutput() *Project {
|
||||
|
||||
|
@ -108,9 +164,9 @@ func (member *Member) GetMemberOutput() *Member {
|
|||
type MemberRole string
|
||||
|
||||
const (
|
||||
RoleOwner MemberRole = "Owner"
|
||||
RoleEditor MemberRole = "Editor"
|
||||
RoleViewer MemberRole = "Viewer"
|
||||
RoleOwner MemberRole = "Owner"
|
||||
RoleExecutor MemberRole = "Executor"
|
||||
RoleViewer MemberRole = "Viewer"
|
||||
)
|
||||
|
||||
// Invitation defines the type of the invitation that is sent by the Owner of the project to other users
|
||||
|
|
|
@ -16,7 +16,8 @@ type ApiTokenInput struct {
|
|||
|
||||
// DeleteApiTokenInput struct for storing DeleteApiTokenInput
|
||||
type DeleteApiTokenInput struct {
|
||||
Token string `json:"token"`
|
||||
Token string `json:"token"`
|
||||
UserID string `json:"userID"`
|
||||
}
|
||||
|
||||
// ApiToken struct for storing API tokens
|
||||
|
|
|
@ -17,22 +17,23 @@ const (
|
|||
|
||||
// User contains the user information
|
||||
type User struct {
|
||||
Audit `bson:",inline"`
|
||||
ID string `bson:"_id,omitempty" json:"userID"`
|
||||
Username string `bson:"username,omitempty" json:"username"`
|
||||
Password string `bson:"password,omitempty" json:"password,omitempty"`
|
||||
Email string `bson:"email,omitempty" json:"email,omitempty"`
|
||||
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||
Role Role `bson:"role,omitempty" json:"role"`
|
||||
DeactivatedAt *int64 `bson:"deactivated_at,omitempty" json:"deactivatedAt,omitempty"`
|
||||
Audit `bson:",inline"`
|
||||
ID string `bson:"_id,omitempty" json:"userID"`
|
||||
Username string `bson:"username,omitempty" json:"username"`
|
||||
Password string `bson:"password,omitempty" json:"password,omitempty"`
|
||||
Salt string `bson:"salt" json:"salt"`
|
||||
Email string `bson:"email,omitempty" json:"email,omitempty"`
|
||||
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||
Role Role `bson:"role,omitempty" json:"role"`
|
||||
DeactivatedAt *int64 `bson:"deactivated_at,omitempty" json:"deactivatedAt,omitempty"`
|
||||
IsInitialLogin bool `bson:"is_initial_login" json:"isInitialLogin"`
|
||||
}
|
||||
|
||||
// UserDetails is used to update user's personal details
|
||||
type UserDetails struct {
|
||||
ID string `bson:"id,omitempty"`
|
||||
Email string `bson:"email,omitempty" json:"email,omitempty"`
|
||||
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||
Password string `bson:"password,omitempty" json:"password,omitempty"`
|
||||
ID string `bson:"id,omitempty"`
|
||||
Email string `bson:"email,omitempty" json:"email,omitempty"`
|
||||
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// UserPassword defines structure for password related requests
|
||||
|
@ -48,11 +49,6 @@ type UpdateUserState struct {
|
|||
IsDeactivate *bool `json:"isDeactivate"`
|
||||
}
|
||||
|
||||
// APIStatus defines structure for APIroute status
|
||||
type APIStatus struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type UserWithProject struct {
|
||||
Audit `bson:",inline"`
|
||||
ID string `bson:"_id" json:"id"`
|
||||
|
@ -62,23 +58,6 @@ type UserWithProject struct {
|
|||
Projects []*Project `bson:"projects" json:"projects"`
|
||||
}
|
||||
|
||||
func (user User) GetUserWithProject() *UserWithProject {
|
||||
|
||||
return &UserWithProject{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Audit: Audit{
|
||||
IsRemoved: user.IsRemoved,
|
||||
CreatedAt: user.CreatedAt,
|
||||
CreatedBy: user.UpdatedBy,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
UpdatedBy: user.UpdatedBy,
|
||||
},
|
||||
Email: user.Email,
|
||||
}
|
||||
}
|
||||
|
||||
// SanitizedUser returns the user object without sensitive information
|
||||
func (user *User) SanitizedUser() *User {
|
||||
user.Password = ""
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"log"
|
||||
"time"
|
||||
|
||||
project_utils "github.com/litmuschaos/litmus/chaoscenter/authentication/api/utils"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
|
||||
|
@ -18,18 +19,21 @@ import (
|
|||
type Repository interface {
|
||||
GetProjectByProjectID(projectID string) (*entities.Project, error)
|
||||
GetProjects(query bson.D) ([]*entities.Project, error)
|
||||
GetProjectsByUserID(uid string, isOwner bool) ([]*entities.Project, error)
|
||||
GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error)
|
||||
GetProjectStats() ([]*entities.ProjectStats, error)
|
||||
CreateProject(project *entities.Project) error
|
||||
AddMember(projectID string, member *entities.Member) error
|
||||
RemoveInvitation(projectID string, userID string, invitation entities.Invitation) error
|
||||
UpdateInvite(projectID string, userID string, invitation entities.Invitation, role *entities.MemberRole) error
|
||||
UpdateProjectName(projectID string, projectName string) error
|
||||
UpdateMemberRole(projectID string, userID string, role *entities.MemberRole) error
|
||||
GetAggregateProjects(pipeline mongo.Pipeline, opts *options.AggregateOptions) (*mongo.Cursor, error)
|
||||
UpdateProjectState(ctx context.Context, userID string, deactivateTime int64, isDeactivate bool) error
|
||||
GetOwnerProjects(ctx context.Context, userID string) ([]*entities.Project, error)
|
||||
GetProjectRole(projectID string, userID string) (*entities.MemberRole, error)
|
||||
GetProjectMembers(projectID string, state string) ([]*entities.Member, error)
|
||||
GetProjectOwners(projectID string) ([]*entities.Member, error)
|
||||
DeleteProject(projectID string) error
|
||||
ListInvitations(userID string, invitationState entities.Invitation) ([]*entities.Project, error)
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,7 @@ func (r repository) GetProjects(query bson.D) ([]*entities.Project, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var projects []*entities.Project
|
||||
var projects = []*entities.Project{}
|
||||
err = results.All(context.TODO(), &projects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -66,51 +70,82 @@ func (r repository) GetProjects(query bson.D) ([]*entities.Project, error) {
|
|||
}
|
||||
|
||||
// GetProjectsByUserID returns a project based on the userID
|
||||
func (r repository) GetProjectsByUserID(userID string, isOwner bool) ([]*entities.Project, error) {
|
||||
var projects []*entities.Project
|
||||
query := bson.D{}
|
||||
func (r repository) GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error) {
|
||||
var projects = []*entities.Project{}
|
||||
ctx := context.TODO()
|
||||
|
||||
if isOwner == true {
|
||||
query = bson.D{
|
||||
{"members", bson.D{
|
||||
{"$elemMatch", bson.D{
|
||||
{"user_id", userID},
|
||||
{"role", bson.D{
|
||||
{"$eq", entities.RoleOwner},
|
||||
}},
|
||||
}},
|
||||
}}}
|
||||
// Construct the pipeline
|
||||
var pipeline mongo.Pipeline
|
||||
|
||||
// Match stage
|
||||
pipeline = append(pipeline, project_utils.CreateMatchStage(request.UserID))
|
||||
|
||||
// Filter stage
|
||||
if request.Filter != nil {
|
||||
filterStages := project_utils.CreateFilterStages(request.Filter, request.UserID)
|
||||
pipeline = append(pipeline, filterStages...)
|
||||
}
|
||||
|
||||
// Sort stage
|
||||
sortStage := project_utils.CreateSortStage(request.Sort)
|
||||
if len(sortStage) > 0 {
|
||||
pipeline = append(pipeline, sortStage)
|
||||
}
|
||||
|
||||
// Pagination stages
|
||||
paginationStages := project_utils.CreatePaginationStage(request.Pagination)
|
||||
|
||||
// Facet stage to count total projects and paginate results
|
||||
facetStage := bson.D{
|
||||
{"$facet", bson.D{
|
||||
{"totalCount", bson.A{
|
||||
bson.D{{"$count", "totalNumberOfProjects"}},
|
||||
}},
|
||||
{"projects", append(mongo.Pipeline{}, paginationStages...)},
|
||||
}},
|
||||
}
|
||||
pipeline = append(pipeline, facetStage)
|
||||
|
||||
// Execute the aggregate pipeline
|
||||
cursor, err := r.Collection.Aggregate(ctx, pipeline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
// Extract results
|
||||
var result struct {
|
||||
TotalCount []struct {
|
||||
TotalNumberOfProjects int64 `bson:"totalNumberOfProjects"`
|
||||
} `bson:"totalCount"`
|
||||
Projects []*entities.Project `bson:"projects"`
|
||||
}
|
||||
|
||||
if cursor.Next(ctx) {
|
||||
if err := cursor.Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var totalNumberOfProjects int64
|
||||
if len(result.TotalCount) > 0 {
|
||||
totalNumberOfProjects = result.TotalCount[0].TotalNumberOfProjects
|
||||
} else {
|
||||
query = bson.D{
|
||||
{"is_removed", false},
|
||||
{"members", bson.D{
|
||||
{"$elemMatch", bson.D{
|
||||
{"user_id", userID},
|
||||
{"$and", bson.A{
|
||||
bson.D{{"invitation", bson.D{
|
||||
{"$ne", entities.PendingInvitation},
|
||||
}}},
|
||||
bson.D{{"invitation", bson.D{
|
||||
{"$ne", entities.DeclinedInvitation},
|
||||
}}},
|
||||
bson.D{{"invitation", bson.D{
|
||||
{"$ne", entities.ExitedProject},
|
||||
}}},
|
||||
}},
|
||||
}},
|
||||
}}}
|
||||
zero := int64(0)
|
||||
return &entities.ListProjectResponse{
|
||||
Projects: projects,
|
||||
TotalNumberOfProjects: &zero,
|
||||
}, nil
|
||||
}
|
||||
|
||||
result, err := r.Collection.Find(context.TODO(), query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = result.All(context.TODO(), &projects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
projects = result.Projects
|
||||
|
||||
response := entities.ListProjectResponse{
|
||||
Projects: projects,
|
||||
TotalNumberOfProjects: &totalNumberOfProjects,
|
||||
}
|
||||
|
||||
return projects, err
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// GetProjectStats returns stats related to projects in the DB
|
||||
|
@ -149,7 +184,7 @@ func (r repository) GetProjectStats() ([]*entities.ProjectStats, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var data []*entities.ProjectStats
|
||||
var data = []*entities.ProjectStats{}
|
||||
for result.Next(context.TODO()) {
|
||||
res := entities.ProjectStats{}
|
||||
if err := result.Decode(&res); err != nil {
|
||||
|
@ -175,7 +210,7 @@ func (r repository) CreateProject(project *entities.Project) error {
|
|||
|
||||
// AddMember adds a new member into the project whose projectID is passed
|
||||
func (r repository) AddMember(projectID string, member *entities.Member) error {
|
||||
query := bson.D{{"_id", projectID}}
|
||||
query := bson.D{{"_id", bson.D{{"$eq", projectID}}}}
|
||||
update := bson.D{{"$push", bson.D{
|
||||
{"members", member},
|
||||
}}}
|
||||
|
@ -193,7 +228,7 @@ func (r repository) AddMember(projectID string, member *entities.Member) error {
|
|||
|
||||
// RemoveInvitation removes member or cancels the invitation
|
||||
func (r repository) RemoveInvitation(projectID string, userID string, invitation entities.Invitation) error {
|
||||
query := bson.D{{"_id", projectID}}
|
||||
query := bson.D{{"_id", bson.D{{"$eq", projectID}}}}
|
||||
update := bson.D{
|
||||
{"$pull", bson.D{
|
||||
{"members", bson.D{
|
||||
|
@ -266,7 +301,7 @@ func (r repository) UpdateInvite(projectID string, userID string, invitation ent
|
|||
|
||||
// UpdateProjectName :Updates Name of the project
|
||||
func (r repository) UpdateProjectName(projectID string, projectName string) error {
|
||||
query := bson.D{{"_id", projectID}}
|
||||
query := bson.D{{"_id", bson.D{{"$eq", projectID}}}}
|
||||
update := bson.D{{"$set", bson.M{"name": projectName}}}
|
||||
|
||||
_, err := r.Collection.UpdateOne(context.TODO(), query, update)
|
||||
|
@ -277,6 +312,24 @@ func (r repository) UpdateProjectName(projectID string, projectName string) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateMemberRole : Updates Role of the member in the project.
|
||||
func (r repository) UpdateMemberRole(projectID string, userID string, role *entities.MemberRole) error {
|
||||
opts := options.Update().SetArrayFilters(options.ArrayFilters{
|
||||
Filters: []interface{}{
|
||||
bson.D{{"elem.user_id", userID}},
|
||||
},
|
||||
})
|
||||
query := bson.D{{"_id", bson.D{{"$eq", projectID}}}}
|
||||
update := bson.D{{"$set", bson.M{"members.$[elem].role": role}}}
|
||||
|
||||
_, err := r.Collection.UpdateOne(context.TODO(), query, update, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAggregateProjects takes a mongo pipeline to retrieve the project details from the database
|
||||
func (r repository) GetAggregateProjects(pipeline mongo.Pipeline, opts *options.AggregateOptions) (*mongo.Cursor, error) {
|
||||
results, err := r.Collection.Aggregate(context.TODO(), pipeline, opts)
|
||||
|
@ -372,7 +425,7 @@ func (r repository) GetOwnerProjects(ctx context.Context, userID string) ([]*ent
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var projects []*entities.Project
|
||||
var projects = []*entities.Project{}
|
||||
err = cursor.All(context.TODO(), &projects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -381,6 +434,28 @@ func (r repository) GetOwnerProjects(ctx context.Context, userID string) ([]*ent
|
|||
return projects, nil
|
||||
}
|
||||
|
||||
// GetProjectOwners takes projectID and returns the owners
|
||||
func (r repository) GetProjectOwners(projectID string) ([]*entities.Member, error) {
|
||||
filter := bson.D{{"_id", bson.D{{"$eq", projectID}}}}
|
||||
|
||||
var project struct {
|
||||
Members []*entities.Member `bson:"members"`
|
||||
}
|
||||
err := r.Collection.FindOne(context.TODO(), filter).Decode(&project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Filter the members to include only the owners
|
||||
var owners = []*entities.Member{}
|
||||
for _, member := range project.Members {
|
||||
if member.Role == entities.RoleOwner && member.Invitation == entities.AcceptedInvitation {
|
||||
owners = append(owners, member)
|
||||
}
|
||||
}
|
||||
return owners, nil
|
||||
}
|
||||
|
||||
// GetProjectRole returns the role of a user in the project
|
||||
func (r repository) GetProjectRole(projectID string, userID string) (*entities.MemberRole, error) {
|
||||
filter := bson.D{
|
||||
|
@ -540,7 +615,7 @@ func (r repository) ListInvitations(userID string, invitationState entities.Invi
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var projects []*entities.Project
|
||||
var projects = []*entities.Project{}
|
||||
err = cursor.All(context.TODO(), &projects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -556,3 +631,19 @@ func NewRepo(collection *mongo.Collection) Repository {
|
|||
Collection: collection,
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteProject deletes the project with given projectID
|
||||
func (r repository) DeleteProject(projectID string) error {
|
||||
query := bson.D{{"_id", bson.D{{"$eq", projectID}}}}
|
||||
|
||||
result, err := r.Collection.DeleteOne(context.TODO(), query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.DeletedCount == 0 {
|
||||
return errors.New("no project found with the given projectID")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
authConfig2 "github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/misc"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/project"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/session"
|
||||
|
@ -14,6 +15,7 @@ type ApplicationService interface {
|
|||
transactionService
|
||||
miscService
|
||||
sessionService
|
||||
authConfigService
|
||||
}
|
||||
|
||||
type applicationService struct {
|
||||
|
@ -22,17 +24,19 @@ type applicationService struct {
|
|||
miscRepository misc.Repository
|
||||
revokedTokenRepository session.RevokedTokenRepository
|
||||
apiTokenRepository session.ApiTokenRepository
|
||||
authConfigRepo authConfig2.Repository
|
||||
db *mongo.Database
|
||||
}
|
||||
|
||||
// NewService creates a new instance of this service
|
||||
func NewService(userRepo user.Repository, projectRepo project.Repository, miscRepo misc.Repository, revokedTokenRepo session.RevokedTokenRepository, apiTokenRepo session.ApiTokenRepository, db *mongo.Database) ApplicationService {
|
||||
func NewService(userRepo user.Repository, projectRepo project.Repository, miscRepo misc.Repository, revokedTokenRepo session.RevokedTokenRepository, apiTokenRepo session.ApiTokenRepository, authConfigRepo authConfig2.Repository, db *mongo.Database) ApplicationService {
|
||||
return &applicationService{
|
||||
userRepository: userRepo,
|
||||
projectRepository: projectRepo,
|
||||
revokedTokenRepository: revokedTokenRepo,
|
||||
apiTokenRepository: apiTokenRepo,
|
||||
db: db,
|
||||
authConfigRepo: authConfigRepo,
|
||||
miscRepository: miscRepo,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
)
|
||||
|
||||
type authConfigService interface {
|
||||
CreateConfig(config authConfig.AuthConfig) error
|
||||
GetConfig(key string) (*authConfig.AuthConfig, error)
|
||||
UpdateConfig(ctx context.Context, key string, value interface{}) error
|
||||
}
|
||||
|
||||
func (a applicationService) CreateConfig(config authConfig.AuthConfig) error {
|
||||
return a.authConfigRepo.CreateConfig(config)
|
||||
}
|
||||
|
||||
func (a applicationService) GetConfig(key string) (*authConfig.AuthConfig, error) {
|
||||
return a.authConfigRepo.GetConfig(key)
|
||||
}
|
||||
|
||||
func (a applicationService) UpdateConfig(ctx context.Context, key string, value interface{}) error {
|
||||
return a.authConfigRepo.UpdateConfig(ctx, key, value)
|
||||
}
|
|
@ -13,18 +13,21 @@ import (
|
|||
type projectService interface {
|
||||
GetProjectByProjectID(projectID string) (*entities.Project, error)
|
||||
GetProjects(query bson.D) ([]*entities.Project, error)
|
||||
GetProjectsByUserID(uid string, isOwner bool) ([]*entities.Project, error)
|
||||
GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error)
|
||||
GetProjectStats() ([]*entities.ProjectStats, error)
|
||||
CreateProject(project *entities.Project) error
|
||||
AddMember(projectID string, member *entities.Member) error
|
||||
RemoveInvitation(projectID string, userID string, invitation entities.Invitation) error
|
||||
UpdateInvite(projectID string, userID string, invitation entities.Invitation, role *entities.MemberRole) error
|
||||
UpdateProjectName(projectID string, projectName string) error
|
||||
UpdateMemberRole(projectID string, userID string, role *entities.MemberRole) error
|
||||
GetAggregateProjects(pipeline mongo.Pipeline, opts *options.AggregateOptions) (*mongo.Cursor, error)
|
||||
UpdateProjectState(ctx context.Context, userID string, deactivateTime int64, isDeactivate bool) error
|
||||
GetOwnerProjectIDs(ctx context.Context, userID string) ([]*entities.Project, error)
|
||||
GetProjectRole(projectID string, userID string) (*entities.MemberRole, error)
|
||||
GetProjectMembers(projectID string, state string) ([]*entities.Member, error)
|
||||
GetProjectOwners(projectID string) ([]*entities.Member, error)
|
||||
DeleteProject(projectID string) error
|
||||
ListInvitations(userID string, invitationState entities.Invitation) ([]*entities.Project, error)
|
||||
}
|
||||
|
||||
|
@ -36,8 +39,8 @@ func (a applicationService) GetProjects(query bson.D) ([]*entities.Project, erro
|
|||
return a.projectRepository.GetProjects(query)
|
||||
}
|
||||
|
||||
func (a applicationService) GetProjectsByUserID(uid string, isOwner bool) ([]*entities.Project, error) {
|
||||
return a.projectRepository.GetProjectsByUserID(uid, isOwner)
|
||||
func (a applicationService) GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error) {
|
||||
return a.projectRepository.GetProjectsByUserID(request)
|
||||
}
|
||||
|
||||
func (a applicationService) GetProjectStats() ([]*entities.ProjectStats, error) {
|
||||
|
@ -64,6 +67,10 @@ func (a applicationService) UpdateProjectName(projectID string, projectName stri
|
|||
return a.projectRepository.UpdateProjectName(projectID, projectName)
|
||||
}
|
||||
|
||||
func (a applicationService) UpdateMemberRole(projectID string, userID string, role *entities.MemberRole) error {
|
||||
return a.projectRepository.UpdateMemberRole(projectID, userID, role)
|
||||
}
|
||||
|
||||
func (a applicationService) GetAggregateProjects(pipeline mongo.Pipeline, opts *options.AggregateOptions) (*mongo.Cursor, error) {
|
||||
return a.projectRepository.GetAggregateProjects(pipeline, opts)
|
||||
}
|
||||
|
@ -82,6 +89,14 @@ func (a applicationService) GetProjectMembers(projectID string, state string) ([
|
|||
return a.projectRepository.GetProjectMembers(projectID, state)
|
||||
}
|
||||
|
||||
func (a applicationService) GetProjectOwners(projectID string) ([]*entities.Member, error) {
|
||||
return a.projectRepository.GetProjectOwners(projectID)
|
||||
}
|
||||
|
||||
func (a applicationService) ListInvitations(userID string, invitationState entities.Invitation) ([]*entities.Project, error) {
|
||||
return a.projectRepository.ListInvitations(userID, invitationState)
|
||||
}
|
||||
|
||||
func (a applicationService) DeleteProject(projectID string) error {
|
||||
return a.projectRepository.DeleteProject(projectID)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
type sessionService interface {
|
||||
RevokeToken(tokenString string) error
|
||||
ValidateToken(encodedToken string) (*jwt.Token, error)
|
||||
GetSignedJWT(user *entities.User) (string, error)
|
||||
GetSignedJWT(user *entities.User, jwtSecret string) (string, error)
|
||||
CreateApiToken(user *entities.User, request entities.ApiTokenInput) (string, error)
|
||||
GetApiTokensByUserID(userID string) ([]entities.ApiToken, error)
|
||||
DeleteApiToken(token string) error
|
||||
|
@ -58,12 +58,17 @@ func (a applicationService) parseToken(encodedToken string) (*jwt.Token, error)
|
|||
if _, isValid := token.Method.(*jwt.SigningMethodHMAC); !isValid {
|
||||
return nil, fmt.Errorf("invalid token %s", token.Header["alg"])
|
||||
}
|
||||
return []byte(utils.JwtSecret), nil
|
||||
salt, err := a.GetConfig("salt")
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, fmt.Errorf("couldn't fetch jwt secret %v", err)
|
||||
}
|
||||
return []byte(salt.Value), nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetSignedJWT generates the JWT Token for the user object
|
||||
func (a applicationService) GetSignedJWT(user *entities.User) (string, error) {
|
||||
func (a applicationService) GetSignedJWT(user *entities.User, jwtSecret string) (string, error) {
|
||||
token := jwt.New(jwt.SigningMethodHS512)
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
claims["uid"] = user.ID
|
||||
|
@ -71,7 +76,7 @@ func (a applicationService) GetSignedJWT(user *entities.User) (string, error) {
|
|||
claims["username"] = user.Username
|
||||
claims["exp"] = time.Now().Add(time.Minute * time.Duration(utils.JWTExpiryDuration)).Unix()
|
||||
|
||||
tokenString, err := token.SignedString([]byte(utils.JwtSecret))
|
||||
tokenString, err := token.SignedString([]byte(jwtSecret))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return "", err
|
||||
|
@ -90,7 +95,12 @@ func (a applicationService) CreateApiToken(user *entities.User, request entities
|
|||
claims["username"] = user.Username
|
||||
claims["exp"] = expiresAt
|
||||
|
||||
tokenString, err := token.SignedString([]byte(utils.JwtSecret))
|
||||
salt, err := a.GetConfig("salt")
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return "", fmt.Errorf("couldn't fetch jwt secret %v", err)
|
||||
}
|
||||
tokenString, err := token.SignedString([]byte(salt.Value))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return "", err
|
||||
|
|
|
@ -3,6 +3,8 @@ package services
|
|||
import (
|
||||
"context"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
)
|
||||
|
||||
|
@ -17,6 +19,7 @@ type userService interface {
|
|||
UpdatePassword(userPassword *entities.UserPassword, isAdminBeingReset bool) error
|
||||
CreateUser(user *entities.User) (*entities.User, error)
|
||||
UpdateUser(user *entities.UserDetails) error
|
||||
UpdateUserByQuery(filter bson.D, updateQuery bson.D) error
|
||||
IsAdministrator(user *entities.User) error
|
||||
UpdateUserState(ctx context.Context, username string, isDeactivate bool, deactivateTime int64) error
|
||||
InviteUsers(invitedUsers []string) (*[]entities.User, error)
|
||||
|
@ -67,6 +70,11 @@ func (a applicationService) UpdateUser(user *entities.UserDetails) error {
|
|||
return a.userRepository.UpdateUser(user)
|
||||
}
|
||||
|
||||
// UpdateUserByQuery updates user details in the database
|
||||
func (a applicationService) UpdateUserByQuery(filter bson.D, updateQuery bson.D) error {
|
||||
return a.userRepository.UpdateUserByQuery(filter, updateQuery)
|
||||
}
|
||||
|
||||
// IsAdministrator verifies if the passed user is an administrator
|
||||
func (a applicationService) IsAdministrator(user *entities.User) error {
|
||||
return a.userRepository.IsAdministrator(user)
|
||||
|
|
|
@ -23,7 +23,7 @@ func (r repository) CreateApiToken(apiToken *entities.ApiToken) error {
|
|||
|
||||
// GetApiTokensByUserID returns all the API tokens for a given user
|
||||
func (r repository) GetApiTokensByUserID(userID string) ([]entities.ApiToken, error) {
|
||||
var apiTokens []entities.ApiToken
|
||||
var apiTokens = []entities.ApiToken{}
|
||||
query := bson.D{
|
||||
{Key: "user_id", Value: userID},
|
||||
}
|
||||
|
|
|
@ -29,12 +29,10 @@ func (r repository) RevokeToken(token *entities.RevokedToken) error {
|
|||
func (r repository) IsTokenRevoked(encodedToken string) bool {
|
||||
var result = entities.RevokedToken{}
|
||||
err := r.Collection.FindOne(context.TODO(), bson.M{
|
||||
"token": encodedToken,
|
||||
"token": bson.M{"$eq": encodedToken},
|
||||
}).Decode(&result)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// NewRevokedTokenRepo creates a new instance of this repository
|
||||
|
|
|
@ -2,6 +2,8 @@ package user
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
|
@ -24,6 +26,7 @@ type Repository interface {
|
|||
UpdatePassword(userPassword *entities.UserPassword, isAdminBeingReset bool) error
|
||||
CreateUser(user *entities.User) (*entities.User, error)
|
||||
UpdateUser(user *entities.UserDetails) error
|
||||
UpdateUserByQuery(filter bson.D, updateQuery bson.D) error
|
||||
IsAdministrator(user *entities.User) error
|
||||
UpdateUserState(ctx context.Context, username string, isDeactivate bool, deactivateTime int64) error
|
||||
InviteUsers(invitedUsers []string) (*[]entities.User, error)
|
||||
|
@ -75,7 +78,7 @@ func (r repository) GetUser(uid string) (*entities.User, error) {
|
|||
|
||||
// GetUsers fetches all the users from the database
|
||||
func (r repository) GetUsers() (*[]entities.User, error) {
|
||||
var Users []entities.User
|
||||
var Users = []entities.User{}
|
||||
cursor, err := r.Collection.Find(context.Background(), bson.M{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -166,7 +169,7 @@ func (r repository) CheckPasswordHash(hash, password string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdatePassword helps to update the password of the user, it acts as a resetPassword when isAdminBeingReset is set to true
|
||||
// UpdatePassword helps to update the password of the user, it acts as a resetPassword when isAdminBeingReset is set to false
|
||||
func (r repository) UpdatePassword(userPassword *entities.UserPassword, isAdminBeingReset bool) error {
|
||||
var result = entities.User{}
|
||||
result.Username = userPassword.Username
|
||||
|
@ -176,21 +179,35 @@ func (r repository) UpdatePassword(userPassword *entities.UserPassword, isAdminB
|
|||
if findOneErr != nil {
|
||||
return findOneErr
|
||||
}
|
||||
newHashedPassword, err := bcrypt.GenerateFromPassword([]byte(userPassword.NewPassword), utils.PasswordEncryptionCost)
|
||||
|
||||
updateQuery := bson.M{"$set": bson.M{
|
||||
"password": string(newHashedPassword),
|
||||
}}
|
||||
|
||||
if isAdminBeingReset {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(userPassword.OldPassword))
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("invalid credentials")
|
||||
}
|
||||
// check if the new pwd is same as old pwd, if yes return err
|
||||
err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(userPassword.NewPassword))
|
||||
if err == nil {
|
||||
return fmt.Errorf("old and new passwords can't be same")
|
||||
}
|
||||
updateQuery = bson.M{"$set": bson.M{
|
||||
"password": string(newHashedPassword),
|
||||
"is_initial_login": false,
|
||||
}}
|
||||
}
|
||||
|
||||
newHashedPassword, err := bcrypt.GenerateFromPassword([]byte(userPassword.NewPassword), utils.PasswordEncryptionCost)
|
||||
_, err = r.Collection.UpdateOne(context.Background(), bson.M{"_id": result.ID}, bson.M{"$set": bson.M{
|
||||
"password": string(newHashedPassword),
|
||||
}})
|
||||
res, err := r.Collection.UpdateOne(context.Background(), bson.M{"username": result.Username}, updateQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if res.MatchedCount == 0 {
|
||||
return errors.New("could not find matching username in database")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -206,10 +223,20 @@ func (r repository) CreateUser(user *entities.User) (*entities.User, error) {
|
|||
return user.SanitizedUser(), nil
|
||||
}
|
||||
|
||||
// UpdateUserByQuery updates user details in the database
|
||||
func (r repository) UpdateUserByQuery(filter bson.D, updateQuery bson.D) error {
|
||||
_, err := r.Collection.UpdateOne(context.Background(), filter, updateQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUser updates user details in the database
|
||||
func (r repository) UpdateUser(user *entities.UserDetails) error {
|
||||
data, _ := toDoc(user)
|
||||
_, err := r.Collection.UpdateOne(context.Background(), bson.M{"_id": user.ID}, bson.M{"$set": data})
|
||||
_, err := r.Collection.UpdateMany(context.Background(), bson.M{"_id": user.ID}, bson.M{"$set": data})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
JwtSecret = os.Getenv("JWT_SECRET")
|
||||
AdminName = os.Getenv("ADMIN_USERNAME")
|
||||
AdminPassword = os.Getenv("ADMIN_PASSWORD")
|
||||
DBUrl = os.Getenv("DB_SERVER")
|
||||
|
@ -15,24 +18,29 @@ var (
|
|||
JWTExpiryDuration = getEnvAsInt("JWT_EXPIRY_MINS", 1440)
|
||||
OAuthJWTExpDuration = getEnvAsInt("OAUTH_JWT_EXP_MINS", 5)
|
||||
OAuthJwtSecret = os.Getenv("OAUTH_SECRET")
|
||||
StrictPasswordPolicy = getEnvAsBool("STRICT_PASSWORD_POLICY", false)
|
||||
DexEnabled = getEnvAsBool("DEX_ENABLED", false)
|
||||
DexCallBackURL = os.Getenv("DEX_OAUTH_CALLBACK_URL")
|
||||
DexClientID = os.Getenv("DEX_OAUTH_CLIENT_ID")
|
||||
DexClientSecret = os.Getenv("DEX_OAUTH_CLIENT_SECRET")
|
||||
DexOIDCIssuer = os.Getenv("OIDC_ISSUER")
|
||||
EnableInternalTls = getEnvAsBool("ENABLE_INTERNAL_TLS", false)
|
||||
TlsCertPath = os.Getenv("TLS_CERT_PATH")
|
||||
TlSKeyPath = os.Getenv("TLS_KEY_PATH")
|
||||
CaCertPath = os.Getenv("CA_CERT_TLS_PATH")
|
||||
RestPort = os.Getenv("REST_PORT")
|
||||
GrpcPort = os.Getenv("GRPC_PORT")
|
||||
DBName = "auth"
|
||||
Port = ":3000"
|
||||
GrpcPort = ":3030"
|
||||
UserCollection = "users"
|
||||
ProjectCollection = "project"
|
||||
AuthConfigCollection = "auth-config"
|
||||
RevokedTokenCollection = "revoked-token"
|
||||
ApiTokenCollection = "api-token"
|
||||
UsernameField = "username"
|
||||
ExpiresAtField = "expires_at"
|
||||
PasswordEncryptionCost = 15
|
||||
PasswordEncryptionCost = 8
|
||||
DefaultLitmusGqlGrpcEndpoint = "localhost"
|
||||
DefaultLitmusGqlGrpcPort = ":8000"
|
||||
//DefaultLitmusGqlGrpcPortHttps = ":8001" // enable when in use
|
||||
)
|
||||
|
||||
func getEnvAsInt(name string, defaultVal int) int {
|
||||
|
@ -50,3 +58,32 @@ func getEnvAsBool(name string, defaultVal bool) bool {
|
|||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
func GetTlsConfig() *tls.Config {
|
||||
|
||||
// read ca's cert, verify to client's certificate
|
||||
caPem, err := os.ReadFile(CaCertPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// create cert pool and append ca's cert
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(caPem) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// read server cert & key
|
||||
serverCert, err := tls.LoadX509KeyPair(TlsCertPath, TlSKeyPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// configuring TLS config based on provided certificates & keys to
|
||||
conf := &tls.Config{
|
||||
Certificates: []tls.Certificate{serverCert},
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
ClientCAs: certPool,
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ var (
|
|||
ErrServerError AppError = errors.New("server_error")
|
||||
ErrInvalidRequest AppError = errors.New("invalid_request")
|
||||
ErrStrictPasswordPolicyViolation AppError = errors.New("password_policy_violation")
|
||||
ErrStrictUsernamePolicyViolation AppError = errors.New("username_policy_violation")
|
||||
ErrUnauthorized AppError = errors.New("unauthorized")
|
||||
ErrUserExists AppError = errors.New("user_exists")
|
||||
ErrUserNotFound AppError = errors.New("user does not exist")
|
||||
|
@ -21,6 +22,8 @@ var (
|
|||
ErrEmptyProjectName AppError = errors.New("invalid project name")
|
||||
ErrInvalidRole AppError = errors.New("invalid role")
|
||||
ErrInvalidEmail AppError = errors.New("invalid email")
|
||||
ErrPasswordNotUpdated AppError = errors.New("default password not updated")
|
||||
ErrOldPassword AppError = errors.New("old and new passwords can't be same")
|
||||
)
|
||||
|
||||
// ErrorStatusCodes holds the http status codes for every AppError
|
||||
|
@ -31,6 +34,7 @@ var ErrorStatusCodes = map[AppError]int{
|
|||
ErrUnauthorized: 401,
|
||||
ErrUserExists: 401,
|
||||
ErrStrictPasswordPolicyViolation: 401,
|
||||
ErrStrictUsernamePolicyViolation: 401,
|
||||
ErrUserNotFound: 400,
|
||||
ErrProjectNotFound: 400,
|
||||
ErrUpdatingAdmin: 400,
|
||||
|
@ -39,6 +43,8 @@ var ErrorStatusCodes = map[AppError]int{
|
|||
ErrEmptyProjectName: 400,
|
||||
ErrInvalidRole: 400,
|
||||
ErrInvalidEmail: 400,
|
||||
ErrPasswordNotUpdated: 401,
|
||||
ErrOldPassword: 400,
|
||||
}
|
||||
|
||||
// ErrorDescriptions holds detailed error description for every AppError
|
||||
|
@ -48,9 +54,12 @@ var ErrorDescriptions = map[AppError]string{
|
|||
ErrInvalidRequest: "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed",
|
||||
ErrUnauthorized: "The user does not have requested authorization to access this resource",
|
||||
ErrUserExists: "This username is already assigned to another user",
|
||||
ErrStrictPasswordPolicyViolation: "Please ensure the password is 8 characters long and has 1 digit, 1 lowercase alphabet, 1 uppercase alphabet and 1 special character",
|
||||
ErrStrictPasswordPolicyViolation: "Please ensure the password is atleast 8 characters long and atmost 16 characters long and has atleast 1 digit, 1 lowercase alphabet, 1 uppercase alphabet and 1 special character",
|
||||
ErrStrictUsernamePolicyViolation: "The username should be atleast 3 characters long and atmost 16 characters long.",
|
||||
ErrEmptyProjectName: "Project name can't be empty",
|
||||
ErrInvalidRole: "Role is invalid",
|
||||
ErrProjectNotFound: "This project does not exist",
|
||||
ErrInvalidEmail: "Email address is invalid",
|
||||
ErrPasswordNotUpdated: "Please update your default password",
|
||||
ErrOldPassword: "old and new passwords can't be same",
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
crypto "crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
@ -13,20 +15,25 @@ func SanitizeString(input string) string {
|
|||
|
||||
/*
|
||||
ValidateStrictPassword represents and checks for the following patterns:
|
||||
- Input is at least 8 characters long
|
||||
- Input contains at least one special character
|
||||
- Input is at least 8 characters long and at most 16 characters long
|
||||
- Input contains at least one special character of these @$!%*?_&#
|
||||
- Input contains at least one digit
|
||||
- Input contains at least one uppercase alphabet
|
||||
- Input contains at least one lowercase alphabet
|
||||
*/
|
||||
func ValidateStrictPassword(input string) error {
|
||||
if len(input) < 8 {
|
||||
return fmt.Errorf("password is less than 8 characters")
|
||||
return fmt.Errorf("password length is less than 8 characters")
|
||||
}
|
||||
|
||||
if len(input) > 16 {
|
||||
return fmt.Errorf("password length is more than 16 characters")
|
||||
}
|
||||
|
||||
digits := `[0-9]{1}`
|
||||
lowerAlphabets := `[a-z]{1}`
|
||||
capitalAlphabets := `[A-Z]{1}`
|
||||
specialCharacters := `[!@#~$%^&*()+|_]{1}`
|
||||
specialCharacters := `[@$!%*?_&#]{1}`
|
||||
if b, err := regexp.MatchString(digits, input); !b || err != nil {
|
||||
return fmt.Errorf("password does not contain digits")
|
||||
}
|
||||
|
@ -41,3 +48,30 @@ func ValidateStrictPassword(input string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RandomString generates random strings, can be used to create ids
|
||||
func RandomString(n int) (string, error) {
|
||||
if n > 0 {
|
||||
b := make([]byte, n)
|
||||
_, err := crypto.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.URLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
return "", fmt.Errorf("length should be greater than 0")
|
||||
}
|
||||
|
||||
// Username must start with a letter - ^[a-zA-Z]
|
||||
// Allow letters, digits, underscores, and hyphens - [a-zA-Z0-9_-]
|
||||
// Ensure the length of the username is between 3 and 16 characters (1 character is already matched above) - {2,15}$
|
||||
|
||||
func ValidateStrictUsername(username string) error {
|
||||
// Ensure username doesn't contain special characters (only letters, numbers, and underscores are allowed)
|
||||
if matched, _ := regexp.MatchString(`^[a-zA-Z][a-zA-Z0-9_-]{2,15}$`, username); !matched {
|
||||
return fmt.Errorf("username can only contain letters, numbers, and underscores")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func FuzzSanitizeString(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, input string) {
|
||||
err := ValidateStrictPassword(input)
|
||||
if err != nil {
|
||||
if isExpectedError(err) {
|
||||
fmt.Printf("Expected validation failure for input '%s': %v\n", input, err)
|
||||
} else {
|
||||
fmt.Printf("Unexpected validation failure for input '%s': %v\n", input, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func isExpectedError(err error) bool {
|
||||
expectedErrors := []string{
|
||||
"password is less than 8 characters",
|
||||
"password does not contain digits",
|
||||
"password does not contain lowercase alphabets",
|
||||
"password does not contain uppercase alphabets",
|
||||
"password does not contain special characters",
|
||||
}
|
||||
|
||||
for _, expected := range expectedErrors {
|
||||
if err.Error() == expected {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -4,11 +4,13 @@ import "github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
|||
|
||||
var MutationRbacRules = map[string][]string{
|
||||
"sendInvitation": {string(entities.RoleOwner)},
|
||||
"acceptInvitation": {string(entities.RoleViewer), string(entities.RoleEditor)},
|
||||
"declineInvitation": {string(entities.RoleViewer),
|
||||
string(entities.RoleEditor)},
|
||||
"acceptInvitation": {string(entities.RoleOwner), string(entities.RoleViewer), string(entities.RoleExecutor)},
|
||||
"declineInvitation": {string(entities.RoleOwner), string(entities.RoleViewer),
|
||||
string(entities.RoleExecutor)},
|
||||
"removeInvitation": {string(entities.RoleOwner)},
|
||||
"leaveProject": {string(entities.RoleViewer), string(entities.RoleEditor)},
|
||||
"leaveProject": {string(entities.RoleOwner), string(entities.RoleViewer), string(entities.RoleExecutor)},
|
||||
"updateProjectName": {string(entities.RoleOwner)},
|
||||
"getProject": {string(entities.RoleOwner), string(entities.RoleViewer), string(entities.RoleEditor)},
|
||||
"updateMemberRole": {string(entities.RoleOwner)},
|
||||
"deleteProject": {string(entities.RoleOwner)},
|
||||
"getProject": {string(entities.RoleOwner), string(entities.RoleViewer), string(entities.RoleExecutor)},
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
subjectAltName=DNS:*.someclient.com,IP:0.0.0.0,DNS:localhost,DNS:litmusportal-auth-server-service,DNS:litmusportal-server-service
|
|
@ -1,3 +1,3 @@
|
|||
FROM ghcr.io/dexidp/dex:latest
|
||||
FROM ghcr.io/dexidp/dex:v2.43.0
|
||||
ENV DEX_FRONTEND_DIR=/srv/dex/web
|
||||
COPY --chown=root:root web /srv/dex/web
|
||||
|
|
|
@ -69,6 +69,15 @@ rules:
|
|||
signingkeies,
|
||||
]
|
||||
verbs: [delete, deletecollection, get, list, patch, create, update, watch]
|
||||
- apiGroups: [apiextensions.k8s.io]
|
||||
resources: [customresourcedefinitions]
|
||||
verbs: [list, get, create]
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: dex-server-account
|
||||
namespace: litmus
|
||||
---
|
||||
# ClusterRoleBinding for DexServer
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
@ -81,7 +90,7 @@ roleRef:
|
|||
name: litmus-dex-server
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: litmus-server-account
|
||||
name: dex-server-account
|
||||
namespace: litmus
|
||||
---
|
||||
# Deployment for DexServer
|
||||
|
@ -102,7 +111,7 @@ spec:
|
|||
labels:
|
||||
component: litmusportal-dex-server
|
||||
spec:
|
||||
serviceAccountName: litmus-server-account
|
||||
serviceAccountName: dex-server-account
|
||||
containers:
|
||||
- name: litmus-dex
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
//go:embed static/* templates/* themes/*
|
||||
var files embed.FS
|
||||
|
||||
// FS returns a filesystem with the default web assets.
|
||||
// FS returns a filesystem with the default web assets
|
||||
func FS() fs.FS {
|
||||
return files
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# BUILD STAGE
|
||||
FROM golang:1.20 AS builder
|
||||
FROM golang:1.22 AS builder
|
||||
|
||||
LABEL maintainer="LitmusChaos"
|
||||
|
||||
|
@ -16,12 +16,19 @@ RUN go env
|
|||
RUN CGO_ENABLED=0 go build -o /output/event-tracker -v
|
||||
|
||||
# Packaging stage
|
||||
# Image source: https://github.com/litmuschaos/test-tools/blob/master/custom/hardened-alpine/infra/Dockerfile
|
||||
# The base image is non-root (have litmus user) with default litmus directory.
|
||||
FROM litmuschaos/infra-alpine
|
||||
# Use RedHat UBI minimal image as base
|
||||
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5
|
||||
|
||||
LABEL maintainer="LitmusChaos"
|
||||
|
||||
COPY --from=builder /output/event-tracker /litmus
|
||||
ENV APP_DIR="/litmus"
|
||||
|
||||
CMD ["./event-tracker"]
|
||||
COPY --from=builder /output/event-tracker $APP_DIR/
|
||||
RUN chown 65534:0 $APP_DIR/event-tracker && chmod 755 $APP_DIR/event-tracker
|
||||
|
||||
WORKDIR $APP_DIR
|
||||
USER 65534
|
||||
|
||||
CMD ["./event-tracker"]
|
||||
|
||||
EXPOSE 8080
|
|
@ -1,13 +1,13 @@
|
|||
module github.com/litmuschaos/litmus/chaoscenter/event-tracker
|
||||
|
||||
go 1.20
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/jmespath/go-jmespath v0.4.0
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.15.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
k8s.io/api v0.22.1
|
||||
k8s.io/apimachinery v0.22.1
|
||||
k8s.io/client-go v0.22.1
|
||||
|
@ -32,7 +32,7 @@ require (
|
|||
github.com/go-logr/zapr v0.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
|
@ -44,7 +44,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_golang v1.11.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
|
@ -52,20 +52,20 @@ require (
|
|||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 // indirect
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20220521103104-8f96da9f5d5e // indirect
|
||||
k8s.io/apiextensions-apiserver v0.22.1 // indirect
|
||||
k8s.io/component-base v0.22.1 // indirect
|
||||
k8s.io/klog/v2 v2.9.0 // indirect
|
||||
|
|
|
@ -176,8 +176,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
|
@ -315,8 +316,9 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
|
@ -338,8 +340,9 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
|||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
|
@ -369,8 +372,9 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
|||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
|
@ -449,8 +453,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -517,8 +521,8 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -580,20 +584,24 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo=
|
||||
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -640,8 +648,9 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -713,8 +722,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -743,8 +753,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20220521103104-8f96da9f5d5e h1:3i3ny04XV6HbZ2N1oIBw1UBYATHAOpo4tfTF83JM3Z0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20220521103104-8f96da9f5d5e/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -111,9 +111,12 @@ func main() {
|
|||
if err != nil {
|
||||
logrus.Fatalf("failed to parse custom tls cert %v", err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(cert)
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{RootCAs: caCertPool}
|
||||
rootCerts, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
logrus.Fatalf("Failed to read system cert pool %v", err)
|
||||
}
|
||||
rootCerts.AppendCertsFromPEM(cert)
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{RootCAs: rootCerts}
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash -eu
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
export GO_MOD_PATHS_MAPPING=( "graphql/server" "authentication" "subscriber" )
|
||||
|
||||
cd chaoscenter
|
||||
export rootDir=$(pwd)
|
||||
|
||||
for dir in "${GO_MOD_PATHS_MAPPING[@]}"; do
|
||||
cd ${dir} && go mod download
|
||||
go install github.com/AdamKorcz/go-118-fuzz-build@latest
|
||||
go get github.com/AdamKorcz/go-118-fuzz-build/testing
|
||||
fuzz_files=($(find "$(pwd)" -type f -name '*_fuzz_test.go'))
|
||||
for file in "${fuzz_files[@]}"; do
|
||||
pkg=$(grep -m 1 '^package' "$file" | awk '{print $2}')
|
||||
package_path=$(dirname "${file%$pkg}")
|
||||
functionList=($(grep -o 'func Fuzz[A-Za-z0-9_]*' ${file} | awk '{print $2}'))
|
||||
for i in "${functionList[@]}"
|
||||
do
|
||||
compile_native_go_fuzzer ${package_path} ${i} ${i}
|
||||
done
|
||||
done
|
||||
cd ${rootDir}
|
||||
done
|
|
@ -29,6 +29,8 @@ enum ExperimentRunStatus {
|
|||
Skipped
|
||||
Error
|
||||
Timeout
|
||||
Terminated
|
||||
Queued
|
||||
NA
|
||||
}
|
||||
|
||||
|
@ -713,7 +715,7 @@ type GetExperimentStatsResponse {
|
|||
totalExpCategorizedByResiliencyScore: [ResilienceScoreCategory]!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
type Query {
|
||||
|
||||
|
||||
"""
|
||||
|
@ -735,7 +737,7 @@ extend type Query {
|
|||
getExperimentStats(projectID: ID!): GetExperimentStatsResponse!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
type Mutation {
|
||||
"""
|
||||
Creates a new experiment and applies its manifest
|
||||
"""
|
||||
|
@ -756,7 +758,7 @@ extend type Mutation {
|
|||
Updates the experiment
|
||||
"""
|
||||
updateChaosExperiment(
|
||||
request: ChaosExperimentRequest
|
||||
request: ChaosExperimentRequest!
|
||||
projectID: ID!
|
||||
): ChaosExperimentResponse!
|
||||
|
||||
|
@ -768,4 +770,12 @@ extend type Mutation {
|
|||
experimentRunID: String
|
||||
projectID: ID!
|
||||
): Boolean!
|
||||
|
||||
"""
|
||||
Enable/Disable cron experiment state
|
||||
"""
|
||||
updateCronExperimentState(experimentID: String!
|
||||
disable: Boolean!
|
||||
projectID: ID!
|
||||
): Boolean! @authorized
|
||||
}
|
||||
|
|
|
@ -32,4 +32,9 @@ extend type Mutation {
|
|||
experimentID: String!
|
||||
projectID: ID!
|
||||
): RunChaosExperimentResponse!
|
||||
|
||||
"""
|
||||
stopExperiment will halt all the ongoing runs of a particular experiment
|
||||
"""
|
||||
stopExperimentRuns(projectID: ID!, experimentID:String!, experimentRunID: String, notifyID: String): Boolean! @authorized
|
||||
}
|
|
@ -358,11 +358,11 @@ type KubeObjectResponse {
|
|||
"""
|
||||
Type of the Kubernetes object
|
||||
"""
|
||||
kubeObj: [KubeObject]!
|
||||
kubeObj: KubeObject!
|
||||
}
|
||||
|
||||
"""
|
||||
KubeObject consists of the namespace and the available resources in the same
|
||||
KubeObject consists of the available resources in a namespace
|
||||
"""
|
||||
type KubeObject {
|
||||
"""
|
||||
|
@ -404,16 +404,75 @@ input KubeObjectRequest {
|
|||
GVR Request
|
||||
"""
|
||||
kubeObjRequest: KubeGVRRequest
|
||||
"""
|
||||
Namespace in which the Kubernetes object is present
|
||||
"""
|
||||
namespace: String!
|
||||
objectType: String!
|
||||
workloads: [Workload]
|
||||
}
|
||||
|
||||
"""
|
||||
Defines details for fetching Kubernetes namespace data
|
||||
"""
|
||||
input KubeNamespaceRequest {
|
||||
"""
|
||||
ID of the infra
|
||||
"""
|
||||
infraID: ID!
|
||||
}
|
||||
|
||||
"""
|
||||
Define name in the infra (not really useful at the moment but maybe we will need other field later)
|
||||
"""
|
||||
type KubeNamespace{
|
||||
"""
|
||||
Name of the namespace
|
||||
"""
|
||||
name: String!
|
||||
}
|
||||
|
||||
|
||||
|
||||
input KubeGVRRequest {
|
||||
group: String!
|
||||
version: String!
|
||||
resource: String!
|
||||
}
|
||||
|
||||
"""
|
||||
Response received for querying Kubernetes Namespaces
|
||||
"""
|
||||
type KubeNamespaceResponse {
|
||||
"""
|
||||
ID of the infra in which the Kubernetes namespace is present
|
||||
"""
|
||||
infraID: ID!
|
||||
"""
|
||||
List of the Kubernetes namespace
|
||||
"""
|
||||
kubeNamespace: [KubeNamespace]!
|
||||
}
|
||||
|
||||
"""
|
||||
Defines the details of Kubernetes namespace
|
||||
"""
|
||||
input KubeNamespaceData {
|
||||
"""
|
||||
Unique request ID for fetching Kubernetes namespace details
|
||||
"""
|
||||
requestID: ID!
|
||||
"""
|
||||
ID of the infra in which the Kubernetes namespace is present
|
||||
"""
|
||||
infraID: InfraIdentity!
|
||||
"""
|
||||
List of KubeNamespace return by subscriber
|
||||
"""
|
||||
kubeNamespace: String!
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
Defines the details of Kubernetes object
|
||||
"""
|
||||
|
@ -638,9 +697,15 @@ extend type Mutation {
|
|||
"""
|
||||
# authorized directive not required
|
||||
kubeObj(request: KubeObjectData!): String!
|
||||
|
||||
"""
|
||||
Receives kubernetes namespace data from subscriber
|
||||
"""
|
||||
# authorized directive not required
|
||||
kubeNamespace(request: KubeNamespaceData!): String!
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
type Subscription {
|
||||
# INFRA OPERATIONS
|
||||
"""
|
||||
Listens infra events from the graphql server
|
||||
|
@ -663,4 +728,9 @@ extend type Subscription {
|
|||
Returns a kubernetes object given an input
|
||||
"""
|
||||
getKubeObject(request: KubeObjectRequest!): KubeObjectResponse!
|
||||
|
||||
"""
|
||||
Returns a kubernetes namespaces given an input
|
||||
"""
|
||||
getKubeNamespace(request: KubeNamespaceRequest!): KubeNamespaceResponse!
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ type ChaosHub implements ResourceDetails & Audit {
|
|||
"""
|
||||
repoBranch: String!
|
||||
"""
|
||||
Connected Hub of remote repository
|
||||
"""
|
||||
remoteHub: String!
|
||||
"""
|
||||
ID of the project in which the chaos hub is present
|
||||
"""
|
||||
projectID: ID!
|
||||
|
@ -206,6 +210,10 @@ type ChaosHubStatus implements ResourceDetails & Audit {
|
|||
"""
|
||||
repoBranch: String!
|
||||
"""
|
||||
Connected Hub of remote repository
|
||||
"""
|
||||
remoteHub: String!
|
||||
"""
|
||||
Bool value indicating whether the hub is available or not.
|
||||
"""
|
||||
isAvailable: Boolean!
|
||||
|
@ -320,6 +328,10 @@ input CreateChaosHubRequest {
|
|||
"""
|
||||
repoBranch: String!
|
||||
"""
|
||||
Connected Hub of remote repository
|
||||
"""
|
||||
remoteHub: String!
|
||||
"""
|
||||
Bool value indicating whether the hub is private or not.
|
||||
"""
|
||||
isPrivate: Boolean!
|
||||
|
@ -382,6 +394,10 @@ input CloningInput {
|
|||
"""
|
||||
repoURL: String!
|
||||
"""
|
||||
Connected Hub of remote repository
|
||||
"""
|
||||
remoteHub: String!
|
||||
"""
|
||||
Bool value indicating whether the hub is private or not.
|
||||
"""
|
||||
isPrivate: Boolean!
|
||||
|
@ -426,6 +442,10 @@ input CreateRemoteChaosHub {
|
|||
URL of the git repository
|
||||
"""
|
||||
repoURL: String!
|
||||
"""
|
||||
Connected Hub of remote repository
|
||||
"""
|
||||
remoteHub: String!
|
||||
}
|
||||
|
||||
|
||||
|
@ -455,6 +475,10 @@ input UpdateChaosHubRequest {
|
|||
"""
|
||||
repoBranch: String!
|
||||
"""
|
||||
Connected Hub of remote repository
|
||||
"""
|
||||
remoteHub: String!
|
||||
"""
|
||||
Bool value indicating whether the hub is private or not.
|
||||
"""
|
||||
isPrivate: Boolean!
|
||||
|
|
|
@ -50,11 +50,11 @@ input EnvironmentFilterInput {
|
|||
"""
|
||||
Type name of environment
|
||||
"""
|
||||
type: String
|
||||
type: EnvironmentType
|
||||
"""
|
||||
Tags of an environment
|
||||
"""
|
||||
tags: [EnvironmentType]
|
||||
tags: [String!]
|
||||
}
|
||||
enum EnvironmentSortingField {
|
||||
NAME
|
||||
|
|
|
@ -5,6 +5,6 @@ enum Invitation {
|
|||
|
||||
enum MemberRole {
|
||||
Owner
|
||||
Editor
|
||||
Executor
|
||||
Viewer
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
with-expecter: true
|
||||
packages:
|
||||
github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/chaos_experiment/ops:
|
||||
interfaces:
|
||||
Service:
|
||||
# Modify package-level config for this specific interface (if applicable)
|
||||
config:
|
||||
dir: "pkg/chaos_experiment/model/mocks"
|
||||
filename: "service.go"
|
||||
mockname: ChaosExperimentService
|
||||
outpkg: mocks
|
||||
github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/chaos_experiment_run:
|
||||
interfaces:
|
||||
Service:
|
||||
# Modify package-level config for this specific interface (if applicable)
|
||||
config:
|
||||
dir: "pkg/chaos_experiment_run/model/mocks"
|
||||
filename: "service.go"
|
||||
mockname: ChaosExperimentRunService
|
||||
outpkg: mocks
|
|
@ -1,5 +1,5 @@
|
|||
# BUILD STAGE
|
||||
FROM golang:1.20 AS builder
|
||||
FROM golang:1.22 AS builder
|
||||
|
||||
LABEL maintainer="LitmusChaos"
|
||||
|
||||
|
@ -11,21 +11,27 @@ WORKDIR /gql-server
|
|||
|
||||
ENV GOOS=${TARGETOS} \
|
||||
GOARCH=${TARGETARCH}
|
||||
|
||||
|
||||
RUN go env
|
||||
RUN CGO_ENABLED=0 go build -o /output/server -v
|
||||
|
||||
## DEPLOY STAGE
|
||||
# Image source: https://github.com/litmuschaos/test-tools/blob/master/custom/hardened-alpine/infra/Dockerfile
|
||||
# The base image is non-root (have litmus user) with default litmus directory.
|
||||
FROM litmuschaos/infra-alpine
|
||||
# DEPLOY STAGE
|
||||
# Use Red Hat UBI minimal image as base
|
||||
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5
|
||||
|
||||
LABEL maintainer="LitmusChaos"
|
||||
|
||||
COPY --from=builder /output/server /litmus
|
||||
# Copy manifests and change the group to 0(root) to make it Openshift friendly
|
||||
# as Openshift runs container with an arbitrary uid that in the root group
|
||||
COPY --chown=litmus:0 --chmod=750 ./manifests/. /litmus/manifests
|
||||
ENV APP_DIR="/litmus"
|
||||
|
||||
COPY --from=builder /output/server $APP_DIR/
|
||||
COPY ./manifests/. $APP_DIR/manifests
|
||||
|
||||
RUN chown -R 65534:0 $APP_DIR/manifests && chmod -R 755 $APP_DIR/manifests
|
||||
RUN chown 65534:0 $APP_DIR/server && chmod 755 $APP_DIR/server
|
||||
|
||||
WORKDIR $APP_DIR
|
||||
USER 65534
|
||||
|
||||
CMD ["./server"]
|
||||
|
||||
EXPOSE 8080
|
||||
EXPOSE 8080
|
|
@ -0,0 +1,58 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
AllowedOrigin string = "Access-Control-Allow-Origin"
|
||||
AllowedMethods string = "Access-Control-Allow-Methods"
|
||||
AllowedHeaders string = "Access-Control-Allow-Headers"
|
||||
AllowedCredentials string = "Access-Control-Allow-Credentials"
|
||||
)
|
||||
|
||||
func ValidateCors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
allowedOrigins := utils.Config.AllowedOrigins
|
||||
origin := c.GetHeader("Origin")
|
||||
if origin == "" {
|
||||
origin = c.Request.Host
|
||||
}
|
||||
|
||||
validOrigin := false
|
||||
|
||||
for _, allowedOrigin := range allowedOrigins {
|
||||
match, err := regexp.MatchString(allowedOrigin, origin)
|
||||
if err == nil && match {
|
||||
validOrigin = true
|
||||
c.Writer.Header().Set(AllowedOrigin, origin)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validOrigin {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Invalid origin",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set(AllowedMethods, strings.Join([]string{
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"OPTIONS",
|
||||
}, ","))
|
||||
c.Writer.Header().Set(AllowedHeaders, "*")
|
||||
c.Writer.Header().Set(AllowedCredentials, "true")
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -1,121 +1,125 @@
|
|||
module github.com/litmuschaos/litmus/chaoscenter/graphql/server
|
||||
|
||||
go 1.20
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/99designs/gqlgen v0.11.3
|
||||
github.com/argoproj/argo-workflows/v3 v3.3.1
|
||||
github.com/99designs/gqlgen v0.17.49
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24
|
||||
github.com/argoproj/argo-workflows/v3 v3.3.5
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/gin-contrib/cors v1.3.1
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-git/go-git/v5 v5.13.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jinzhu/copier v0.3.5
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/litmuschaos/chaos-operator v0.0.0-20230718113617-6819a4be12e4
|
||||
github.com/litmuschaos/chaos-operator v0.0.0-20240601063404-e96a7ee7f1f7
|
||||
github.com/litmuschaos/chaos-scheduler v0.0.0-20220714173615-d7513d616a71
|
||||
github.com/mrz1836/go-sanitize v1.3.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/tidwall/gjson v1.14.0
|
||||
github.com/tidwall/sjson v1.2.4
|
||||
github.com/vektah/gqlparser/v2 v2.1.0
|
||||
go.mongodb.org/mongo-driver v1.11.4
|
||||
golang.org/x/crypto v0.5.0
|
||||
google.golang.org/grpc v1.44.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tidwall/gjson v1.17.3
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
github.com/vektah/gqlparser/v2 v2.5.16
|
||||
go.mongodb.org/mongo-driver v1.16.1
|
||||
golang.org/x/crypto v0.31.0
|
||||
google.golang.org/grpc v1.64.1
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.26.0
|
||||
k8s.io/apimachinery v0.26.0
|
||||
k8s.io/client-go v12.0.0+incompatible
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.3 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/agnivade/levenshtein v1.0.3 // indirect
|
||||
github.com/bytedance/sonic v1.8.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful v2.15.0+incompatible // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/emicklei/go-restful v2.16.0+incompatible // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||
github.com/goccy/go-json v0.10.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.14.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/sosodev/duration v1.3.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
github.com/urfave/cli/v2 v2.1.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.1 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/client-go v12.0.0+incompatible // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
|
||||
sigs.k8s.io/controller-runtime v0.11.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
// Pinned to kubernetes-1.21.2
|
||||
|
@ -144,3 +148,7 @@ replace (
|
|||
)
|
||||
|
||||
replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm
|
||||
|
||||
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20190924102528-32369d4db2ad // Required until https://github.com/operator-framework/operator-lifecycle-manager/pull/1241 is resolved
|
||||
|
||||
replace vbom.ml/util => github.com/fvbommel/util v0.0.0-20160121211510-db5cfe13f5cc
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue