Esempio n. 1
0
    def _verify(self, profile_json):
        cis_profile = User(user_structure_json=profile_json)
        try:
            if self.config("verify_publishers", namespace="cis") == "true":
                cis_profile.verify_all_publishers(User())

            if self.config("verify_signatures", namespace="cis") == "true":
                cis_profile.verify_all_signatures()
        except SignatureVerificationFailure:
            return False
        except PublisherVerificationFailure:
            return False
        return True
    def get(self):
        """Return a single user with id `user_id`."""
        parser = reqparse.RequestParser()
        parser.add_argument("Authorization", location="headers")
        parser.add_argument("nextPage", type=str)
        parser.add_argument("primaryEmail", type=str)
        parser.add_argument("filterDisplay", type=str)
        args = parser.parse_args()

        filter_display = args.get("filterDisplay", None)
        primary_email = args.get("primaryEmail", None)
        next_page = args.get("nextPage", None)
        scopes = get_scopes(args.get("Authorization"))

        if next_page is not None:
            nextPage = load_dirty_json(next_page)
        else:
            nextPage = None

        if transactions == "false":
            identity_vault = user.Profile(dynamodb_table, dynamodb_client, transactions=False)

        if transactions == "true":
            identity_vault = user.Profile(dynamodb_table, dynamodb_client, transactions=True)

        next_page_token = None
        if primary_email is None:
            result = identity_vault.all_by_page(next_page=nextPage, limit=25)
            next_page_token = result.get("LastEvaluatedKey")
        else:
            result = identity_vault.find_by_email(primary_email)
        v2_profiles = []

        for profile in result.get("Items"):
            vault_profile = json.loads(profile.get("profile"))
            v2_profile = User(user_structure_json=vault_profile)
            if "read:fullprofile" in scopes:
                # Assume someone has asked for all the data.
                logger.info(
                    "The provided token has access to all of the data.", extra={"query_args": args, "scopes": scopes}
                )
                pass
            else:
                # Assume the we are filtering falls back to public with no scopes
                logger.info("This is a limited scoped query.", extra={"query_args": args, "scopes": scopes})
                v2_profile.filter_scopes(scope_to_mozilla_data_classification(scopes))

            if "display:all" in scopes:
                logger.info("display:all in token not filtering profile.", extra={"query_args": args, "scopes": scopes})
            else:
                logger.info("display filtering engaged for query.", extra={"query_args": args, "scopes": scopes})
                v2_profile.filter_display(scope_to_display_level(scopes))

            if filter_display is not None:
                v2_profile.filter_display(DisplayLevelParms.map(filter_display))

            v2_profiles.append(v2_profile.as_dict())

        response = {"Items": v2_profiles, "nextPage": next_page_token}
        return jsonify(response)
Esempio n. 3
0
 def load_new_user_profile(self):
     """Return an instance of cis_profile User."""
     kinesis_data = self.event_record["kinesis"]["data"]
     user_profile = json.loads(base64.b64decode(kinesis_data))
     profile_v2_data = user_profile
     user_object = User(user_structure_json=profile_v2_data)
     return user_object
Esempio n. 4
0
 def load_old_user_profile(self):
     user_id = self._get_user_id_from_stream()
     vault_user = DynamoDbUser(self.dynamodb_table)
     search_result = vault_user.find_by_id(user_id)
     if len(search_result.get("Items")) > 0:
         profile_data = search_result.get("Items")[0]
         user_object = User(
             user_structure_json=json.loads(profile_data["profile"]))
         logger.info(
             "A prior integration has been found for user: {}".format(
                 user_id))
         return user_object
     else:
         logger.info("No user_id was matched for user: {}".format(user_id))
         # Return an empty cis_profile.user.User()
         return User()
Esempio n. 5
0
    def _update_attr_owned_by_cis(self, profile_json):
        """Updates the attributes owned by cisv2.  Takes profiles profile_json
        and returns a profile json with updated values and sigs."""

        # New up a a cis_profile object
        user = User(user_structure_json=profile_json)
        user.update_timestamp("last_modified")
        user.last_modified.value = user._get_current_utc_time()
        user.sign_attribute("last_modified", "cis")
