<commit_before>'use strict';

const should = require('should');
const sinon = require('sinon');

const {
  InternalError,
  Request,
} = require('../../../index');
const Kuzzle = require('../../mocks/kuzzle.mock');
const { Profile } = require('../../../lib/model/security/profile');
const { User } = require('../../../lib/model/security/user');


describe('Test: model/security/user', () => {
  let kuzzle;
  let profile;
  let profile2;
  let user;

  beforeEach(() => {
    kuzzle = new Kuzzle();

    profile = new Profile();
    profile._id = 'profile';
    profile.isActionAllowed = sinon.stub().resolves(true);

    profile2 = new Profile();
    profile2._id = 'profile2';
    profile2.isActionAllowed = sinon.stub().resolves(false);

    user = new User();
    user.profileIds = ['profile', 'profile2'];

    kuzzle.ask
      .withArgs('core:security:profile:mGet')
      .resolves([profile, profile2]);
  });

  it('should retrieve the good rights list', () => {
    const
      profileRights = {
        rights1: {
          controller: 'document',
          action: 'get',
          index: 'foo',
          collection: 'bar',
          value: 'allowed'
        },
        rights4: {
          controller: 'document',
          action: 'delete',
          index: 'foo',
          collection: 'bar',
          value: 'denied'
        }
      },
      profileRights2 = {
        rights1: {
          controller: 'document',
          action: 'get',
          index: 'foo',
          collection: 'bar',
          value: 'denied'
        },
        rights3: {
          controller: 'document',
          action: 'create',
          index: 'foo',
          collection: 'bar',
          value: 'allowed'
        }
      };

    sinon.stub(user, 'getProfiles').resolves([profile, profile2]);
    sinon.stub(profile, 'getRights').resolves(profileRights);
    sinon.stub(profile2, 'getRights').resolves(profileRights2);

    return user.getRights()
      .then(rights => {
        let filteredItem;

        should(rights).be.an.Object();
        rights = Object.keys(rights).reduce((array, item) => array.concat(rights[item]), []);
        should(rights).be.an.Array();

        filteredItem = rights.filter(
          item => item.controller === 'document' && item.action === 'get');

        should(filteredItem).length(1);
        should(filteredItem[0].index).be.equal('foo');
        should(filteredItem[0].collection).be.equal('bar');
        should(filteredItem[0].value).be.equal('allowed');

        filteredItem = rights.filter(
          item => item.controller === 'document' && item.action === 'delete');

        should(filteredItem).length(1);
        should(filteredItem[0].index).be.equal('foo');
        should(filteredItem[0].collection).be.equal('bar');
        should(filteredItem[0].value).be.equal('denied');

        filteredItem = rights.filter(
          item => item.controller === 'document' && item.action === 'create');

        should(filteredItem).length(1);
        should(filteredItem[0].index).be.equal('foo');
        should(filteredItem[0].collection).be.equal('bar');
        should(filteredItem[0].value).be.equal('allowed');
      });
  });

  it('should retrieve the profile', () => {
    return user.getProfiles()
      .then(p => {
        should(p).be.an.Array();
        should(p[0]).be.an.Object();
        should(p[0]).be.exactly(profile);
      });
  });

  it('should use the isActionAllowed method from its profile', () => {
    return user.isActionAllowed(new Request({}))
      .then(isActionAllowed => {
        should(isActionAllowed).be.a.Boolean();
        should(isActionAllowed).be.true();
        should(profile.isActionAllowed).be.called();
      });
  });

  it('should respond false if the user have no profileIds', () => {
    user.profileIds = [];
    return user.isActionAllowed(new Request({}))
      .then(isActionAllowed => {
        should(isActionAllowed).be.a.Boolean();
        should(isActionAllowed).be.false();
        should(profile.isActionAllowed).not.be.called();
      });
  });

  it('should reject if loadProfiles throws an error', () => {
    global.kuzzle = null;

    return should(user.isActionAllowed(new Request({}))).be.rejectedWith(
      InternalError,
      { id: 'security.user.uninitialized' });
  });

});<commit_msg>add unit test for user.areTargetsAllowed and user.isActionAllowed<commit_after>'use strict';

const should = require('should');
const sinon = require('sinon');

const {
  InternalError,
  Request,
} = require('../../../index');
const Kuzzle = require('../../mocks/kuzzle.mock');
const { Profile } = require('../../../lib/model/security/profile');
const { User } = require('../../../lib/model/security/user');


