Possibility to configure an OpenID Connect provider on the instance level WIP (#128)
Pruning external users periodically.
This commit is contained in:
parent
a9a0925ac0
commit
ce2d8ed123
@ -7,6 +7,8 @@ module:depends"http";
|
|||||||
|
|
||||||
local module_host = module:get_host(); -- this module is not global
|
local module_host = module:get_host(); -- this module is not global
|
||||||
|
|
||||||
|
local bare_sessions = prosody.bare_sessions;
|
||||||
|
|
||||||
local vcards = module:open_store("vcard");
|
local vcards = module:open_store("vcard");
|
||||||
|
|
||||||
function check_auth(routes)
|
function check_auth(routes)
|
||||||
@ -113,10 +115,35 @@ local function ensure_user(event)
|
|||||||
});
|
});
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: add a function to prune user that have not logged in since X days.
|
|
||||||
|
local function prune_users(event) -- delete all users that are not connected!
|
||||||
|
local request, response = event.request, event.response;
|
||||||
|
event.response.headers["Content-Type"] = "application/json";
|
||||||
|
|
||||||
|
module:log("info", "Calling prune_users for host %s", module_host);
|
||||||
|
|
||||||
|
for user in usermanager.users(module_host) do
|
||||||
|
-- has the user still open sessions?
|
||||||
|
if (bare_sessions[user..'@'..module_host] ~= nil) then
|
||||||
|
module:log("debug", "User %s on host %s has still active sessions, ignoring.", user, module_host);
|
||||||
|
else
|
||||||
|
-- FIXME: there is a little chance that we delete a user that is currently connecting...
|
||||||
|
-- As this prune should not be called too often, we can consider it is not an issue for now.
|
||||||
|
|
||||||
|
module:log("debug", "Deleting user %s on host %s", user, module_host);
|
||||||
|
update_vcard(user, nil);
|
||||||
|
usermanager.delete_user(user, module_host);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return json.encode({
|
||||||
|
result = "ok";
|
||||||
|
});
|
||||||
|
end
|
||||||
|
|
||||||
module:provides("http", {
|
module:provides("http", {
|
||||||
route = check_auth {
|
route = check_auth {
|
||||||
["POST /" .. module_host .. "/ensure-user"] = ensure_user;
|
["POST /" .. module_host .. "/ensure-user"] = ensure_user;
|
||||||
|
["POST /" .. module_host .. "/prune-users"] = prune_users;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -19,6 +19,7 @@ interface DebugContent {
|
|||||||
alwaysPublishXMPPRoom?: boolean
|
alwaysPublishXMPPRoom?: boolean
|
||||||
enablePodcastChatTagForNonLive?: boolean
|
enablePodcastChatTagForNonLive?: boolean
|
||||||
mucAdmins?: string[]
|
mucAdmins?: string[]
|
||||||
|
externalAccountPruneInterval?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type DebugNumericValue = 'renewCertCheckInterval'
|
type DebugNumericValue = 'renewCertCheckInterval'
|
||||||
@ -26,6 +27,7 @@ type DebugNumericValue = 'renewCertCheckInterval'
|
|||||||
| 'logRotateEvery'
|
| 'logRotateEvery'
|
||||||
| 'logRotateCheckInterval'
|
| 'logRotateCheckInterval'
|
||||||
| 'remoteServerInfosMaxAge'
|
| 'remoteServerInfosMaxAge'
|
||||||
|
| 'externalAccountPruneInterval'
|
||||||
|
|
||||||
type DebugBooleanValue = 'alwaysPublishXMPPRoom' | 'enablePodcastChatTagForNonLive' | 'useOpenSSL'
|
type DebugBooleanValue = 'alwaysPublishXMPPRoom' | 'enablePodcastChatTagForNonLive' | 'useOpenSSL'
|
||||||
|
|
||||||
@ -65,6 +67,7 @@ function _readDebugFile (options: RegisterServerOptions): DebugContent | false {
|
|||||||
debugContent.alwaysPublishXMPPRoom = json.always_publish_xmpp_room === true
|
debugContent.alwaysPublishXMPPRoom = json.always_publish_xmpp_room === true
|
||||||
debugContent.enablePodcastChatTagForNonLive = json.enable_podcast_chat_tag_for_nonlive === true
|
debugContent.enablePodcastChatTagForNonLive = json.enable_podcast_chat_tag_for_nonlive === true
|
||||||
debugContent.mucAdmins = _getJIDs(options, json, 'muc_admins')
|
debugContent.mucAdmins = _getJIDs(options, json, 'muc_admins')
|
||||||
|
debugContent.externalAccountPruneInterval = _getNumericOptions(options, json, 'external_account_prune_interval')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to read the debug_mode file content:', err)
|
logger.error('Failed to read the debug_mode file content:', err)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ import { ExternalAuthenticationError } from './error'
|
|||||||
import { getBaseRouterRoute } from '../helpers'
|
import { getBaseRouterRoute } from '../helpers'
|
||||||
import { canonicalizePluginUri } from '../uri/canonicalize'
|
import { canonicalizePluginUri } from '../uri/canonicalize'
|
||||||
import { getProsodyDomain } from '../prosody/config/domain'
|
import { getProsodyDomain } from '../prosody/config/domain'
|
||||||
|
import { pruneUsers } from '../prosody/api/manage-users'
|
||||||
import { getProsodyFilePaths } from '../prosody/config'
|
import { getProsodyFilePaths } from '../prosody/config'
|
||||||
|
import { debugNumericParameter } from '../debug'
|
||||||
import { createCipheriv, createDecipheriv, randomBytes, Encoding } from 'node:crypto'
|
import { createCipheriv, createDecipheriv, randomBytes, Encoding } from 'node:crypto'
|
||||||
import { Issuer, BaseClient, generators, UnknownObject } from 'openid-client'
|
import { Issuer, BaseClient, generators, UnknownObject } from 'openid-client'
|
||||||
import { JID } from '@xmpp/jid'
|
import { JID } from '@xmpp/jid'
|
||||||
@ -80,6 +82,7 @@ class ExternalAuthOIDC {
|
|||||||
private readonly externalVirtualhost: string
|
private readonly externalVirtualhost: string
|
||||||
private readonly avatarsDir: string
|
private readonly avatarsDir: string
|
||||||
private readonly avatarsFiles: string[]
|
private readonly avatarsFiles: string[]
|
||||||
|
private pruneTimer?: NodeJS.Timer
|
||||||
|
|
||||||
private readonly encryptionOptions = {
|
private readonly encryptionOptions = {
|
||||||
algorithm: 'aes256' as string,
|
algorithm: 'aes256' as string,
|
||||||
@ -619,11 +622,45 @@ class ExternalAuthOIDC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an interval timer to prune external users from Prosody.
|
||||||
|
* @param options Peertube server options.
|
||||||
|
*/
|
||||||
|
public startPruneTimer (options: RegisterServerOptions): void {
|
||||||
|
this.stopPruneTimer() // just in case...
|
||||||
|
|
||||||
|
// every 4 hour (every minutes in debug mode)
|
||||||
|
const pruneInterval = debugNumericParameter(options, 'externalAccountPruneInterval', 60 * 1000, 4 * 60 * 60 * 1000)
|
||||||
|
this.logger.info(`Creating a timer for external account pruning, every ${Math.round(pruneInterval / 1000)}s.`)
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
this.pruneTimer = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
if (!await this.isOk()) { return }
|
||||||
|
|
||||||
|
this.logger.info('Pruning external users...')
|
||||||
|
await pruneUsers(options)
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error('Error while pruning external users: ' + (err as string))
|
||||||
|
}
|
||||||
|
}, pruneInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the prune timer.
|
||||||
|
*/
|
||||||
|
public stopPruneTimer (): void {
|
||||||
|
if (!this.pruneTimer) { return }
|
||||||
|
clearInterval(this.pruneTimer)
|
||||||
|
this.pruneTimer = undefined
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* frees the singleton
|
* frees the singleton
|
||||||
*/
|
*/
|
||||||
public static async destroySingleton (): Promise<void> {
|
public static async destroySingleton (): Promise<void> {
|
||||||
if (!singleton) { return }
|
if (!singleton) { return }
|
||||||
|
singleton.stopPruneTimer()
|
||||||
singleton = undefined
|
singleton = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,6 +700,8 @@ class ExternalAuthOIDC {
|
|||||||
avatarsFiles: prosodyFilePaths.avatarsFiles
|
avatarsFiles: prosodyFilePaths.avatarsFiles
|
||||||
})
|
})
|
||||||
|
|
||||||
|
singleton.startPruneTimer(options)
|
||||||
|
|
||||||
return singleton
|
return singleton
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,47 @@ async function ensureUser (options: RegisterServerOptions, infos: ExternalAccoun
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
/**
|
||||||
ensureUser
|
* Calls an API provided by mod_http_peertubelivechat_manage_users, to prune unused users.
|
||||||
|
* @param options Peertube server options
|
||||||
|
* @throws Error
|
||||||
|
*/
|
||||||
|
async function pruneUsers (options: RegisterServerOptions): Promise<void> {
|
||||||
|
const logger = options.peertubeHelpers.logger
|
||||||
|
|
||||||
|
const currentProsody = getCurrentProsody()
|
||||||
|
if (!currentProsody) {
|
||||||
|
throw new Error('It seems that prosody is not binded... Cant call API.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const prosodyDomain = await getProsodyDomain(options)
|
||||||
|
|
||||||
|
logger.info('Calling pruneUsers')
|
||||||
|
|
||||||
|
// Requesting on localhost, because currentProsody.host does not always resolves correctly (docker use case, ...)
|
||||||
|
const apiUrl = `http://localhost:${currentProsody.port}/` +
|
||||||
|
'peertubelivechat_manage_users/' +
|
||||||
|
`external.${prosodyDomain}/` + // the virtual host name
|
||||||
|
'prune-users'
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.debug('Calling prune-users API on url: ' + apiUrl)
|
||||||
|
await got(apiUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
authorization: 'Bearer ' + await getAPIKey(options),
|
||||||
|
host: currentProsody.host
|
||||||
|
},
|
||||||
|
json: {},
|
||||||
|
responseType: 'json',
|
||||||
|
resolveBodyOnly: true
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`prune-users failed: ' ${err as string}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ensureUser,
|
||||||
|
pruneUsers
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user