Esempio n. 6
0
def getUser(id, find_by):
    """Return a single user with identifier using find_by."""
    id = urllib.parse.unquote(id)
    parser = reqparse.RequestParser()
    parser.add_argument("Authorization", location="headers")
    parser.add_argument("filterDisplay", type=str)
    parser.add_argument("active", type=str)
    args = parser.parse_args()
    scopes = get_scopes(args.get("Authorization"))
    filter_display = args.get("filterDisplay", None)

    if args.get("active") is not None and args.get("active").lower() == "false":
        active = False
    elif args.get("active") is not None and args.get("active").lower() == "any":
        active = None
    else:
        active = True

    if transactions == "false":
        identity_vault = user.Profile(dynamodb_table, dynamodb_client, transactions=False)

    if transactions == "true":
        identity_vault = user.Profile(dynamodb_table, dynamodb_client, transactions=True)

    result = find_by(identity_vault, id)

    if len(result["Items"]) > 0:
        vault_profile = result["Items"][0]["profile"]
        v2_profile = User(user_structure_json=json.loads(vault_profile))

        if v2_profile.active.value == active or active is None:
            if "read:fullprofile" in scopes:
                logger.debug(
                    "read:fullprofile in token not filtering based on scopes.",
                    extra={"query_args": args, "scopes": scopes},
                )
            else:
                v2_profile.filter_scopes(scope_to_mozilla_data_classification(scopes))

            if "display:all" in scopes:
                logger.debug(
                    "display:all in token not filtering profile based on display.",
                    extra={"query_args": args, "scopes": scopes},
                )
            else:
                v2_profile.filter_display(scope_to_display_level(scopes))

            if filter_display is not None:
                logger.debug(
                    "filter_display argument is passed, applying display level filter.", extra={"query_args": args}
                )
                v2_profile.filter_display(DisplayLevelParms.map(filter_display))

            return jsonify(v2_profile.as_dict())

    logger.debug("No user was found for the query", extra={"query_args": args, "scopes": scopes})
    return jsonify({})
Esempio n. 7
0
def getUser(id, find_by):
    """Return a single user with identifier using find_by."""
    id = urllib.parse.unquote(id)
    parser = reqparse.RequestParser()
    parser.add_argument("Authorization", location="headers")
    parser.add_argument("filterDisplay", type=str)
    args = parser.parse_args()
    scopes = get_scopes(args.get("Authorization"))
    filter_display = args.get("filterDisplay", None)

    if transactions == "false":
        identity_vault = user.Profile(dynamodb_table,
                                      dynamodb_client,
                                      transactions=False)

    if transactions == "true":
        identity_vault = user.Profile(dynamodb_table,
                                      dynamodb_client,
                                      transactions=True)

    result = find_by(identity_vault, id)

    if len(result["Items"]) > 0:
        vault_profile = result["Items"][0]["profile"]
        v2_profile = User(user_structure_json=json.loads(vault_profile))
        if "read:fullprofile" in scopes:
            logger.debug(
                "read:fullprofile in token returning the full user profile.")
        else:
            v2_profile.filter_scopes(
                scope_to_mozilla_data_classification(scopes))

        if "display:all" in scopes:
            logger.debug("display:all in token not filtering profile.")
        else:
            v2_profile.filter_display(scope_to_display_level(scopes))

        if filter_display is not None:
            v2_profile.filter_display(DisplayLevelParms.map(filter_display))

        return jsonify(v2_profile.as_dict())
    else:
        return jsonify({})
Esempio n. 8
0
    def get(self, primary_email):
        logger.info("Attempting to get public metadata for primary email: {}".format(primary_email))
        exists_in_cis = exists_in_ldap = False

        identity_vault = user.Profile(dynamodb_table, dynamodb_client, transactions=False)
        result = user.Profile.find_by_email(identity_vault, primary_email)

        if len(result["Items"]) > 0:
            vault_profile = result["Items"][0]["profile"]
            exists_in_cis = True
            exists_in_ldap = User(user_structure_json=json.loads(vault_profile)) \
                .as_dict()["access_information"]["ldap"]["values"] is not None

        return jsonify({
            "exists": {
                "cis": exists_in_cis,
                "ldap": exists_in_ldap,
            }
        })
Esempio n. 9
0
    def __init__(self, sequence_number=None, profile_json=None, **kwargs):
        self.connection_object = connect.AWS()
        self.identity_vault_client = None
        self.config = common.get_config()
        self.condition = "unknown"
        self.user_id = kwargs.get("user_id")
        self.user_uuid = kwargs.get("user_uuid")
        self.primary_email = kwargs.get("primary_email")
        self.primary_username = kwargs.get("primary_username")

        if self.user_id is None:
            logger.info("No user_id arg was passed for the payload. This is a new user or batch.")
            tmp_user = User(user_structure_json=profile_json)
            self.user_id = tmp_user.user_id.value
            self.condition = "create"

        if sequence_number is not None:
            self.sequence_number = str(sequence_number)
        else:
            self.sequence_number = str(uuid.uuid4().int)
Esempio n. 10
0
    def get(self):
        """Return a single user with id `user_id`."""
        parser = reqparse.RequestParser()
        parser.add_argument("Authorization", location="headers")
        parser.add_argument("nextPage", type=str)
        parser.add_argument("primaryEmail", type=str)
        parser.add_argument("filterDisplay", type=str)
        parser.add_argument("active", type=str)

        args = parser.parse_args()

        filter_display = args.get("filterDisplay", None)
        primary_email = args.get("primaryEmail", None)
        next_page = args.get("nextPage", None)
        scopes = get_scopes(args.get("Authorization"))

        logger.info(
            f"Attempting to get paginated users: primary_email:{primary_email}, next_page:{next_page}, "
            "filter_display:{filter_display}, scopes:{scopes}")

        if next_page is not None:
            nextPage = load_dirty_json(next_page)
        else:
            nextPage = None

        if transactions == "false":
            identity_vault = user.Profile(dynamodb_table,
                                          dynamodb_client,
                                          transactions=False)

        if transactions == "true":
            identity_vault = user.Profile(dynamodb_table,
                                          dynamodb_client,
                                          transactions=True)

        next_page_token = None
        if primary_email is None:
            result = identity_vault.all_by_page(next_page=nextPage)
            next_page_token = result.get("LastEvaluatedKey")
        else:
            result = identity_vault.find_by_email(primary_email)
        v2_profiles = []

        if args.get("active") is not None and args.get(
                "active").lower() == "false":
            active = False
        else:
            active = True  # Support returning only active users by default.

        for profile in result.get("Items"):
            vault_profile = json.loads(profile.get("profile"))
            v2_profile = User(user_structure_json=vault_profile)

            # This must be a pre filtering check because mutation is real.
            if v2_profile.active.value == active:
                allowed_in_list = True
            else:
                allowed_in_list = False

            if "read:fullprofile" in scopes:
                # Assume someone has asked for all the data.
                logger.debug(
                    "The provided token has access to all of the data.",
                    extra={
                        "query_args": args,
                        "scopes": scopes
                    })
                pass
            else:
                # Assume the we are filtering falls back to public with no scopes
                logger.debug("This is a limited scoped query.",
                             extra={
                                 "query_args": args,
                                 "scopes": scopes
                             })
                v2_profile.filter_scopes(
                    scope_to_mozilla_data_classification(scopes))

            if "display:all" in scopes:
                logger.debug("display:all in token not filtering profile.",
                             extra={
                                 "query_args": args,
                                 "scopes": scopes
                             })
            else:
                logger.debug("display filtering engaged for query.",
                             extra={
                                 "query_args": args,
                                 "scopes": scopes
                             })
                v2_profile.filter_display(scope_to_display_level(scopes))

            if filter_display is not None:
                v2_profile.filter_display(
                    DisplayLevelParms.map(filter_display))

            if allowed_in_list:
                v2_profiles.append(v2_profile.as_dict())
            else:
                logger.debug(
                    "Skipping adding this profile to the list of profiles because it is: {}"
                    .format(active))
                pass

        response = {"Items": v2_profiles, "nextPage": next_page_token}
        return jsonify(response)
Esempio n. 11
0
 def test_profile_env(self):
     os.environ[
         "CIS_DISCOVERY_URL"] = "https://auth.allizom.org/.well-known/mozilla-iam"
     u = User()
     assert u._User__well_known.discovery_url == "https://auth.allizom.org/.well-known/mozilla-iam"
