Exemple #1
0
    def refresh_token_grant(old_refresh_token: UUID):
        """Implements logic for refresh token grant."""

        # check token exists in database and get expire time and user id
        collection = RefreshTokens()
        old_token_document = collection.find_one(
            {"token": old_refresh_token}, {"expire_time": 1, "user_id": 1}
        )
        if old_token_document is None:
            raise InvalidGrant("Refresh token is invalid.")

        # check token is not expired
        expire_time = old_token_document["expire_time"]
        if expire_time < datetime.now():
            raise InvalidGrant("Refresh token is expired.")

        # check user exists
        user_id = old_token_document["user_id"]
        user = Users().find_one({"_id": user_id}, {"password_hash": 0})
        if user is None:
            raise InvalidGrant("Refresh token is invalid.")

        # generate token
        access_token = LoadedAccessToken(
            user["_id"], user["username"], user.get("scope", {})
        ).encode()
        refresh_token = OAuth2.generate_refresh_token(user["_id"])

        # delete old refresh token from database
        collection.delete_one({"token": old_refresh_token})
        collection.delete_many({"expire_time": {"$lte": datetime.now()}})

        return OAuth2.success_response(access_token, refresh_token)
Exemple #2
0
    def get(self, username: str, token: AccessToken.Payload):
        # if user in url is not user in token, check user permission
        if username != token.username:
            if not token.get_permission("users", "ssh_keys"):
                raise errors.NotEnoughPrivilege()

        user = Users().find_one({"username": username}, {"ssh_keys": 1})
        if user is None:
            raise errors.NotFound()

        ssh_keys = user.get("ssh_keys", [])
        return jsonify(ssh_keys)
Exemple #3
0
    def get(self, token: AccessToken.Payload, username: str):
        # if user in url is not user in token, check user permission
        if username != token.username:
            if not token.get_permission("users", "read"):
                raise errors.NotEnoughPrivilege()

        # find user based on _id or username
        user = Users().find_one(
            {"username": username},
            {"_id": 0, "username": 1, "email": 1, "scope": 1, "ssh_keys": 1},
        )

        if user is None:
            raise errors.NotFound()

        user["role"] = get_role_for(user.get("scope"))

        return jsonify(user)
Exemple #4
0
    def get(self, username: str, fingerprint: str):
        # list of permission to test the matching user against
        requested_permissions = request.args.getlist("with_permission") or []

        query = {}
        # request using `-` as username searchs on all users
        if username != "-":
            query.update({"username": username})

        query.update({"ssh_keys.fingerprint": fingerprint})

        # database
        user = Users().find_one(
            query,
            {
                "username": 1,
                "scope": 1,
                "ssh_keys": 1,
            },
        )

        # no user means no matching SSH key for fingerprint
        if not user:
            raise errors.NotFound()

        key = [
            key for key in user["ssh_keys"]
            if key["fingerprint"] == fingerprint
        ][-1]

        for permission in requested_permissions:
            namespace, perm_name = permission.split(".", 1)
            if not user.get("scope", {}).get(namespace, {}).get(perm_name):
                raise errors.NotEnoughPrivilege(permission)

        payload = {
            "username": user["username"],
            "key": key["key"],
            "type": key["type"],
            "name": key["name"],
        }

        return jsonify(payload)
Exemple #5
0
    def password_grant(username: str, password: str):
        """Implements logic for password grant."""

        # check user exists
        user = Users().find_one({"username": username})
        if user is None:
            raise InvalidGrant("Username or password is invalid.")

        # check password is valid
        password_hash = user.pop("password_hash")
        is_valid = check_password_hash(password_hash, password)
        if not is_valid:
            raise InvalidGrant("Username or password is invalid.")

        # generate token
        access_token = LoadedAccessToken(
            user["_id"], user["username"], user.get("scope", {})
        ).encode()
        refresh_token = OAuth2.generate_refresh_token(user["_id"])

        return OAuth2.success_response(access_token, refresh_token)
Exemple #6
0
    def post(self, username: str, token: AccessToken.Payload):
        # if user in url is not user in token, not allowed to add ssh keys
        if username != token.username:
            if not token.get_permission("users", "ssh_keys"):
                raise errors.NotEnoughPrivilege()

        try:
            request_json = KeySchema().load(request.get_json())
        except ValidationError as e:
            raise errors.InvalidRequestJSON(e.messages)

        # parse public key string
        key = request_json["key"]
        key_parts = key.split(" ")
        if len(key_parts) >= 2:
            key = key_parts[1]

        # compute fingerprint
        try:
            rsa_key = paramiko.RSAKey(data=base64.b64decode(key))
            fingerprint = binascii.hexlify(rsa_key.get_fingerprint()).decode()
        except (binascii.Error, paramiko.SSHException):
            raise errors.BadRequest("Invalid RSA key")

        # get existing ssh key fingerprints
        query = {"username": username}
        user = Users().find_one(query, {"ssh_keys.fingerprint": 1})
        if user is None:
            raise errors.NotFound()

        # find out if new ssh already exist
        fingerprints = set(
            [ssh_key["fingerprint"] for ssh_key in user.get("ssh_keys", [])]
        )
        if fingerprint in fingerprints:
            raise errors.BadRequest("SSH key already exists")

        # add new ssh key to database
        ssh_key = {
            "name": request_json["name"],
            # ssh-keygen -l -f xxx.pub -E md5 - just data, without dots
            "fingerprint": fingerprint,
            "key": key,
            "type": "RSA",
            "added": datetime.now(),
            "last_used": None,
        }

        # get PKCS8 - ssh-keygen -e -f xxx.priv -m PKCS8
        with tempfile.NamedTemporaryFile(mode="w", suffix=".pub", delete=False) as fp:
            fp.write("ssh-rsa {} {}\n".format(ssh_key["key"], ssh_key["name"]))
        keygen = subprocess.run(
            ["/usr/bin/ssh-keygen", "-e", "-f", fp.name, "-m", "PKCS8"],
            capture_output=True,
            text=True,
        )
        if keygen.returncode != 0:
            raise errors.BadRequest(keygen.stderr)
        ssh_key.update({"pkcs8_key": keygen.stdout})
        os.unlink(fp.name)

        Users().update_one(query, {"$push": {"ssh_keys": ssh_key}})

        return Response(status=HTTPStatus.CREATED)