remove [github] admin routes (#7105)
This commit is contained in:
@@ -93,7 +93,6 @@ private:
|
||||
obs_pass: 'OBS_PASS'
|
||||
redis_url: 'REDIS_URL'
|
||||
sentry_dsn: 'SENTRY_DSN'
|
||||
shields_secret: 'SHIELDS_SECRET'
|
||||
sl_insight_userUuid: 'SL_INSIGHT_USER_UUID'
|
||||
sl_insight_apiToken: 'SL_INSIGHT_API_TOKEN'
|
||||
sonarqube_token: 'SONARQUBE_TOKEN'
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
function constEq(a, b) {
|
||||
if (a.length !== b.length) {
|
||||
return false
|
||||
}
|
||||
let zero = 0
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
zero |= a.charCodeAt(i) ^ b.charCodeAt(i)
|
||||
}
|
||||
return zero === 0
|
||||
}
|
||||
|
||||
function makeSecretIsValid(shieldsSecret) {
|
||||
return function secretIsValid(secret = '') {
|
||||
return shieldsSecret && constEq(secret, shieldsSecret)
|
||||
}
|
||||
}
|
||||
|
||||
export { makeSecretIsValid }
|
||||
@@ -177,7 +177,6 @@ const privateConfigSchema = Joi.object({
|
||||
obs_pass: Joi.string(),
|
||||
redis_url: Joi.string().uri({ scheme: ['redis', 'rediss'] }),
|
||||
sentry_dsn: Joi.string(),
|
||||
shields_secret: Joi.string(),
|
||||
sl_insight_userUuid: Joi.string(),
|
||||
sl_insight_apiToken: Joi.string(),
|
||||
sonarqube_token: Joi.string(),
|
||||
|
||||
@@ -328,29 +328,6 @@ class TokenPool {
|
||||
this.fifoQueue.forEach(visit)
|
||||
this.priorityQueue.forEach(visit)
|
||||
}
|
||||
|
||||
allValidTokenIds() {
|
||||
const result = []
|
||||
this.forEach(({ id }) => result.push(id))
|
||||
return result
|
||||
}
|
||||
|
||||
serializeDebugInfo({ sanitize = true } = {}) {
|
||||
const maybeSanitize = sanitize ? id => sanitizeToken(id) : id => id
|
||||
|
||||
const priorityQueue = []
|
||||
this.priorityQueue.forEach(t =>
|
||||
priorityQueue.push(t.getDebugInfo({ sanitize }))
|
||||
)
|
||||
|
||||
return {
|
||||
utcEpochSeconds: getUtcEpochSeconds(),
|
||||
allValidTokenIds: this.allValidTokenIds().map(maybeSanitize),
|
||||
fifoQueue: this.fifoQueue.map(t => t.getDebugInfo({ sanitize })),
|
||||
priorityQueue,
|
||||
sanitized: sanitize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { sanitizeToken, Token, TokenPool }
|
||||
|
||||
@@ -19,10 +19,6 @@ describe('The token pool', function () {
|
||||
ids.forEach(id => tokenPool.add(id))
|
||||
})
|
||||
|
||||
it('allValidTokenIds() should return the full list', function () {
|
||||
expect(tokenPool.allValidTokenIds()).to.deep.equal(ids)
|
||||
})
|
||||
|
||||
it('should yield the expected tokens', function () {
|
||||
ids.forEach(id =>
|
||||
times(batchSize, () => expect(tokenPool.next().id).to.equal(id))
|
||||
@@ -38,67 +34,6 @@ describe('The token pool', function () {
|
||||
)
|
||||
})
|
||||
|
||||
describe('serializeDebugInfo should initially return the expected', function () {
|
||||
beforeEach(function () {
|
||||
sinon.useFakeTimers({ now: 1544307744484 })
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
context('sanitize is not specified', function () {
|
||||
it('returns fully sanitized results', function () {
|
||||
// This is `sha()` of '1', '2', '3', '4', '5'. These are written
|
||||
// literally for avoidance of doubt as to whether sanitization is
|
||||
// happening.
|
||||
const sanitizedIds = [
|
||||
'6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b',
|
||||
'd4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35',
|
||||
'4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce',
|
||||
'4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a',
|
||||
'ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d',
|
||||
]
|
||||
|
||||
expect(tokenPool.serializeDebugInfo()).to.deep.equal({
|
||||
allValidTokenIds: sanitizedIds,
|
||||
priorityQueue: [],
|
||||
fifoQueue: sanitizedIds.map(id => ({
|
||||
data: '[redacted]',
|
||||
id,
|
||||
isFrozen: false,
|
||||
isValid: true,
|
||||
nextReset: Token.nextResetNever,
|
||||
usesRemaining: batchSize,
|
||||
})),
|
||||
sanitized: true,
|
||||
utcEpochSeconds: 1544307744,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('with sanitize: false', function () {
|
||||
it('returns unsanitized results', function () {
|
||||
expect(tokenPool.serializeDebugInfo({ sanitize: false })).to.deep.equal(
|
||||
{
|
||||
allValidTokenIds: ids,
|
||||
priorityQueue: [],
|
||||
fifoQueue: ids.map(id => ({
|
||||
data: undefined,
|
||||
id,
|
||||
isFrozen: false,
|
||||
isValid: true,
|
||||
nextReset: Token.nextResetNever,
|
||||
usesRemaining: batchSize,
|
||||
})),
|
||||
sanitized: false,
|
||||
utcEpochSeconds: 1544307744,
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('tokens are marked exhausted immediately', function () {
|
||||
it('should be exhausted', function () {
|
||||
ids.forEach(() => {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# Back up the GitHub tokens from each production server.
|
||||
#
|
||||
|
||||
if test (count $argv) -lt 1
|
||||
echo Usage: (basename (status -f)) shields_secret
|
||||
end
|
||||
|
||||
set shields_secret $argv[1]
|
||||
|
||||
function do_backup
|
||||
set server $argv[1]
|
||||
curl --insecure -u ":$shields_secret" "https://$server.servers.shields.io/\$github-auth/tokens" > "$server""_tokens.json"
|
||||
end
|
||||
|
||||
for server in s0 s1 s2
|
||||
do_backup $server
|
||||
end
|
||||
@@ -1,32 +0,0 @@
|
||||
import { makeSecretIsValid } from '../../../core/server/secret-is-valid.js'
|
||||
|
||||
function setRoutes({ shieldsSecret }, { apiProvider, server }) {
|
||||
const secretIsValid = makeSecretIsValid(shieldsSecret)
|
||||
|
||||
// Allow the admin to obtain the tokens for operational and debugging
|
||||
// purposes. This could be used to:
|
||||
//
|
||||
// - Ensure tokens have been propagated to all servers
|
||||
// - Debug GitHub badge failures
|
||||
//
|
||||
// The admin can authenticate with HTTP Basic Auth, with an empty/any
|
||||
// username and the shields secret in the password and an empty/any
|
||||
// password.
|
||||
//
|
||||
// e.g.
|
||||
// curl --insecure -u ':very-very-secret' 'https://img.shields.io/$github-auth/tokens'
|
||||
server.ajax.on('github-auth/tokens', (json, end, ask) => {
|
||||
if (!secretIsValid(ask.password)) {
|
||||
// An unknown entity tries to connect. Let the connection linger for a minute.
|
||||
return setTimeout(() => {
|
||||
ask.res.statusCode = 401
|
||||
ask.res.setHeader('Cache-Control', 'private')
|
||||
end('Invalid secret.')
|
||||
}, 10000)
|
||||
}
|
||||
ask.res.setHeader('Cache-Control', 'private')
|
||||
end(apiProvider.serializeDebugInfo({ sanitize: false }))
|
||||
})
|
||||
}
|
||||
|
||||
export { setRoutes }
|
||||
@@ -1,71 +0,0 @@
|
||||
import { expect } from 'chai'
|
||||
import Camp from '@shields_io/camp'
|
||||
import portfinder from 'portfinder'
|
||||
import got from '../../../core/got-test-client.js'
|
||||
import GithubApiProvider from '../github-api-provider.js'
|
||||
import { setRoutes } from './admin.js'
|
||||
|
||||
describe('GitHub admin route', function () {
|
||||
const shieldsSecret = '7'.repeat(40)
|
||||
|
||||
let port, baseUrl
|
||||
before(async function () {
|
||||
port = await portfinder.getPortPromise()
|
||||
baseUrl = `http://127.0.0.1:${port}`
|
||||
})
|
||||
|
||||
let camp
|
||||
before(async function () {
|
||||
camp = Camp.start({ port, hostname: '::' })
|
||||
await new Promise(resolve => camp.on('listening', () => resolve()))
|
||||
})
|
||||
after(async function () {
|
||||
if (camp) {
|
||||
await new Promise(resolve => camp.close(resolve))
|
||||
camp = undefined
|
||||
}
|
||||
})
|
||||
|
||||
before(function () {
|
||||
const apiProvider = new GithubApiProvider({ withPooling: true })
|
||||
setRoutes({ shieldsSecret }, { apiProvider, server: camp })
|
||||
})
|
||||
|
||||
context('the password is correct', function () {
|
||||
it('returns a valid JSON response', async function () {
|
||||
const { statusCode, body, headers } = await got(
|
||||
`${baseUrl}/$github-auth/tokens`,
|
||||
{
|
||||
username: '',
|
||||
password: shieldsSecret,
|
||||
responseType: 'json',
|
||||
}
|
||||
)
|
||||
expect(statusCode).to.equal(200)
|
||||
expect(body).to.be.ok
|
||||
expect(headers['cache-control']).to.equal('private')
|
||||
})
|
||||
})
|
||||
|
||||
// Disabled because this code isn't modified often and the test is very
|
||||
// slow. To run it, run `SLOW=true npm run test:core`
|
||||
//
|
||||
// I wasn't able to make this work with fake timers:
|
||||
// https://github.com/sinonjs/sinon/issues/1739
|
||||
if (process.env.SLOW) {
|
||||
context('the password is missing', function () {
|
||||
it('returns the expected message', async function () {
|
||||
this.timeout(11000)
|
||||
const { statusCode, body, headers } = await got(
|
||||
`${baseUrl}/$github-auth/tokens`,
|
||||
{
|
||||
throwHttpErrors: false,
|
||||
}
|
||||
)
|
||||
expect(statusCode).to.equal(401)
|
||||
expect(body).to.equal('"Invalid secret."')
|
||||
expect(headers['cache-control']).to.equal('private')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -54,18 +54,6 @@ class GithubApiProvider {
|
||||
}
|
||||
}
|
||||
|
||||
serializeDebugInfo({ sanitize = true } = {}) {
|
||||
if (this.withPooling) {
|
||||
return {
|
||||
standardTokens: this.standardTokens.serializeDebugInfo({ sanitize }),
|
||||
searchTokens: this.searchTokens.serializeDebugInfo({ sanitize }),
|
||||
graphqlTokens: this.graphqlTokens.serializeDebugInfo({ sanitize }),
|
||||
}
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
addToken(tokenString) {
|
||||
if (this.withPooling) {
|
||||
this.standardTokens.add(tokenString)
|
||||
|
||||
@@ -2,7 +2,6 @@ import { AuthHelper } from '../../core/base-service/auth-helper.js'
|
||||
import RedisTokenPersistence from '../../core/token-pooling/redis-token-persistence.js'
|
||||
import log from '../../core/server/log.js'
|
||||
import GithubApiProvider from './github-api-provider.js'
|
||||
import { setRoutes as setAdminRoutes } from './auth/admin.js'
|
||||
import { setRoutes as setAcceptorRoutes } from './auth/acceptor.js'
|
||||
|
||||
// Convenience class with all the stuff related to the Github API and its
|
||||
@@ -23,7 +22,6 @@ class GithubConstellation {
|
||||
constructor(config) {
|
||||
this._debugEnabled = config.service.debug.enabled
|
||||
this._debugIntervalSeconds = config.service.debug.intervalSeconds
|
||||
this.shieldsSecret = config.private.shields_secret
|
||||
|
||||
const { redis_url: redisUrl, gh_token: globalToken } = config.private
|
||||
if (redisUrl) {
|
||||
@@ -74,9 +72,6 @@ class GithubConstellation {
|
||||
this.apiProvider.addToken(tokenString)
|
||||
})
|
||||
|
||||
const { shieldsSecret, apiProvider } = this
|
||||
setAdminRoutes({ shieldsSecret }, { apiProvider, server })
|
||||
|
||||
if (this.oauthHelper.isConfigured) {
|
||||
setAcceptorRoutes({
|
||||
server,
|
||||
|
||||
Reference in New Issue
Block a user