Esempio n. 12
0
    def _search_and_merge(self, user_id, cis_profile_object):
        """
        Search for an existing user in the vault for the given profile
        If one exist, merge the given profile with the existing user
        If not, return the given profile

        WARNING: This function also verifies the publishers are valid, as this verification requires knowledge of the
        incoming user profile, profile in the vault, and resulting merged profile.

        @cis_profile_object cis_profile.User object of an incoming user
        @user_id str the user id of cis_profile_object

        Returns a cis_profile.User object
        """

        try:
            self._connect()
            vault = user.Profile(self.identity_vault_client.get("table"),
                                 self.identity_vault_client.get("client"))
            res = vault.find_by_id(user_id)
            logger.info("Search user in vault results: {}".format(
                len(res["Items"])))

        except Exception as e:
            logger.error(
                "Problem finding user profile in identity vault due to: {}".
                format(e))
            res = {"Items": []}

        if len(res["Items"]) > 0:
            # This profile exists in the vault and will be merged and it's publishers verified
            self.condition = "update"
            logger.info(
                "A record already exists in the identity vault for user: {}.".
                format(user_id),
                extra={"user_id": user_id},
            )

            old_user_profile = User(
                user_structure_json=json.loads(res["Items"][0]["profile"]))
            new_user_profile = copy.deepcopy(old_user_profile)
            difference = new_user_profile.merge(cis_profile_object)

            if ((difference == ["user_id"]) or
                (new_user_profile.active.value == old_user_profile.active.value
                 and difference == ["active"]) or (len(difference) == 0)
                    or ((new_user_profile.active.value
                         == old_user_profile.active.value and
                         (new_user_profile.uuid.value is None
                          and new_user_profile.primary_username.value is None))
                        and sorted(difference) == sorted(
                            ["active", "uuid", "primary_username"]))):
                logger.info(
                    "Will not merge user as there were no difference found with the vault instance of the user"
                    .format(extra={"user_id": user_id}))
                return None
            else:
                logger.info(
                    "Differences found during merge: {}".format(difference),
                    extra={"user_id": user_id})

            # XXX This is safe but this is not great. Probably should have a route to deactivate since its a CIS
            # attribute.
            if difference == ["active"]:
                logger.info(
                    "Partial update only contains the `active` attribute, bypassing publisher verification as CIS "
                    "will enforce this check on it's own'",
                    extra={"user_id": user_id},
                )
                return new_user_profile

            if self.config("verify_publishers", namespace="cis") == "true":
                logger.info("Verifying publishers", extra={"user_id": user_id})
                try:
                    new_user_profile.verify_all_publishers(old_user_profile)
                except Exception as e:
                    logger.error(
                        "The merged profile failed to pass publisher verification",
                        extra={
                            "user_id": user_id,
                            "profile": new_user_profile.as_dict(),
                            "reason": e,
                            "trace": format_exc(),
                        },
                    )
                    raise VerificationError(
                        {
                            "code": "invalid_publisher",
                            "description": "{}".format(e)
                        }, 403)
            else:
                logger.warning(
                    "Bypassing profile publisher verification due to `verify_publishers` setting being false",
                    extra={"user_id": user_id},
                )
            return new_user_profile
        else:
            # This is a new profile, set uuid and primary_username and verify.
            self.condition = "create"
            logger.info(
                "A record does not exist in the identity vault for user: {}.".
                format(user_id),
                extra={"user_id": user_id},
            )
            # We raise an exception if uuid or primary_username is already set. This must not happen.
            if cis_profile_object.uuid.value is not None or cis_profile_object.primary_username.value is not None:
                logger.error(
                    "Trying to create profile, but uuid ({}) or primary_username ({}) was already "
                    "set".format(cis_profile_object.uuid.value,
                                 cis_profile_object.primary_username.value),
                    extra={
                        "user_id": user_id,
                        "profile": cis_profile_object.as_dict()
                    },
                )
                raise VerificationError(
                    {
                        "code":
                        "uuid_or_primary_username_set",
                        "description":
                        "The fields primary_username or uuid have been set in a new profile.",
                    },
                    403,
                )
            cis_profile_object.initialize_uuid_and_primary_username()
            cis_profile_object.sign_attribute("uuid", "cis")
            cis_profile_object.sign_attribute("primary_username", "cis")

            if self.config("verify_publishers", namespace="cis") == "true":
                logger.info("Verifying publishers", extra={"user_id": user_id})
                try:
                    cis_profile_object.verify_all_publishers(
                        cis_profile.User())
                except Exception as e:
                    logger.error(
                        "The profile failed to pass publisher verification",
                        extra={
                            "user_id": user_id,
                            "profile": cis_profile_object.as_dict(),
                            "reason": e,
                            "trace": format_exc(),
                        },
                    )
                    raise VerificationError(
                        {
                            "code": "invalid_publisher",
                            "description": "{}".format(e)
                        }, 403)
            else:
                logger.warning(
                    "Bypassing profile publisher verification due to `verify_publishers` setting being false",
                    extra={"user_id": user_id},
                )
            return cis_profile_object