mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #2676 from overleaf/ew-check-sso-cert-expirations
show expiration data and throw on date errors when loading certs GitOrigin-RevId: d81c96c722f0ce20352f2ef9a346b6d9bfa028d1
This commit is contained in:
parent
7ade96473f
commit
d717ebb2e0
5 changed files with 176 additions and 1 deletions
2
services/web/scripts/ukamf/.gitignore
vendored
Normal file
2
services/web/scripts/ukamf/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
ukfederation-metadata.xml
|
109
services/web/scripts/ukamf/check-certs.js
Normal file
109
services/web/scripts/ukamf/check-certs.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
'use strict'
|
||||
|
||||
/**
|
||||
* Checks that all institutional sso provider certs are still current with the
|
||||
* data provided by the ukamf export file.
|
||||
*
|
||||
* Run with: node check-certs /path/ukamf.xml
|
||||
*
|
||||
* The ukamf metadata xml file can be downloaded from:
|
||||
* http://metadata.ukfederation.org.uk/
|
||||
*/
|
||||
|
||||
const { Certificate } = require('@fidm/x509')
|
||||
const UKAMFDB = require('./ukamf-db')
|
||||
const V1Api = require(`../../app/src/Features/V1/V1Api`).promises
|
||||
const { db } = require('../../app/src/infrastructure/mongojs')
|
||||
const moment = require('moment')
|
||||
|
||||
main()
|
||||
.catch(err => {
|
||||
console.error(err.stack)
|
||||
})
|
||||
.then(() => process.exit())
|
||||
|
||||
async function main() {
|
||||
const [, , file] = process.argv
|
||||
|
||||
console.log(`loading file ${file}`)
|
||||
|
||||
const ukamfDB = new UKAMFDB(file)
|
||||
await ukamfDB.init()
|
||||
|
||||
const activeProviderIds = await getActiveProviderIds()
|
||||
|
||||
for (const providerId of activeProviderIds) {
|
||||
await checkCert(ukamfDB, providerId)
|
||||
}
|
||||
}
|
||||
|
||||
async function checkCert(ukamfDB, providerId) {
|
||||
console.log(`Checking certificates for providerId: ${providerId}`)
|
||||
try {
|
||||
const { body } = await V1Api.request({
|
||||
json: true,
|
||||
qs: { university_id: providerId },
|
||||
uri: '/api/v1/sharelatex/university_saml'
|
||||
})
|
||||
// show notice if sso not currently enabled
|
||||
if (body.sso_enabled === true) {
|
||||
console.log(` * SSO enabled`)
|
||||
} else {
|
||||
console.log(` ! SSO NOT enabled`)
|
||||
}
|
||||
// lookup entity id in ukamf database
|
||||
const entity = ukamfDB.findByEntityID(body.sso_entity_id)
|
||||
// if entity found then compare certs
|
||||
if (entity) {
|
||||
const samlConfig = entity.getSamlConfig()
|
||||
// check if certificates match
|
||||
if (samlConfig.cert === body.sso_cert) {
|
||||
console.log(' * UKAMF certificate matches configuration')
|
||||
} else {
|
||||
console.log(' ! UKAMF certificate DOES NOT match configuration')
|
||||
}
|
||||
} else {
|
||||
console.log(` ! No UKAMF entity found for ${body.sso_entity_id}`)
|
||||
}
|
||||
// check expiration on configured certificate
|
||||
const certificate = Certificate.fromPEM(
|
||||
Buffer.from(
|
||||
`-----BEGIN CERTIFICATE-----\n${
|
||||
body.sso_cert
|
||||
}\n-----END CERTIFICATE-----`,
|
||||
'utf8'
|
||||
)
|
||||
)
|
||||
|
||||
const validFrom = moment(certificate.validFrom)
|
||||
const validTo = moment(certificate.validTo)
|
||||
|
||||
if (validFrom.isAfter(moment())) {
|
||||
console.log(` ! Certificate not valid till: ${validFrom.format('LLL')}`)
|
||||
} else if (validTo.isBefore(moment())) {
|
||||
console.log(` ! Certificate expired: ${validTo.format('LLL')}`)
|
||||
} else if (validTo.isBefore(moment().add(60, 'days'))) {
|
||||
console.log(` ! Certificate expires: ${validTo.format('LLL')}`)
|
||||
} else {
|
||||
console.log(` * Certificate expires: ${validTo.format('LLL')}`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(` ! ${err.statusCode} Error getting university config from v1`)
|
||||
}
|
||||
}
|
||||
|
||||
async function getActiveProviderIds() {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.users.distinct(
|
||||
'samlIdentifiers.providerId',
|
||||
{ 'samlIdentifiers.externalUserId': { $exists: true } },
|
||||
(err, providerIds) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(providerIds)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
|
@ -3,12 +3,17 @@
|
|||
/**
|
||||
* Run with: node metadata-processor /path/ukamf.xml http://idp/entity/id
|
||||
*
|
||||
* `npm install` must be run for scripts/ukamf first.
|
||||
*
|
||||
* The ukamf metadata xml file can be downloaded from:
|
||||
* http://metadata.ukfederation.org.uk/
|
||||
*
|
||||
* The entity id should be provided by the university.
|
||||
*/
|
||||
|
||||
const { Certificate } = require('@fidm/x509')
|
||||
const moment = require('moment')
|
||||
|
||||
const UKAMFDB = require('./ukamf-db')
|
||||
|
||||
main().catch(err => {
|
||||
|
@ -18,7 +23,7 @@ main().catch(err => {
|
|||
async function main() {
|
||||
const [, , file, entityId] = process.argv
|
||||
|
||||
console.log(`loading file ${file}`)
|
||||
console.log(`loading file ${file}...\n`)
|
||||
|
||||
const ukamfDB = new UKAMFDB(file)
|
||||
await ukamfDB.init()
|
||||
|
@ -29,6 +34,32 @@ async function main() {
|
|||
}
|
||||
const samlConfig = entity.getSamlConfig()
|
||||
|
||||
const certificate = Certificate.fromPEM(
|
||||
Buffer.from(
|
||||
`-----BEGIN CERTIFICATE-----\n${
|
||||
samlConfig.cert
|
||||
}\n-----END CERTIFICATE-----`,
|
||||
'utf8'
|
||||
)
|
||||
)
|
||||
|
||||
const validFrom = moment(certificate.validFrom)
|
||||
const validTo = moment(certificate.validTo)
|
||||
|
||||
if (validFrom.isAfter(moment())) {
|
||||
throw new Error(`certificate not valid till: ${validFrom.format('LLL')}`)
|
||||
}
|
||||
|
||||
if (validTo.isBefore(moment())) {
|
||||
throw new Error(`certificate expired: ${validTo.format('LLL')}`)
|
||||
}
|
||||
|
||||
console.log(
|
||||
`!!!!!!!!!!!!!\nCERTIFICATE EXPIRES: ${validTo.format(
|
||||
'LLL'
|
||||
)}\n!!!!!!!!!!!!!\n`
|
||||
)
|
||||
|
||||
console.log(`UPDATE universities SET
|
||||
sso_entity_id = '${samlConfig.entityId}',
|
||||
sso_entry_point = '${samlConfig.entryPoint}',
|
||||
|
|
28
services/web/scripts/ukamf/package-lock.json
generated
Normal file
28
services/web/scripts/ukamf/package-lock.json
generated
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@fidm/asn1": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@fidm/asn1/-/asn1-1.0.4.tgz",
|
||||
"integrity": "sha512-esd1jyNvRb2HVaQGq2Gg8Z0kbQPXzV9Tq5Z14KNIov6KfFD6PTaRIO8UpcsYiTNzOqJpmyzWgVTrUwFV3UF4TQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@fidm/x509": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fidm/x509/-/x509-1.2.1.tgz",
|
||||
"integrity": "sha512-nwc2iesjyc9hkuzcrMCBXQRn653XuAUKorfWM8PZyJawiy1QzLj4vahwzaI25+pfpwOLvMzbJ0uKpWLDNmo16w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@fidm/asn1": "^1.0.4",
|
||||
"tweetnacl": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
5
services/web/scripts/ukamf/package.json
Normal file
5
services/web/scripts/ukamf/package.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"@fidm/x509": "^1.2.1"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue