Exemplo n.º 1
0
    def update_user(self, user_info):
        """
        Change the user name and/or password.

        :param user_info: user info modifications
        """
        uid = user_info["_id"]
        user_data = self.db.get_one("users",
                                    {BaseTopic.id_field("users", uid): uid})
        BaseTopic.format_on_edit(user_data, user_info)
        # User Name
        usnm = user_info.get("username")
        if usnm:
            user_data["username"] = usnm
        # If password is given and is not already encripted
        pswd = user_info.get("password")
        if pswd and (len(pswd) != 64 or not re.match(
                '[a-fA-F0-9]*', pswd)):  # TODO: Improve check?
            salt = uuid4().hex
            if "_admin" not in user_data:
                user_data["_admin"] = {}
            user_data["_admin"]["salt"] = salt
            user_data["password"] = sha256(
                pswd.encode('utf-8') + salt.encode('utf-8')).hexdigest()
        # Project-Role Mappings
        # TODO: Check that user_info NEVER includes "project_role_mappings"
        if "project_role_mappings" not in user_data:
            user_data["project_role_mappings"] = []
        for prm in user_info.get("add_project_role_mappings", []):
            user_data["project_role_mappings"].append(prm)
        for prm in user_info.get("remove_project_role_mappings", []):
            for pidf in ["project", "project_name"]:
                for ridf in ["role", "role_name"]:
                    try:
                        user_data["project_role_mappings"].remove({
                            "role":
                            prm[ridf],
                            "project":
                            prm[pidf]
                        })
                    except KeyError:
                        pass
                    except ValueError:
                        pass
        idf = BaseTopic.id_field("users", uid)
        self.db.set_one("users", {idf: uid}, user_data)
        if user_info.get("remove_project_role_mappings"):
            self.db.del_list("tokens",
                             {"user_id" if idf == "_id" else idf: uid})
            self.token_cache.clear()
Exemplo n.º 2
0
    def delete_project(self, project_id):
        """
        Delete a project.

        :param project_id: project identifier.
        :raises AuthconnOperationException: if project deletion failed.
        """
        filter_q = {BaseTopic.id_field("projects", project_id): project_id}
        r = self.db.del_one("projects", filter_q)
        return r
Exemplo n.º 3
0
    def update_project(self, project_id, project_info):
        """
        Change the name of a project

        :param project_id: project to be changed
        :param project_info: full project info
        :return: None
        :raises AuthconnOperationException: if project update failed.
        """
        self.db.set_one(
            "projects",
            {BaseTopic.id_field("projects", project_id): project_id},
            project_info)
Exemplo n.º 4
0
 def get_project(self, _id, fail=True):
     """
     Get one project
     :param _id:  id or name
     :param fail: True to raise exception on not found. False to return None on not found
     :return: dictionary with the project information
     """
     filt = {BaseTopic.id_field("projects", _id): _id}
     projs = self.get_project_list(filt)
     if not projs:
         if fail:
             raise AuthconnNotFoundException("project with {} not found".format(filt))
         else:
             return None
     return projs[0]
Exemplo n.º 5
0
 def get_role(self, _id, fail=True):
     """
     Get one role
     :param _id: id or name
     :param fail: True to raise exception on not found. False to return None on not found
     :return: dictionary with the role information
     """
     filt = {BaseTopic.id_field("roles", _id): _id}
     roles = self.get_role_list(filt)
     if not roles:
         if fail:
             raise AuthconnNotFoundException("Role with {} not found".format(filt))
         else:
             return None
     return roles[0]
Exemplo n.º 6
0
 def get_user(self, _id, fail=True):
     """
     Get one user
     :param _id:  id or name
     :param fail: True to raise exception on not found. False to return None on not found
     :return: dictionary with the user information
     """
     filt = {BaseTopic.id_field("users", _id): _id}
     users = self.get_user_list(filt)
     if not users:
         if fail:
             raise AuthconnNotFoundException("User with {} not found".format(filt), http_code=HTTPStatus.NOT_FOUND)
         else:
             return None
     return users[0]