describe('Test: model/security/user', () => {
  let kuzzle;
  let profile;
  let profilePolicies;
  let profile2;
  let profilePolicies2;
  let user;

  beforeEach(() => {
    kuzzle = new Kuzzle();

    profile = new Profile();
    profile._id = 'profile';
    profile.isActionAllowed = sinon.stub().resolves(true);
    profilePolicies = [
      { 
        role: { checkRestrictions: sinon.stub().returns(false) },
        restrictedTo: new Map()
      },
      {
        role: { checkRestrictions: sinon.stub().returns(false) },
        restrictedTo: new Map()
      },
    ];
    profile.getAllowedPolicies = sinon.stub().resolves(profilePolicies);

    profile2 = new Profile();
    profile2._id = 'profile2';
    profile2.isActionAllowed = sinon.stub().resolves(false);
    profilePolicies2 = [
      { 
        role: { checkRestrictions: sinon.stub().returns(false) },
        restrictedTo: new Map()
      },
      { 
        role: { checkRestrictions: sinon.stub().returns(true) },
        restrictedTo: new Map()
      },
    ];
    profile2.getAllowedPolicies = sinon.stub().resolves(profilePolicies2);

    user = new User();
    user.profileIds = ['profile', 'profile2'];

    kuzzle.ask
      .withArgs('core:security:profile:mGet')
      .resolves([profile, profile2]);
  });

  it('should retrieve the good rights list', () => {
    const
      profileRights = {
        rights1: {
          controller: 'document',
          action: 'get',
          index: 'foo',
          collection: 'bar',
          value: 'allowed'
        },
        rights4: {
          controller: 'document',
          action: 'delete',
          index: 'foo',
          collection: 'bar',
          value: 'denied'
        }
      },
      profileRights2 = {
        rights1: {
          controller: 'document',
          action: 'get',
          index: 'foo',
          collection: 'bar',
          value: 'denied'
        },
        rights3: {
          controller: 'document',
          action: 'create',
          index: 'foo',
          collection: 'bar',
          value: 'allowed'
        }
      };

    sinon.stub(user, 'getProfiles').resolves([profile, profile2]);
    sinon.stub(profile, 'getRights').resolves(profileRights);
    sinon.stub(profile2, 'getRights').resolves(profileRights2);

    return user.getRights()
      .then(rights => {
        let filteredItem;

        should(rights).be.an.Object();
        rights = Object.keys(rights).reduce((array, item) => array.concat(rights[item]), []);
        should(rights).be.an.Array();

        filteredItem = rights.filter(
          item => item.controller === 'document' && item.action === 'get');

        should(filteredItem).length(1);
        should(filteredItem[0].index).be.equal('foo');
        should(filteredItem[0].collection).be.equal('bar');
        should(filteredItem[0].value).be.equal('allowed');

        filteredItem = rights.filter(
          item => item.controller === 'document' && item.action === 'delete');

        should(filteredItem).length(1);
        should(filteredItem[0].index).be.equal('foo');
        should(filteredItem[0].collection).be.equal('bar');
        should(filteredItem[0].value).be.equal('denied');

        filteredItem = rights.filter(
          item => item.controller === 'document' && item.action === 'create');

        should(filteredItem).length(1);
        should(filteredItem[0].index).be.equal('foo');
        should(filteredItem[0].collection).be.equal('bar');
        should(filteredItem[0].value).be.equal('allowed');
      });
  });

  it('should retrieve the profile', () => {
    return user.getProfiles()
      .then(p => {
        should(p).be.an.Array();
        should(p[0]).be.an.Object();
        should(p[0]).be.exactly(profile);
      });
  });

  it('should use the isActionAllowed method from its profile', () => {
    return user.isActionAllowed(new Request({}))
      .then(isActionAllowed => {
        should(isActionAllowed).be.a.Boolean();
        should(isActionAllowed).be.true();
        should(profile.isActionAllowed).be.called();
      });
  });

  it('should respond false if the user have no profileIds', () => {
    user.profileIds = [];
    return user.isActionAllowed(new Request({}))
      .then(isActionAllowed => {
        should(isActionAllowed).be.a.Boolean();
        should(isActionAllowed).be.false();
        should(profile.isActionAllowed).not.be.called();
      });
  });

  it('should reject if loadProfiles throws an error', () => {
    global.kuzzle = null;

    return should(user.isActionAllowed(new Request({}))).be.rejectedWith(
      InternalError,
      { id: 'security.user.uninitialized' });
  });

  describe('#isActionAllowed', () => {
    it('should return false if there is no profileIds', async () => {
      user.profileIds = undefined;
      let isAllowed = await user.isActionAllowed(new Request({}));
      await should(isAllowed).be.false();

      user.profileIds = [];
      isAllowed = await user.isActionAllowed(new Request({}));
      await should(isAllowed).be.false();
    });

    it('should call isActionAllowed on each profile until one resolves to true, only if there is no targets in the request', async () => {
      const profiles = [
        { isActionAllowed: sinon.stub().resolves(false) },
        { isActionAllowed: sinon.stub().resolves(true) },
      ];
      const request = new Request({});
      user.getProfiles = sinon.stub().resolves(profiles);
      const allowed = await user.isActionAllowed(request);

      await should(profiles[0].isActionAllowed)
        .be.calledOnce().and.be.calledWith(request);

      await should(profiles[1].isActionAllowed)
        .be.calledOnce().and.be.calledWith(request);

      await should(allowed).be.true();
    });

    it('should call areTargetsAllowed if the request contains targets', async () => {
      const profiles = [
        { isActionAllowed: sinon.stub().resolves(false) },
        { isActionAllowed: sinon.stub().resolves(true) },
      ];
      const targets = [
        { index: 'index', collections: ['collection'] },
        { index: 'foo', collections: ['bar'] },
      ];
      user.getProfiles = sinon.stub().resolves(profiles);
      user.areTargetsAllowed = sinon.stub().resolves(true);
      const request = new Request({
        targets
      });

      const allowed = await user.isActionAllowed(request);
      await should(allowed).be.true();
      await should(user.areTargetsAllowed)
        .be.calledOnce().and.be.calledWith(request, profiles, targets);
    });
  });

  describe('#areTargetsAllowed', () => {
    it('should retrieve policies for each profiles', async () => {
      const request = new Request({});
      await user.areTargetsAllowed(
        request,
        [profile, profile2],
        []
      );
      await should(profile.getAllowedPolicies)
        .be.calledOnce()
        .and.be.calledWith(request);
      await should(profile2.getAllowedPolicies)
        .be.calledOnce()
        .and.be.calledWith(request);
    });

    it('should return false if one of the targets contains a wildcard', async () => {
      const request = new Request({});
      let allowed = await user.areTargetsAllowed(
        request,
        [profile, profile2],
        [{ index: '*', collections: ['foo'] }]
      );

      await should(allowed).be.false();

      allowed = await user.areTargetsAllowed(
        request,
        [profile, profile2],
        [{ index: 'foo', collections: ['*'] }]
      );

      await should(allowed).be.false();
    });

    it('should return true if there is no targets', async () => {
      const request = new Request({});
      let allowed = await user.areTargetsAllowed(
        request,
        [profile, profile2],
        []
      );

      await should(allowed).be.true();
    });

    it('should skip targets with missing index or collections', async () => {
      const request = new Request({});
      let allowed = await user.areTargetsAllowed(
        request,
        [profile, profile2],
        [{ collections: ['foo'] }]
      );

      await should(allowed).be.true();

      allowed = await user.areTargetsAllowed(
        request,
        [profile, profile2],
        [{ index: 'foo' }]
      );

      await should(allowed).be.true();
    });

    it('should check the restrictions for each role of each policies of each profiles', async () => {
      const request = new Request({});
      let allowed = await user.areTargetsAllowed(
        request,
        [profile, profile2],
        [
          { index: 'foo', collections: ['bar', 'baz'] },
          { index: 'alpha', collections: ['beta'] },
        ]
      );

      await should(allowed).be.true();

      await sinon.assert.calledWithMatch(
        profilePolicies[0].role.checkRestrictions.firstCall,
        'foo',
        'bar',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies[0].role.checkRestrictions.secondCall,
        'foo',
        'baz',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies[0].role.checkRestrictions.thirdCall,
        'alpha',
        'beta',
        new Map(),
      );

      await sinon.assert.calledWithMatch(
        profilePolicies[1].role.checkRestrictions.firstCall,
        'foo',
        'bar',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies[1].role.checkRestrictions.secondCall,
        'foo',
        'baz',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies[1].role.checkRestrictions.thirdCall,
        'alpha',
        'beta',
        new Map(),
      );

      await sinon.assert.calledWithMatch(
        profilePolicies2[0].role.checkRestrictions.firstCall,
        'foo',
        'bar',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies2[0].role.checkRestrictions.secondCall,
        'foo',
        'baz',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies2[0].role.checkRestrictions.thirdCall,
        'alpha',
        'beta',
        new Map(),
      );

      await sinon.assert.calledWithMatch(
        profilePolicies2[1].role.checkRestrictions.firstCall,
        'foo',
        'bar',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies2[1].role.checkRestrictions.secondCall,
        'foo',
        'baz',
        new Map(),
      );
      await sinon.assert.calledWithMatch(
        profilePolicies2[1].role.checkRestrictions.thirdCall,
        'alpha',
        'beta',
        new Map(),
      );
    });
  });
});
{'fraction_non_alphanumeric': 0.15937418852246168, 'fraction_numerical': 0.005842638275772527, 'mean_word_length': 2.3976621085134537, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 43, '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': '7567535', 'n_tokens_mistral': 4544, 'n_tokens_neox': 4553, 'n_words': 836}