const { Middleware } = require('libflitter')

class PermissionMiddleware extends Middleware {
    static get services() {
        return [...super.services, 'models', 'activity']
    }

    async test(req, res, next, { check }) {
        const Policy = this.models.get('iam:Policy')

        if ( !req.additional_api_log_data ) req.additional_api_log_data = {}
        req.additional_api_log_data.permission_check = check

        // If the request was authorized using an OAuth2 bearer token,
        // make sure the associated client has permission to access this endpoint.
        if ( req?.oauth?.client ) {
            if ( !req.oauth.client.can(check) ) {
                const reason = 'oauth-permission-fail'
                const fail_activity = await this.activity.api_access_denial({
                    req,
                    reason,
                    check,
                    oauth_client_id: req.oauth.client.uuid,
                })

                req.additional_api_log_data.permission_check_succeeded = false
                req.additional_api_log_data.permission_check_activity_id = fail_activity.id

                return res.status(401)
                    .message('Insufficient permissions (OAuth2 Client).')
                    .api()
            }

            req.additional_api_log_data.permission_check_succeeded = true

            // If the oauth2 client has this permission, then allow the request to continue,
            // even if the user does not.
            // OAuth2Clients need to be able to query users via the API.
            return next()
        }

        const policy_denied = await Policy.check_user_denied(req.user, check)
        const policy_access = await Policy.check_user_access(req.user, check)

        // Make sure the user has permission
        if ( policy_denied || (!req.user.can(check) && !policy_access) ) {
            // Record the failed API access
            const reason = policy_denied ? 'iam-denial' : (!req.user.can(check) ? 'user-permission-fail' : 'iam-not-granted')
            const fail_activity = await this.activity.api_access_denial({ req, reason, check })

            req.additional_api_log_data.permission_check_succeeded = false
            req.additional_api_log_data.permission_check_reason = reason
            req.additional_api_log_data.permission_check_activity_id = fail_activity.id

            return res.status(401)
                .message('Insufficient permissions.')
                .api()
        }

        req.additional_api_log_data.permission_check_succeeded = true
        return next()
    }
}

module.exports = exports = PermissionMiddleware
