Exemple #1
0
    def test_create_bad(self, mock_authzero, mock_secrets, mock_request_get, mock_request_post):
        """
        test that we'll correctly fixup profiles on creation
        """
        mock_authzero.return_value = "dinopark"
        mock_secrets.return_value = "is_pretty_cool"

        class FakeResponse:
            def __init__(self, fake={}):
                self.fake = fake
                self.text = str(fake)

            def json(self):
                return self.fake

            def ok(self):
                return True

        mock_request_post.return_value = FakeResponse()
        mock_request_get.return_value = FakeResponse(fake=self.mu)

        u = cis_profile.User()
        # Bad bad
        u.user_id.value = "anotherauser"
        u.fun_title.metadata.display = "private"
        u.fun_title.value = None
        profiles = [u]
        publisher = cis_publisher.Publish(profiles, login_method="ad", publisher_name="ldap")
        publisher.filter_known_cis_users(profiles)
        assert publisher.profiles[0].fun_title.metadata.display != "private"
Exemple #2
0
    def test_post_specific_user(self, mock_validate, mock_authzero,
                                mock_secrets, mock_request_get,
                                mock_request_post):
        mock_authzero.return_value = "dinopark"
        mock_secrets.return_value = "is_pretty_cool"
        mock_validate.return_value = True

        class FakeResponse:
            def __init__(self, fake={}):
                self.fake = fake
                self.text = str(fake)

            def json(self):
                return self.fake

            def ok(self):
                return True

        mock_request_post.return_value = FakeResponse()
        mock_request_get.return_value = FakeResponse()
        profiles = [cis_profile.User(user_id="test")]
        publisher = cis_publisher.Publish(profiles,
                                          login_method="ad",
                                          publisher_name="ldap")
        publisher.post_all(user_ids=["test"])
        assert publisher.profiles[0].user_id.value == "test"
Exemple #3
0
    def test_known_users(self, mock_request_get, mock_authzero, mock_secrets):
        mock_secrets.return_value = "hi"
        mock_authzero.return_value = "hi"

        class FakeResponse:
            def __init__(self, fake={}):
                self.fake = fake
                self.text = str(fake)

            def json(self):
                return self.fake

            def ok(self):
                return True

        mu = [{
            "user_id": "auser",
            "uuid": "093249324",
            "primary_email": "*****@*****.**"
        }]
        mock_request_get.return_value = FakeResponse(fake=mu)

        profiles = [cis_profile.User()]
        publisher = cis_publisher.Publish(profiles,
                                          login_method="ad",
                                          publisher_name="ldap")
        u = publisher.get_known_cis_users()
        assert u == mu
Exemple #4
0
    def publish(self, user_ids=None, chunk_size=30):
        """
        Glue to create or fetch cis_profile.User profiles for this publisher
        Then pass everything over to the Publisher class
        None, ALL profiles are sent.
        @user_ids: list of str - user ids to publish. If None, all users are published.
        @chunk_size: int when no user_id is selected, this is the size of the chunk/slice we'll create to divide the
        work between function calls (to self)
        """
        if user_ids is None:
            le = "All"
        else:
            le = len(user_ids)
        logger.info("Starting Auth0 Publisher [{} users]".format(le))
        # XXX login_method is overridden when posting the user or listing users, i.e. the one here does not matter
        publisher = cis_publisher.Publish([],
                                          login_method="github",
                                          publisher_name="auth0")

        # These are the users auth0 knows about
        self.az_users = self.fetch_az_users(user_ids)

        # Should we fan-out processing to multiple function calls?
        if user_ids is None:
            # These are the users CIS knows about
            all_cis_users = []
            for c in self.az_whitelisted_connections:
                publisher.login_method = c
                publisher.get_known_cis_users(include_inactive=False)
                all_cis_users += publisher.known_cis_users_by_user_id.keys()
            logger.info(
                "Got {} known CIS users for all whitelisted login methods".
                format(len(all_cis_users)))

            # Because we do not care about most attributes update, we only process new users, or users that will be
            # deactivated in order to save time. Note that there is (currently) no auth0 hook to notify of new user
            # event, so this (the auth0 publisher that is) function needs to be reasonably fast to avoid delays when
            # provisioning users
            # So first, remove all known users from the requested list
            user_ids_to_process = list(
                set(self.get_az_user_ids()) - set(all_cis_users))
            # Add blocked users so that they get deactivated
            for u in self.az_users:
                if u["user_id"] in self.get_az_user_ids():
                    if "blocked" in u.keys() and u["blocked"] is True:
                        user_ids_to_process.append(u["user_id"])

            logger.info(
                "After filtering out known CIS users/in auth0 blocked users, we will process {} users"
                .format(len(user_ids_to_process)))
            self.fan_out(publisher, chunk_size, user_ids_to_process)
        else:
            # Don't cache auth0 list if we're just getting a single user, so that we get the most up to date data
            # and because it's pretty fast for a single user
            if len(user_ids) == 1:
                os.environ["CIS_AUTHZERO_CACHE_TIME_SECONDS"] = 0
                logger.info(
                    "CIS_AUTHZERO_CACHE_TIME_SECONDS was set to 0 (caching disabled) for this run"
                )
            self.process(publisher, user_ids)
