<commit_before>var assert = require('assert-plus');
var PUBLIC_STOR_PATH = /^\/([a-zA-Z][a-zA-Z0-9_\-\.@%]+)\/public(\/(.*)|)/;
var ANONYMOUS_USER = 'anonymous';

module.exports = {
    authorize: authorize
};

/*
 * mahi: mahi client
 * context.principal: the object received from mahi.authenticate
 * context.action: auth action
 * context.resource.owner: the object received from calling mahi.getAccount
 *       with the resource owner's login
 * context.resource.key: resource key or path (used to check for PUBLIC gets)
 * context.resource.roles: resource role tags (UUIDs)
 * context.conditions: all additional context collected as part of the request,
 *      including activeRoles
 */
function authorize(opts) {
    assert.object(opts);
    assert.object(opts.mahi, 'mahi');
    assert.object(opts.context, 'context');
    assert.object(opts.context.principal, 'principal');
    assert.string(opts.context.action, 'action');
    assert.object(opts.context.resource, 'resource');
    assert.object(opts.context.conditions, 'conditions');

    assert.object(opts.context.principal.account, 'principal.account');
    assert.optionalObject(opts.context.principal.user, 'principal.user');

    assert.object(opts.context.resource.owner, 'resource.owner');
    assert.string(opts.context.resource.owner.account.uuid,
        'resource.owner.account.uuid');
    assert.arrayOfString(opts.context.resource.roles, 'resource.roles');
    assert.string(opts.context.resource.key, 'resource.key');

    assert.string(opts.context.conditions.method, 'conditions.method');
    assert.arrayOfString(opts.context.conditions.activeRoles,
        'condition.activeRoles'); // array of UUID

    var mahi = opts.mahi;
    var method = opts.context.conditions.method;
    var resource = opts.context.resource;
    resource.roles = resource.roles || [];

    // Authorize if public GET.
    if (PUBLIC_STOR_PATH.test(resource.key) &&
        (method === 'GET' || method === 'HEAD' || method === 'OPTIONS')) {

        return (true);
    }

    var ok = false;

    try {
        ok = mahi.authorize(opts.context);
    } catch (e) {
        if (e.restCode === 'RoleTagMismatch') {
            ok = false;
        } else {
            throw e;
        }
    }

    // If authorization fails as the authenticated user, check if the resource
    // owner has allowed anonymous access to the resource.
    if (!ok &&
        resource.owner.user &&
        resource.owner.user.login === ANONYMOUS_USER) {

        opts.context.principal = opts.context.resource.owner;

        try {
            ok = mahi.authorize(opts.context);
        } catch (e) {
            if (e.restCode === 'RoleTagMismatch') {
                ok = false;
            } else {
                throw e;
            }
        }
    }

    return (ok);
}<commit_msg>MANTA-2168 be consistent about errors<commit_after>var assert = require('assert-plus');
var PUBLIC_STOR_PATH = /^\/([a-zA-Z][a-zA-Z0-9_\-\.@%]+)\/public(\/(.*)|)/;
var ANONYMOUS_USER = 'anonymous';

module.exports = {
    authorize: authorize
};

/*
 * mahi: mahi client
 * context.principal: the object received from mahi.authenticate
 * context.action: auth action
 * context.resource.owner: the object received from calling mahi.getAccount
 *       with the resource owner's login
 * context.resource.key: resource key or path (used to check for PUBLIC gets)
 * context.resource.roles: resource role tags (UUIDs)
 * context.conditions: all additional context collected as part of the request,
 *      including activeRoles
 */
function authorize(opts) {
    assert.object(opts);
    assert.object(opts.mahi, 'mahi');
    assert.object(opts.context, 'context');
    assert.object(opts.context.principal, 'principal');
    assert.string(opts.context.action, 'action');
    assert.object(opts.context.resource, 'resource');
    assert.object(opts.context.conditions, 'conditions');

    assert.object(opts.context.principal.account, 'principal.account');
    assert.optionalObject(opts.context.principal.user, 'principal.user');

    assert.object(opts.context.resource.owner, 'resource.owner');
    assert.string(opts.context.resource.owner.account.uuid,
        'resource.owner.account.uuid');
    assert.arrayOfString(opts.context.resource.roles, 'resource.roles');
    assert.string(opts.context.resource.key, 'resource.key');

    assert.string(opts.context.conditions.method, 'conditions.method');
    assert.arrayOfString(opts.context.conditions.activeRoles,
        'condition.activeRoles'); // array of UUID

    var mahi = opts.mahi;
    var method = opts.context.conditions.method;
    var resource = opts.context.resource;
    resource.roles = resource.roles || [];

    // Authorize if public GET.
    if (PUBLIC_STOR_PATH.test(resource.key) &&
        (method === 'GET' || method === 'HEAD' || method === 'OPTIONS')) {

        return (true);
    }

    var error;

    try {
        mahi.authorize(opts.context);
    } catch (e) {
        error = e;
    }

    // If authorization fails as the authenticated user, check if the resource
    // owner has allowed anonymous access to the resource.
    if (error &&
        resource.owner.user &&
        resource.owner.user.login === ANONYMOUS_USER) {

        opts.context.principal = opts.context.resource.owner;
        opts.context.conditions.activeRoles =
            opts.context.principal.user.defaultRoles;

        try {
            mahi.authorize(opts.context);
        } catch (e) {
            throw error; // throw the error from when we attempted to authorize
                         // as the user himself, not as the anonymous user, as
                         // that feedback is more likely something the user can
                         // act upon and do something about, if possible
        }
    } else if (error) {
        throw error;
    }

    return (true);
}
{'fraction_non_alphanumeric': 0.13302674161130984, 'fraction_numerical': 0.0013626298756600238, 'mean_word_length': 2.7665169980756894, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1112418', 'n_tokens_mistral': 1772, 'n_tokens_neox': 1697, 'n_words': 392}