Exemplo n.º 7
0
    def get_user_list(self, filter_q=None):
        """
        Get user list.

        :param filter_q: dictionary to filter user list by name (username is also admited) and/or _id
        :return: returns a list of users.
        """
        filt = filter_q or {}
        if "name" in filt:
            filt["username"] = filt["name"]
            del filt["name"]
        users = self.db.get_list("users", filt)
        project_id_name = {}
        role_id_name = {}
        for user in users:
            prms = user.get("project_role_mappings")
            projects = user.get("projects")
            if prms:
                projects = []
                # add project_name and role_name. Generate projects for backward compatibility
                for prm in prms:
                    project_id = prm["project"]
                    if project_id not in project_id_name:
                        pr = self.db.get_one("projects", {
                            BaseTopic.id_field("projects", project_id):
                            project_id
                        },
                                             fail_on_empty=False)
                        project_id_name[
                            project_id] = pr["name"] if pr else None
                    prm["project_name"] = project_id_name[project_id]
                    if prm["project_name"] not in projects:
                        projects.append(prm["project_name"])

                    role_id = prm["role"]
                    if role_id not in role_id_name:
                        role = self.db.get_one(
                            "roles",
                            {BaseTopic.id_field("roles", role_id): role_id},
                            fail_on_empty=False)
                        role_id_name[role_id] = role["name"] if role else None
                    prm["role_name"] = role_id_name[role_id]
                user["projects"] = projects  # for backward compatibility
            elif projects:
                # user created with an old version. Create a project_role mapping with role project_admin
                user["project_role_mappings"] = []
                role = self.db.get_one("roles", {
                    BaseTopic.id_field("roles", "project_admin"):
                    "project_admin"
                })
                for p_id_name in projects:
                    pr = self.db.get_one(
                        "projects",
                        {BaseTopic.id_field("projects", p_id_name): p_id_name})
                    prm = {
                        "project": pr["_id"],
                        "project_name": pr["name"],
                        "role_name": "project_admin",
                        "role": role["_id"]
                    }
                    user["project_role_mappings"].append(prm)
            else:
                user["projects"] = []
                user["project_role_mappings"] = []

        return users
Exemplo n.º 8
0
    def authenticate(self, user, password, project=None, token_info=None):
        """
        Authenticate a user using username/password or previous token_info plus project; its creates a new token

        :param user: user: name, id or None
        :param password: password or None
        :param project: name, id, or None. If None first found project will be used to get an scope token
        :param token_info: previous token_info to obtain authorization
        :param remote: remote host information
        :return: the scoped token info or raises an exception. The token is a dictionary with:
            _id:  token string id,
            username: username,
            project_id: scoped_token project_id,
            project_name: scoped_token project_name,
            expires: epoch time when it expires,
        """

        now = time()
        user_content = None

        # Try using username/password
        if user:
            user_rows = self.db.get_list(
                "users", {BaseTopic.id_field("users", user): user})
            if user_rows:
                user_content = user_rows[0]
                salt = user_content["_admin"]["salt"]
                shadow_password = sha256(
                    password.encode('utf-8') +
                    salt.encode('utf-8')).hexdigest()
                if shadow_password != user_content["password"]:
                    user_content = None
            if not user_content:
                raise AuthException("Invalid username/password",
                                    http_code=HTTPStatus.UNAUTHORIZED)
        elif token_info:
            user_rows = self.db.get_list("users",
                                         {"username": token_info["username"]})
            if user_rows:
                user_content = user_rows[0]
            else:
                raise AuthException("Invalid token",
                                    http_code=HTTPStatus.UNAUTHORIZED)
        else:
            raise AuthException(
                "Provide credentials: username/password or Authorization Bearer token",
                http_code=HTTPStatus.UNAUTHORIZED)

        token_id = ''.join(
            random_choice(
                'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
            ) for _ in range(0, 32))

        # projects = user_content.get("projects", [])
        prm_list = user_content.get("project_role_mappings", [])

        if not project:
            project = prm_list[0]["project"] if prm_list else None
        if not project:
            raise AuthException("can't find a default project for this user",
                                http_code=HTTPStatus.UNAUTHORIZED)

        projects = [prm["project"] for prm in prm_list]

        proj = self.db.get_one(
            "projects", {BaseTopic.id_field("projects", project): project})
        project_name = proj["name"]
        project_id = proj["_id"]
        if project_name not in projects and project_id not in projects:
            raise AuthException(
                "project {} not allowed for this user".format(project),
                http_code=HTTPStatus.UNAUTHORIZED)

        # TODO remove admin, this vill be used by roles RBAC
        if project_name == "admin":
            token_admin = True
        else:
            token_admin = proj.get("admin", False)

        # add token roles
        roles = []
        roles_list = []
        for prm in prm_list:
            if prm["project"] in [project_id, project_name]:
                role = self.db.get_one(
                    "roles",
                    {BaseTopic.id_field("roles", prm["role"]): prm["role"]})
                rid = role["_id"]
                if rid not in roles:
                    rnm = role["name"]
                    roles.append(rid)
                    roles_list.append({"name": rnm, "id": rid})
        if not roles_list:
            rid = self.db.get_one("roles", {"name": "project_admin"})["_id"]
            roles_list = [{"name": "project_admin", "id": rid}]

        new_token = {
            "issued_at": now,
            "expires": now + 3600,
            "_id": token_id,
            "id": token_id,
            "project_id": proj["_id"],
            "project_name": proj["name"],
            "username": user_content["username"],
            "user_id": user_content["_id"],
            "admin": token_admin,
            "roles": roles_list,
        }

        self.token_cache[token_id] = new_token
        self.db.create("tokens", new_token)
        return deepcopy(new_token)