Exemple #5
0
    def test_user_deactivate(self, mock_cis_user):
        hris_data = {}
        with open("tests/fixture/workday.json") as fd:
            hris_data = json.load(fd)

        def side_effect(*args, **kwargs):
            fake_user = cis_profile.User(user_id=args[0])
            if args[0] == "ad|Mozilla-LDAP|community":
                fake_user.staff_information.staff.value = False
            else:
                fake_user.staff_information.staff.value = True
            return fake_user

        mock_cis_user.side_effect = side_effect

        hris = cis_publisher.hris.HRISPublisher()
        publisher = cis_publisher.Publish([],
                                          login_method="ad",
                                          publisher_name="hris")
        # we pass 2 fake users, ndonna is in the fixture, nolongerexist is not in the fixture but "CIS" "has it"
        publisher.known_cis_users_by_user_id = {
            "ad|Mozilla-LDAP|NDonna": "*****@*****.**",
            "ad|Mozilla-LDAP|notexist": "*****@*****.**",
            "ad|Mozilla-LDAP|community": "*****@*****.**",
        }
        publisher.known_cis_users_by_email = {
            "*****@*****.**": "ad|Mozilla-LDAP|NDonna",
            "*****@*****.**": "ad|Mozilla-LDAP|notexist",
            "*****@*****.**": "ad|Mozilla-LDAP|community",
        }
        for uid in publisher.known_cis_users_by_user_id:
            p = cis_profile.User(
                user_id=uid,
                primary_email=publisher.known_cis_users_by_user_id[uid])
            if uid == "ad|Mozilla-LDAP|community":
                p.staff_information.staff.value = False
            else:
                p.staff_information.staff.value = True
            publisher.all_known_profiles[uid] = p

        profiles = hris.convert_hris_to_cis_profiles(
            hris_data,
            publisher.known_cis_users_by_user_id,
            publisher.known_cis_users_by_email,
            user_ids=[
                "ad|Mozilla-LDAP|NDonna", "ad|Mozilla-LDAP|notexist",
                "ad|Mozilla-LDAP|community"
            ],
        )
        profiles = hris.deactivate_users(publisher, profiles, hris_data)
        # nolongerexist is returned by fake cis reply, but is not in hris workday fixture, so it should be active.value
        # = false
        # Community doesnt exist in HRIS but should not be touched so we should have 2 profiles back (ie community is
        # excluded)
        assert len(profiles) == 2
        assert profiles[1].active.value is False
        assert profiles[1].primary_email.value == "*****@*****.**"
        assert profiles[0].active.value is True
        assert profiles[0].primary_email.value == "*****@*****.**"
