Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a944137
Bump the typescript-eslint group with 2 updates
dependabot[bot] Nov 18, 2024
3c3a101
Merge pull request #445 from shapehq/dependabot/npm_and_yarn/typescri…
simonbs Nov 18, 2024
90ef3dd
Bump @octokit/auth-app from 7.1.2 to 7.1.3 in the octokit group
dependabot[bot] Nov 18, 2024
a334a48
Merge pull request #443 from shapehq/dependabot/npm_and_yarn/octokit-…
simonbs Nov 18, 2024
089947e
Bump core-js from 3.38.1 to 3.39.0
dependabot[bot] Nov 18, 2024
3f7aed1
Merge pull request #446 from shapehq/dependabot/npm_and_yarn/core-js-…
simonbs Nov 18, 2024
918d36d
Bump the mui group with 2 updates
dependabot[bot] Nov 18, 2024
60cb3ca
Merge pull request #444 from shapehq/dependabot/npm_and_yarn/mui-87f8…
simonbs Nov 18, 2024
6862e49
Bump the fontawesome group with 4 updates
dependabot[bot] Nov 25, 2024
30526dd
Bump the mui group with 2 updates
dependabot[bot] Nov 25, 2024
ae4376e
Bump the typescript-eslint group with 2 updates
dependabot[bot] Nov 25, 2024
baa3b5b
Merge pull request #450 from shapehq/dependabot/npm_and_yarn/typescri…
simonbs Dec 3, 2024
9626e30
Wraps links in Swagger
simonbs Dec 4, 2024
db043d3
Merge pull request #452 from shapehq/enhancement/wrap-urls
ulrikandersen Dec 4, 2024
803cf75
Merge branch 'develop' into dependabot/npm_and_yarn/mui-593ac69e91
simonbs Dec 5, 2024
6c6a477
Merge pull request #449 from shapehq/dependabot/npm_and_yarn/mui-593a…
simonbs Dec 5, 2024
86495ad
Merge branch 'develop' into dependabot/npm_and_yarn/fontawesome-c73e5…
simonbs Dec 5, 2024
f368a66
Merge pull request #447 from shapehq/dependabot/npm_and_yarn/fontawes…
simonbs Dec 5, 2024
ec4af52
Basic auth via config file (#451)
ulrikandersen Dec 5, 2024
67c466c
In case we fail to decrypt auth info is omitted
ulrikandersen Dec 5, 2024
96a807e
Merge pull request #453 from shapehq/fix/catch-decrypt-error
ulrikandersen Dec 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ GITHUB_CLIENT_ID=GitHub App client ID
GITHUB_CLIENT_SECRET=GitHub App client secret
GITHUB_APP_ID=123456
GITHUB_PRIVATE_KEY_BASE_64=base 64 encoded version of the private key - see README.md for more info
ENCRYPTION_PUBLIC_KEY_BASE_64=base 64 encoded version of the public key
ENCRYPTION_PRIVATE_KEY_BASE_64=base 64 encoded version of the private key
80 changes: 80 additions & 0 deletions __test__/encrypt/EncryptionService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import RsaEncryptionService from '../../src/features/encrypt/EncryptionService'

const publicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1k4JT719AUz/wuXb2rt
8933okfM2Iynmc6akSsZWEsW19byzO0UHp8b79xvsmNQKM1wBEBnXb5t+uLjJJZe
rqCiTB7fBL64tExSKIDIRAlMnQtMfHs/rMgR+o/N2Yo2KimQw9G84goCEbBF2kbw
5/MQfe43HeEoVWbNfgmRyP8VudO1UtVr07dGoUEWvFjudtd/h5H9THVdEpp2vH2Z
pSGypn8hRAbOzhIM4ExLOH4ZHb8gPQGiHRGUYXk3Cy95RSf/SpEnRi0p4/63Nx5M
JNXGM2Jk0RgGcYZcwJvLanT5Xdb9LM/IsDxLKXN+utDUgkzddvJbBC12aLaKaJA5
LwIDAQAB
-----END PUBLIC KEY-----`

const privateKey = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7WTglPvX0BTP/
C5dvau3z3feiR8zYjKeZzpqRKxlYSxbX1vLM7RQenxvv3G+yY1AozXAEQGddvm36
4uMkll6uoKJMHt8Evri0TFIogMhECUydC0x8ez+syBH6j83ZijYqKZDD0bziCgIR
sEXaRvDn8xB97jcd4ShVZs1+CZHI/xW507VS1WvTt0ahQRa8WO5213+Hkf1MdV0S
mna8fZmlIbKmfyFEBs7OEgzgTEs4fhkdvyA9AaIdEZRheTcLL3lFJ/9KkSdGLSnj
/rc3Hkwk1cYzYmTRGAZxhlzAm8tqdPld1v0sz8iwPEspc3660NSCTN128lsELXZo
topokDkvAgMBAAECggEAAWQMl0laQ8OZfiqWY72Ry0oYPgFvFO1PpkQHObm3+S+d
8Q81IgXNLNtWKSA4VpXYQ4zcJUpADmg1ZdxAfszUB4kcshHdpz4Z9Y849i6KW4l4
qZsP3hbQWtTbgYWG71+M+y2sqJu0hgCkLPmm31AsJDG6zPtEKokKbYH7jWV0Xo5z
0g6IUqepc1ElNzsJAU10hgX5UZUPxvzbWHxhBhFzC51GKpfx/W5ZOQtB+W8+nlmC
OSVlZ9pfr6qxOZbSLWESU1xplywPTPLoYs/38oN5OHIJvB2j8kl+JfcR7v2ezLeV
fx1Z+x9ME0at7AbGCfhjIfJtftPsoCR60nzN3wWoAQKBgQDfOmfzLaWhVkvt49Hn
zeLdLI8pwqWXVYozsPMRlExwuIT1KeNolPzWWKx6dG38UzY4XWSvq+w3WAcQ7m6E
qiRWoRPL3qlWu3pDJYr/EfR2haPMQMwbJM/hg+nC0bhUSVqBEjOZgaQUHStIyugb
SWQFI3jE9fgj71DtbiVNrb1vAQKBgQDW2ljkotAjF81vI+EoN9QmuPYnejo42nK9
jlSEU4hrDQMLiqxc5yJidQh75vZRfaO9rdUqHxoXK0DEU3Jk16Kb0n4nkM+xqKoc
yHTtAgUyflpenbrr4pRZf783XgI0bn/FhoMFQtAvSblru3NfEFQUtKIY82+Xa5H5
g+cezSDYLwKBgBeViB39GJ6vC16azzZ6XhmX95gl5HDUrMFBVKzqyhiupf1w64HF
G+FZhP97BZO/Bt91nomg1FgUiMqVJkAF6cjtQ7YqVCHBtO0bLlA8iWNsQx31Spsj
jIL6+NuIZL0i8tjoH2N8euVVH5mVNmiLnHGeicflZM4HHrm3BWHrlTQBAoGBALeW
W98CQFe8Pw542ixDiESOR8fz6UwrXWAb/pwTxL20oKV8GUxJNFhtKJK3CEMZ2JB7
uWoEqYairvUTWOxSVeBQPPwSAWcNeE6f+0mKMGa1EQNIRDDLq3fOcNYevkOPKB7g
kZQtQzclCAvGYQ8aJL6MmvY3DWOVx2YuD4+COE6BAoGAEGdChfJW5QGXaXEO/PnA
PbQCCzcqbs+0O6LVR1w68H0WQww94tZjfWPqn9kvwjzLd22ZMmdiBJ3bEbDeCjmG
Ybt48kS7y9n22CDgL7JkatszYpybvBSrDQL7ms7x2kKPkTMb7C5zpIIzdtvwH+Jf
6K3kQbqfFCM7VmyR7AmoyOk=
-----END PRIVATE KEY-----`

const encryptionService = new RsaEncryptionService({ publicKey, privateKey })

describe('RsaEncryptionService', () => {
it('should encrypt and decrypt data correctly', () => {
const data = 'Hello, World!'
const encryptedData = encryptionService.encrypt(data)
const decryptedData = encryptionService.decrypt(encryptedData)

expect(decryptedData).toBe(data)
})

it('should throw an error when decrypting with incorrect data', () => {
const incorrectData = 'invalidEncryptedData'

expect(() => {
encryptionService.decrypt(incorrectData)
}).toThrow()
})

it('should throw an error when encrypting with an invalid public key', () => {
const invalidPublicKey = 'invalidPublicKey'
const invalidEncryptionService = new RsaEncryptionService({ publicKey: invalidPublicKey, privateKey })

expect(() => {
invalidEncryptionService.encrypt('test')
}).toThrow()
})

it('should throw an error when decrypting with an invalid private key', () => {
const data = 'Hello, World!'
const encryptedData = encryptionService.encrypt(data)
const invalidPrivateKey = 'invalidPrivateKey'
const invalidEncryptionService = new RsaEncryptionService({ publicKey, privateKey: invalidPrivateKey })

expect(() => {
invalidEncryptionService.decrypt(encryptedData)
}).toThrow()
})
})
115 changes: 89 additions & 26 deletions __test__/projects/GitHubProjectDataSource.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
import { GitHubProjectDataSource } from "@/features/projects/data"
import RemoteConfig from "@/features/projects/domain/RemoteConfig"

/**
* Simple encryption service for testing. Does nothing.
*/
const noopEncryptionService = {
encrypt: function (data: string): string {
return data
},
decrypt: function (encryptedDataBase64: string): string {
return encryptedDataBase64
}
}

/**
* Simple encoder for testing
*/
const base64RemoteConfigEncoder = {
encode: function (remoteConfig: RemoteConfig): string {
return Buffer.from(JSON.stringify(remoteConfig)).toString("base64")
},
decode: function (encodedString: string): RemoteConfig {
return JSON.parse(Buffer.from(encodedString, "base64").toString())
}
}

test("It loads repositories from data source", async () => {
let didLoadRepositories = false
Expand All @@ -9,7 +34,9 @@ test("It loads repositories from data source", async () => {
didLoadRepositories = true
return []
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
await sut.getProjects()
expect(didLoadRepositories).toBeTruthy()
Expand Down Expand Up @@ -43,7 +70,9 @@ test("It maps projects including branches and tags", async () => {
}]
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects).toEqual([{
Expand Down Expand Up @@ -107,7 +136,9 @@ test("It removes suffix from project name", async () => {
}]
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].id).toEqual("acme-foo")
Expand Down Expand Up @@ -147,7 +178,9 @@ test("It supports multiple OpenAPI specifications on a branch", async () => {
}]
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects).toEqual([{
Expand Down Expand Up @@ -209,7 +242,9 @@ test("It filters away projects with no versions", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects.length).toEqual(0)
Expand Down Expand Up @@ -243,7 +278,9 @@ test("It filters away branches with no specifications", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions.length).toEqual(1)
Expand Down Expand Up @@ -283,7 +320,9 @@ test("It filters away tags with no specifications", async () => {
}]
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions.length).toEqual(2)
Expand Down Expand Up @@ -314,7 +353,9 @@ test("It reads image from configuration file with .yml extension", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].imageURL).toEqual("/api/blob/acme/foo-openapi/icon.png?ref=12345678")
Expand Down Expand Up @@ -345,7 +386,9 @@ test("It reads display name from configuration file with .yml extension", async
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].id).toEqual("acme-foo")
Expand Down Expand Up @@ -378,7 +421,9 @@ test("It reads image from configuration file with .yaml extension", async () =>
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].imageURL).toEqual("/api/blob/acme/foo-openapi/icon.png?ref=12345678")
Expand Down Expand Up @@ -409,7 +454,9 @@ test("It reads display name from configuration file with .yaml extension", async
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].id).toEqual("acme-foo")
Expand Down Expand Up @@ -478,7 +525,9 @@ test("It sorts projects alphabetically", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].name).toEqual("anne")
Expand Down Expand Up @@ -529,7 +578,9 @@ test("It sorts versions alphabetically", async () => {
}]
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions[0].name).toEqual("1.0")
Expand Down Expand Up @@ -593,7 +644,9 @@ test("It prioritizes main, master, develop, and development branch names when so
}]
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions[0].name).toEqual("main")
Expand Down Expand Up @@ -641,7 +694,9 @@ test("It identifies the default branch in returned versions", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
const defaultVersionNames = projects[0]
Expand Down Expand Up @@ -682,7 +737,9 @@ test("It adds remote versions from the project configuration", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions).toEqual([{
Expand All @@ -692,11 +749,11 @@ test("It adds remote versions from the project configuration", async () => {
specifications: [{
id: "huey",
name: "Huey",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/huey.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/huey.yml" })}`
}, {
id: "dewey",
name: "Dewey",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/dewey.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/dewey.yml" })}`
}]
}, {
id: "bobby",
Expand All @@ -705,7 +762,7 @@ test("It adds remote versions from the project configuration", async () => {
specifications: [{
id: "louie",
name: "Louie",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/louie.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/louie.yml" })}`
}]
}])
})
Expand Down Expand Up @@ -745,7 +802,9 @@ test("It modifies ID of remote version if the ID already exists", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions).toEqual([{
Expand All @@ -766,7 +825,7 @@ test("It modifies ID of remote version if the ID already exists", async () => {
specifications: [{
id: "baz",
name: "Baz",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/baz.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`
}]
}, {
id: "bar2",
Expand All @@ -775,7 +834,7 @@ test("It modifies ID of remote version if the ID already exists", async () => {
specifications: [{
id: "hello",
name: "Hello",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/hello.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/hello.yml" })}`
}]
}])
})
Expand Down Expand Up @@ -806,7 +865,9 @@ test("It lets users specify the ID of a remote version", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions).toEqual([{
Expand All @@ -816,7 +877,7 @@ test("It lets users specify the ID of a remote version", async () => {
specifications: [{
id: "baz",
name: "Baz",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/baz.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`
}]
}])
})
Expand Down Expand Up @@ -847,7 +908,9 @@ test("It lets users specify the ID of a remote specification", async () => {
tags: []
}]
}
}
},
encryptionService: noopEncryptionService,
remoteConfigEncoder: base64RemoteConfigEncoder
})
const projects = await sut.getProjects()
expect(projects[0].versions).toEqual([{
Expand All @@ -857,7 +920,7 @@ test("It lets users specify the ID of a remote specification", async () => {
specifications: [{
id: "some-spec",
name: "Baz",
url: `/api/proxy?url=${encodeURIComponent("https://example.com/baz.yml")}`
url: `/api/remotes/${base64RemoteConfigEncoder.encode({ url: "https://example.com/baz.yml" })}`
}]
}])
})
Loading
Loading