Exemple #6
0
    def test_post(self, mock_authzero, mock_secrets, mock_request_post):
        mock_authzero.return_value = "dinopark"
        mock_secrets.return_value = "is_pretty_cool"

        class FakeResponse:
            def ok(self):
                return True

        mock_request_post.return_value = FakeResponse()
        profiles = [cis_profile.User()]
        cis_publisher.Publish(profiles, login_method="ad", publisher_name="ldap")
    def publish(self, user_ids=None, chunk_size=50):
        """
        Glue to create or fetch cis_profile.User profiles for this publisher
        Then pass everything over to the Publisher class
        None, ALL profiles are sent.
        @user_ids: list of str - user ids to publish. If None, all users are published.
        @chunk_size: int when no user_id is selected, this is the size of the chunk/slice we'll create to divide the
        work between function calls (to self)
        """
        logger.info("Starting HRIS Publisher")
        report_profiles = self.fetch_report()

        # Should we fan-out processing to multiple function calls?
        if user_ids is None:
            all_user_ids = []
            if os.environ.get("CIS_ENVIRONMENT") == "development":
                user_id_tpl = "ad|Mozilla-LDAP-Dev|"
            else:
                user_id_tpl = "ad|Mozilla-LDAP|"

            for u in report_profiles.get("Report_Entry"):
                all_user_ids.append("{}{}".format(user_id_tpl, u.get("PrimaryWorkEmail").split("@")[0]))
            sliced = [all_user_ids[i : i + chunk_size] for i in range(0, len(all_user_ids), chunk_size)]
            logger.info(
                "No user_id selected. Creating slices of work, chunck size: {}, slices: {}, total users: {} and "
                "faning-out work to self".format(chunk_size, len(sliced), len(all_user_ids))
            )
            lambda_client = boto3.client("lambda")
            for s in sliced:
                lambda_client.invoke(
                    FunctionName=self.context.function_name, InvocationType="Event", Payload=json.dumps(s)
                )

            logger.info("Exiting slicing function successfully")
            return 0

        profiles = self.convert_hris_to_cis_profiles(report_profiles, user_ids)
        del report_profiles

        logger.info("Processing {} profiles".format(len(profiles)))

        publisher = cis_publisher.Publish(profiles, publisher_name="hris", login_method="ad")

        failures = []
        try:
            failures = publisher.post_all(user_ids=user_ids)
        except Exception as e:
            logger.error("Failed to post_all() HRIS profiles. Trace: {}".format(format_exc()))
            raise e

        if len(failures) > 0:
            logger.error("Failed to post {} profiles: {}".format(len(failures), failures))
Exemple #8
0
    def test_filter_cis_users(self, mock_authzero, mock_secrets,
                              mock_request_get, mock_request_post):
        mock_authzero.return_value = "dinopark"
        mock_secrets.return_value = "is_pretty_cool"

        class FakeResponse:
            def __init__(self, fake={}):
                self.fake = fake
                self.text = str(fake)

            def json(self):
                return self.fake

            def ok(self):
                return True

        mock_request_post.return_value = FakeResponse()
        mu = [{
            "user_id": "auser",
            "uuid": "0932493241",
            "primary_email": "*****@*****.**"
        }]
        mock_request_get.return_value = FakeResponse(fake=mu)

        profiles = [cis_profile.User()]
        profiles[0].user_id.value = "auser"

        profiles[0].first_name.value = "firstname"
        profiles[0].first_name.signature.publisher.name = "wrong"

        profiles[0].access_information.hris.values = {"test": "test"}
        profiles[0].access_information.hris.signature.publisher.name = "wrong"

        profiles[0].access_information.ldap.values = {"test": "test"}
        profiles[0].access_information.ldap.signature.publisher.name = "ldap"

        publisher = cis_publisher.Publish(profiles,
                                          login_method="ad",
                                          publisher_name="ldap")
        publisher.filter_known_cis_users()

        # Should be filtered out because publisher = "wrong"
        assert publisher.profiles[0].first_name.value != "firstname"
        # Should not because publisher = "ldap" and thats "us"
        assert publisher.profiles[0].as_dict(
        )["access_information"]["ldap"]["values"] == {
            "test": "test"
        }
        # Should be filtered out because publisher = "wrong"
        assert publisher.profiles[0].as_dict(
        )["access_information"]["hris"]["values"] is None
Exemple #9
0
    def publish(self, user_ids=None, chunk_size=30):
        """
        Glue to create or fetch cis_profile.User profiles for this publisher
        Then pass everything over to the Publisher class
        None, ALL profiles are sent.
        @user_ids: list of str - user ids to publish. If None, all users are published.
        @chunk_size: int when no user_id is selected, this is the size of the chunk/slice we'll create to divide the
        work between function calls (to self)
        """
        if user_ids is None:
            le = "All"
        else:
            le = len(user_ids)
        logger.info("Starting HRIS Publisher [{} users]".format(le))
        cache = self.get_s3_cache()

        # Get access to the known_users function first
        # We override profiles from `publisher` object later on
        publisher = cis_publisher.Publish([],
                                          login_method="ad",
                                          publisher_name="hris")
        attributes = {"staff_information.staff": True, "active": True}
        self.hkey = hash(json.dumps(attributes))
        if cache is None:
            publisher.get_known_cis_users(include_inactive=True)
            publisher.get_known_cis_user_by_attribute_paginated(attributes)
            self.fetch_report()
            self.save_s3_cache({
                "known_profiles":
                publisher.known_profiles[self.hkey],
                "known_cis_users":
                publisher.known_cis_users,
                "report":
                self.report,
            })
        else:
            publisher.known_profiles[self.hkey] = cache["known_profiles"]
            publisher.known_cis_users = cache["known_cis_users"]
            for u in publisher.known_cis_users:
                publisher.known_cis_users_by_user_id[
                    u["user_id"]] = u["primary_email"]
                publisher.known_cis_users_by_email[
                    u["primary_email"]] = u["user_id"]
            self.report = cache["report"]

        # Should we fan-out processing to multiple function calls?
        if user_ids is None:
            self.fan_out(publisher, chunk_size)
        else:
            self.process(publisher, user_ids)
    def publish(self, user_ids=None):
        """
        Glue to create or fetch cis_profile.User profiles for this publisher
        Then pass everything over to the Publisher class
        None, ALL profiles are sent.
        @user_ids: list of str - user ids to publish. If None, all users are published.
        """
        logger.info("Starting LDAP Publisher")
        profiles_xz = self.fetch_from_s3()
        # If there are memory issues here, use lzma.LZMADecompressor() instead
        raw = lzma.decompress(profiles_xz)
        profiles_json = json.loads(raw)
        # Free some memory
        del profiles_xz
        del raw

        profiles = []
        logger.info("Processing {} profiles".format(len(profiles_json)))
        for p in profiles_json:
            str_p = json.dumps(profiles_json[p])
            if (user_ids is None) or (profiles_json[p]["user_id"]["value"]
                                      in user_ids):
                profiles.append(cis_profile.User(user_structure_json=str_p))

        logger.info("Will publish {} profiles".format(len(profiles)))
        publisher = cis_publisher.Publish(profiles,
                                          publisher_name="ldap",
                                          login_method="ad")
        failures = []
        try:
            publisher.filter_known_cis_users()
            failures = publisher.post_all(user_ids=user_ids)
        except Exception as e:
            logger.error(
                "Failed to post_all() LDAP profiles. Trace: {}".format(
                    format_exc()))
            raise e

        if len(failures) > 0:
            logger.error("Failed to post {} profiles: {}".format(
                len(failures), failures))
Exemple #11
0
    def test_known_users(self, mock_request_get, mock_authzero, mock_secrets):
        mock_secrets.return_value = "hi"
        mock_authzero.return_value = "hi"

        class FakeResponse:
            def __init__(self, fake={}):
                self.fake = fake
                self.text = str(fake)

            def json(self):
                return self.fake

            def ok(self):
                return True

        mock_request_get.return_value = FakeResponse(fake=self.mu)

        profiles = [cis_profile.User()]
        publisher = cis_publisher.Publish(profiles, login_method="ad", publisher_name="ldap")
        u = publisher.get_known_cis_users()
        assert u == self.mu["users"]
Exemple #12
0
    def test_known_users_by_attribute(self, mock_request_get, mock_authzero, mock_secrets):
        mock_secrets.return_value = "hi"
        mock_authzero.return_value = "hi"

        class FakeResponse:
            def __init__(self, fake={}):
                self.fake = fake
                self.text = str(fake)

            def json(self):
                return self.fake

            def ok(self):
                return True

        mock_request_get.return_value = FakeResponse(fake=self.mu2)

        profiles = [cis_profile.User()]
        publisher = cis_publisher.Publish(profiles, login_method="ad", publisher_name="ldap")
        attributes = {"staff_information.staff": True, "active": True}
        u = publisher.get_known_cis_user_by_attribute_paginated(attributes)
        print(u)
        assert u["auser"] == self.mu2["users"][0]
Exemple #13
0
 def test_profile_validate(self):
     profiles = [cis_profile.User()]
     publisher = cis_publisher.Publish(profiles,
                                       login_method="ad",
                                       publisher_name="ldap")
     publisher.validate()
Exemple #14
0
 def test_obj(self):
     profiles = [cis_profile.User()]
     publisher = cis_publisher.Publish(profiles,
                                       login_method="ad",
                                       publisher_name="ldap")
     assert isinstance(publisher, object)
Exemple #15
0
 def __init__(self):
     self.secret_manager = cis_publisher.secret.Manager()
     self.publisher = cis_publisher.Publish([], login_method=None, publisher_name="mozilliansorg")