async def create_new_role(request):
    """Create a new role."""
    required_fields = ["name", "administrators", "owners"]
    utils.validate_fields(required_fields, request.json)

    conn = await db_utils.create_connection(
        request.app.config.DB_HOST,
        request.app.config.DB_PORT,
        request.app.config.DB_NAME,
    )
    response = await roles_query.roles_search_duplicate(conn, request.json.get("name"))
    if not response:
        txn_key, txn_user_id = await utils.get_transactor_key(request)
        role_id = str(uuid4())
        batch_list = Role().batch_list(
            signer_keypair=txn_key,
            signer_user_id=txn_user_id,
            name=request.json.get("name"),
            role_id=role_id,
            metadata=request.json.get("metadata"),
            admins=request.json.get("administrators"),
            owners=request.json.get("owners"),
            description=request.json.get("description"),
        )
        await utils.send(
            request.app.config.VAL_CONN, batch_list, request.app.config.TIMEOUT
        )
        return create_role_response(request, role_id)
    raise ApiBadRequest(
        "Error: could not create this role because role name has been taken or already exists"
    )
예제 #2
0
async def add_role_task(request, role_id):
    """Add a task to a role."""
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)
    txn_key, txn_user_id = await utils.get_transactor_key(request)
    proposal_id = str(uuid4())
    conn = await create_connection()
    approver = await fetch_relationships(
        "task_owners", "task_id", request.json.get("id")
    ).run(conn)
    conn.close()
    batch_list = Role().task.propose.batch_list(
        signer_keypair=txn_key,
        signer_user_id=txn_user_id,
        proposal_id=proposal_id,
        role_id=role_id,
        task_id=request.json.get("id"),
        reason=request.json.get("reason"),
        metadata=request.json.get("metadata"),
        assigned_approver=approver,
    )
    await utils.send(
        request.app.config.VAL_CONN, batch_list, request.app.config.TIMEOUT
    )
    return json({"proposal_id": proposal_id})
async def add_role_member(request, role_id):
    """Add a member to a role."""
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)
    txn_key, txn_user_id = await utils.get_transactor_key(request)
    proposal_id = str(uuid4())
    batch_list = Role().member.propose.batch_list(
        signer_keypair=txn_key,
        signer_user_id=txn_user_id,
        proposal_id=proposal_id,
        role_id=role_id,
        pack_id=request.json.get("pack_id"),
        next_id=request.json.get("id"),
        reason=request.json.get("reason"),
        metadata=request.json.get("metadata"),
    )
    batch_status = await utils.send(
        request.app.config.VAL_CONN,
        batch_list,
        request.app.config.TIMEOUT,
        request.json.get("tracker") and True,
    )
    if request.json.get("tracker"):
        return utils.create_tracker_response("batch_status", batch_status)
    return json({"proposal_id": proposal_id})
예제 #4
0
async def update_password(request):
    """Update a user's password.  The request must come from an admin.
    Args:
        request:
            obj: a request object
    """
    log_request(request)
    env = Env()
    if not env.int("ENABLE_NEXT_BASE_USE"):
        raise ApiDisabled("Not a valid action. Source not enabled")
    required_fields = ["next_id", "password"]
    validate_fields(required_fields, request.json)
    txn_key, txn_user_id = await get_transactor_key(request)
    is_admin = await check_admin_status(txn_user_id)
    if not is_admin:
        raise ApiBadRequest("You are not a NEXT Administrator.")

    salt = hashlib.sha256(os.urandom(60)).hexdigest().encode("utf-8")
    password = request.json.get("password").encode("utf-8")
    hashed_password = hashlib.pbkdf2_hmac("sha256", password, salt,
                                          100000).hex()

    conn = await create_connection()
    await users_query.update_user_password(conn,
                                           request.json.get("next_id"),
                                           hashed_password=hashed_password,
                                           salt=salt)
    conn.close()
    return json({"message": "Password successfully updated"})
예제 #5
0
async def add_role_owner(request, role_id):
    """Add an owner to a role."""
    log_request(request)
    env = Env()
    if not env.int("ENABLE_NEXT_BASE_USE"):
        raise ApiDisabled("Not a valid action. Source not enabled")
    required_fields = ["id"]
    validate_fields(required_fields, request.json)

    txn_key, txn_user_id = await get_transactor_key(request)
    proposal_id = str(uuid4())
    conn = await create_connection()
    approver = await fetch_relationships("role_admins", "role_id", role_id).run(conn)
    conn.close()
    batch_list = Role().owner.propose.batch_list(
        signer_keypair=txn_key,
        signer_user_id=txn_user_id,
        proposal_id=proposal_id,
        role_id=role_id,
        next_id=request.json.get("id"),
        reason=request.json.get("reason"),
        metadata=request.json.get("metadata"),
        assigned_approver=approver,
    )
    await send(request.app.config.VAL_CONN, batch_list, request.app.config.TIMEOUT)
    if isinstance(approver, list):
        for user in approver:
            await send_notification(user, proposal_id)
    else:
        await send_notification(approver, proposal_id)
    return json({"proposal_id": proposal_id})
예제 #6
0
async def add_task_owner(request, task_id):
    """Propose add a task owner."""
    log_request(request)
    required_fields = ["id"]
    validate_fields(required_fields, request.json)

    txn_key, txn_user_id = await get_transactor_key(request)
    proposal_id = str(uuid4())
    conn = await create_connection()
    approver = await fetch_relationships("task_admins", "task_id",
                                         task_id).run(conn)
    conn.close()
    batch_list = Task().owner.propose.batch_list(
        signer_keypair=txn_key,
        signer_user_id=txn_user_id,
        proposal_id=proposal_id,
        task_id=task_id,
        next_id=request.json.get("id"),
        reason=request.json.get("reason"),
        metadata=request.json.get("metadata"),
        assigned_approver=approver,
    )
    await send(request.app.config.VAL_CONN, batch_list,
               request.app.config.TIMEOUT)
    return json({"proposal_id": proposal_id})
예제 #7
0
async def chatbot_webhook(request):
    """Webhook enabling the chatbot engine to execute RBAC actions
    where tracker is the full JSON representation of the state of a
    given conversation"""
    required_fields = ["tracker", "sender_id"]
    utils.validate_fields(required_fields, request.json)
    return await execute_action_add_role_member(request)
예제 #8
0
async def update_manager(request, next_id):
    """Update a user's manager."""
    log_request(request)
    env = Env()
    if not env.int("ENABLE_NEXT_BASE_USE"):
        raise ApiDisabled("Not a valid action. Source not enabled")
    required_fields = ["id"]
    validate_fields(required_fields, request.json)
    txn_key, txn_user_id = await get_transactor_key(request)
    proposal_id = str(uuid4())
    if await check_admin_status(txn_user_id):
        conn = await create_connection()
        next_admins_list = await users_query.get_next_admins(conn)
        conn.close()
        batch_list = User().manager.propose.batch_list(
            signer_keypair=txn_key,
            signer_user_id=txn_user_id,
            proposal_id=proposal_id,
            next_id=next_id,
            new_manager_id=request.json.get("id"),
            reason=request.json.get("reason"),
            metadata=request.json.get("metadata"),
            assigned_approver=next_admins_list,
        )
        await send(request.app.config.VAL_CONN, batch_list,
                   request.app.config.TIMEOUT)
        await send_notification(request.json.get("id"), proposal_id)
    else:
        raise ApiBadRequest("Proposal opener is not a Next Admin.")
    return json({"proposal_id": proposal_id})
예제 #9
0
async def batch_update_proposals(request):
    """Update multiple proposals"""
    required_fields = ["ids"]
    utils.validate_fields(required_fields, request.json)
    for proposal_id in request.json["ids"]:
        await update_proposal(request, proposal_id)
    return json({"proposal_ids": request.json["ids"]})
예제 #10
0
async def update_password(request):
    """Update a user's password.  The request must come from an admin.
    Args:
        request:
            obj: a request object
    """
    env = Env()
    next_enabled = env.int("ENABLE_NEXT_BASE_USE", 0)
    if not next_enabled:
        raise ApiBadRequest("This capability is not enabled for this mode.")
    required_fields = ["next_id", "password"]
    utils.validate_fields(required_fields, request.json)
    txn_key, txn_user_id = await utils.get_transactor_key(request)
    is_admin = await utils.check_admin_status(txn_user_id)
    if not is_admin:
        raise ApiBadRequest("You are not a NEXT Administrator.")
    hashed_pwd = hashlib.sha256(
        request.json.get("password").encode("utf-8")
    ).hexdigest()
    conn = await create_connection()
    await users_query.update_user_password(
        conn, request.json.get("next_id"), hashed_pwd
    )
    conn.close()
    return json({"message": "Password successfully updated"})
예제 #11
0
async def update_proposal(request, proposal_id):
    LOGGER.debug("update proposal %s\n%s", proposal_id, request.json)
    required_fields = ["reason", "status"]
    utils.validate_fields(required_fields, request.json)
    if request.json["status"] not in [Status.REJECTED, Status.APPROVED]:
        raise ApiBadRequest(
            "Bad Request: status must be either 'REJECTED' or 'APPROVED'")
    txn_key, txn_user_id = await utils.get_transactor_key(request=request)
    block = await utils.get_request_block(request)

    conn = await db_utils.create_connection(
        request.app.config.DB_HOST,
        request.app.config.DB_PORT,
        request.app.config.DB_NAME,
    )

    proposal_resource = await proposals_query.fetch_proposal_resource(
        conn, proposal_id=proposal_id, head_block_num=block.get("num"))
    conn.close()

    batch_list = PROPOSAL_TRANSACTION[proposal_resource.get("type")][
        request.json["status"]](
            signer_keypair=txn_key,
            signer_user_id=txn_user_id,
            proposal_id=proposal_id,
            object_id=proposal_resource.get("object"),
            related_id=proposal_resource.get("target"),
            reason=request.json.get("reason"),
        )
    await utils.send(request.app.config.VAL_CONN, batch_list,
                     request.app.config.TIMEOUT)
    return json({"proposal_id": proposal_id})
예제 #12
0
async def authorize(request):
    """ User login (authorization)
    """
    required_fields = ["id", "password"]
    utils.validate_fields(required_fields, request.json)
    password = request.json.get("password")
    hashed_pwd = hashlib.sha256(password.encode("utf-8")).hexdigest()
    auth_info = await auth_query.fetch_info_by_username(request)
    email = auth_info.get("email")
    if auth_info.get("hashed_password") is None:
        if request.app.config.DEMO_MODE:
            token = generate_api_key(
                request.app.config.SECRET_KEY, auth_info.get("user_id")
            )
            return utils.create_authorization_response(
                token,
                {
                    "message": "Authorization (demo mode) successful",
                    "user_id": auth_info.get("user_id"),
                },
            )
        if not email:
            raise ApiUnauthorized(
                "Unauthorized: No password nor email is set on this account"
            )
        # TODO: send email confirmation with password set link
        raise ApiUnauthorized("Unauthorized: No password is set")
    if auth_info.get("hashed_password") != hashed_pwd:
        # TODO: rate limit password attempts
        raise ApiUnauthorized("Unauthorized: Incorrect password")
    token = generate_api_key(request.app.config.SECRET_KEY, auth_info.get("user_id"))
    return utils.create_authorization_response(
        token,
        {"message": "Authorization successful", "user_id": auth_info.get("user_id")},
    )
예제 #13
0
async def create_corpuser(request):
    """Create a new CORP user."""
    required_fields = ["id", "password"]
    utils.validate_fields(required_fields, request.json)
    log_request(request, True)

    env = Env()
    username = env("ADAPI_USERNAME")
    password = env("ADAPI_PASSWORD")

    auth = aiohttp.BasicAuth(login=username, password=password)
    url = ADAPI_REST_ENDPOINT + "?command=new-corpuser"
    data = {
        "ntid": request.json.get("id"),
        "userName": request.json.get("id"),
        "password": request.json.get("password"),
    }
    conn = aiohttp.TCPConnector(
        limit=request.app.config.AIOHTTP_CONN_LIMIT,
        ttl_dns_cache=request.app.config.AIOHTTP_DNS_TTL,
        verify_ssl=False,
    )
    async with aiohttp.ClientSession(connector=conn, auth=auth) as session:
        async with session.post(url=url, json=data) as response:
            data = await response.read()
            res = json.loads(data.decode("utf-8"))
            if res.get("success") == "false":
                raise ApiBadRequest("Invalid CORP account request.")
            return sanic_json({"data": {"message": "CORP account request successful."}})
예제 #14
0
async def update_proposal(request, proposal_id):
    required_fields = ["reason", "status"]
    utils.validate_fields(required_fields, request.json)
    if request.json["status"] not in [Status.REJECTED, Status.APPROVED]:
        raise ApiBadRequest(
            "Bad Request: status must be either 'REJECTED' or 'APPROVED'")
    txn_key = await utils.get_transactor_key(request=request)
    block = await utils.get_request_block(request)
    proposal_resource = await proposals_query.fetch_proposal_resource(
        request.app.config.DB_CONN,
        proposal_id=proposal_id,
        head_block_num=block.get("num"),
    )

    batch_list, _ = PROPOSAL_TRANSACTION[proposal_resource.get("type")][
        request.json["status"]](
            txn_key,
            request.app.config.BATCHER_KEY_PAIR,
            proposal_id,
            proposal_resource.get("object"),
            proposal_resource.get("target"),
            request.json.get("reason"),
        )
    await utils.send(request.app.config.VAL_CONN, batch_list,
                     request.app.config.TIMEOUT)
    return json({"proposal_id": proposal_id})
예제 #15
0
async def update_manager(request, user_id):
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)

    txn_key, txn_user_id = await utils.get_transactor_key(request)
    proposal_id = str(uuid4())
    batch_list = rbac.user.manager.propose.batch_list(
        signer_keypair=txn_key,
        signer_user_id=txn_user_id,
        proposal_id=proposal_id,
        user_id=user_id,
        new_manager_id=request.json.get("id"),
        reason=request.json.get("reason"),
        metadata=request.json.get("metadata"),
    )

    conn = await db_utils.create_connection(
        request.app.config.DB_HOST,
        request.app.config.DB_PORT,
        request.app.config.DB_NAME,
    )

    await utils.send(conn, batch_list, request.app.config.TIMEOUT)

    conn.close()

    return json({"proposal_id": proposal_id})
예제 #16
0
async def add_pack_role(request, pack_id):
    """Add roles to a pack"""
    required_fields = ["roles"]
    utils.validate_fields(required_fields, request.json)
    await packs_query.add_roles(request.app.config.DB_CONN, pack_id,
                                request.json.get("roles"))
    return json({"roles": request.json.get("roles")})
async def create_new_pack(request):
    """Create a new pack"""
    required_fields = ["owners", "name", "roles"]
    utils.validate_fields(required_fields, request.json)
    conn = await db_utils.create_connection(
        request.app.config.DB_HOST,
        request.app.config.DB_PORT,
        request.app.config.DB_NAME,
    )
    response = await packs_query.packs_search_duplicate(
        conn, request.json.get("name"))
    if not response:
        pack_id = str(uuid4())
        await packs_query.create_pack_resource(
            conn,
            pack_id,
            request.json.get("owners"),
            request.json.get("name"),
            request.json.get("description"),
        )
        await packs_query.add_roles(conn, pack_id, request.json.get("roles"))
        conn.close()
        return create_pack_response(request, pack_id)
    raise ApiBadRequest(
        "Error: could not create this pack because pack name has been taken or already exists"
    )
예제 #18
0
async def update_proposal(request, proposal_id):
    """Update proposal."""
    log_request(request)
    LOGGER.debug("update proposal %s\n%s", proposal_id, request.json)
    required_fields = ["reason", "status"]
    validate_fields(required_fields, request.json)
    if request.json["status"] not in ("REJECTED", "APPROVED"):
        raise ApiBadRequest(
            "Bad Request: status must be either 'REJECTED' or 'APPROVED'"
        )
    txn_key, txn_user_id = await get_transactor_key(request=request)

    conn = await create_connection()
    proposal_resource = await proposals_query.fetch_proposal_resource(
        conn, proposal_id=proposal_id
    )
    approvers_list = await compile_proposal_resource(conn, proposal_resource)
    conn.close()
    if txn_user_id not in approvers_list["approvers"]:
        raise ApiUnauthorized(
            "Bad Request: You don't have the authorization to APPROVE or REJECT the proposal"
        )
    batch_list = PROPOSAL_TRANSACTION[proposal_resource.get("type")][
        request.json["status"]
    ].batch_list(
        signer_keypair=txn_key,
        signer_user_id=txn_user_id,
        proposal_id=proposal_id,
        object_id=proposal_resource.get("object"),
        related_id=proposal_resource.get("target"),
        reason=request.json.get("reason"),
    )
    await send(request.app.config.VAL_CONN, batch_list, request.app.config.TIMEOUT)
    await send_notification(proposal_resource.get("target"), proposal_id)
    return json({"proposal_id": proposal_id})
예제 #19
0
async def create_new_role(request):
    """Create a new role."""
    required_fields = ["name", "administrators", "owners"]
    utils.validate_fields(required_fields, request.json)
    conn = await create_connection()
    role_title = " ".join(request.json.get("name").split())
    response = await roles_query.roles_search_duplicate(conn, role_title)
    if request.json.get("metadata") is None or request.json.get("metadata") == {}:
        set_metadata = {}
    else:
        set_metadata = request.json.get("metadata")
    set_metadata["sync_direction"] = "OUTBOUND"
    if not response:
        txn_key, txn_user_id = await utils.get_transactor_key(request)
        role_id = str(uuid4())
        batch_list = Role().batch_list(
            signer_keypair=txn_key,
            signer_user_id=txn_user_id,
            name=role_title,
            role_id=role_id,
            metadata=set_metadata,
            admins=request.json.get("administrators"),
            owners=request.json.get("owners"),
            description=request.json.get("description"),
        )
        await utils.send(
            request.app.config.VAL_CONN, batch_list, request.app.config.TIMEOUT
        )
        conn.close()
        if role_title != "NextAdmins":
            distinguished_name_formatted = "CN=" + role_title + "," + GROUP_BASE_DN
            data_formatted = {
                "created_date": r.now(),
                "distinguished_name": distinguished_name_formatted,
                "group_nickname": role_title,
                "group_types": -2147483646,
                "name": role_title,
                "remote_id": distinguished_name_formatted,
            }
            outbound_entry = {
                "data": data_formatted,
                "data_type": "group",
                "timestamp": r.now(),
                "provider_id": LDAP_DC,
            }
            # Insert to outbound_queue and close
            conn = await create_connection()
            role_outbound_resource = await roles_query.insert_to_outboundqueue(
                conn, outbound_entry
            )
            conn.close()
        else:
            LOGGER.info(
                "The role being created is NextAdmins, which is local to NEXT and will not be inserted into the outbound_queue."
            )
        return create_role_response(request, role_id)
    raise ApiBadRequest(
        "Error: Could not create this role because the role name already exists."
    )
예제 #20
0
async def chatbot(request, ws):
    while True:
        required_fields = ["do", "message", "user_id"]
        recv = json.loads(await ws.recv())

        utils.validate_fields(required_fields, recv)
        response = await create_response(request, recv)
        await ws.send(response)
예제 #21
0
async def feed(request, ws):
    """Socket feed enabling real-time notifications"""
    while True:
        required_fields = ["user_id"]
        recv = json.loads(await ws.recv())

        utils.validate_fields(required_fields, recv)
        await proposal_feed(request, ws, recv)
예제 #22
0
async def update_expired_roles(request, next_id):
    """Manually expire user role membership"""
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)

    await roles_query.expire_role_member(request.app.config.DB_CONN,
                                         request.json.get("id"), next_id)
    return json({"role_id": request.json.get("id")})
예제 #23
0
async def create_new_pack(request):
    """Create a new pack"""
    required_fields = ["name"]
    utils.validate_fields(required_fields, request.json)

    pack_id = str(uuid4())
    await packs_query.create_pack_resource(request.app.config.DB_CONN, pack_id,
                                           request.json.get("name"))
    return create_pack_response(request, pack_id)
예제 #24
0
async def chatbot(request, web_socket):
    """Chatbot websocket listener."""
    while True:
        required_fields = ["text", "next_id"]
        recv = json.loads(await web_socket.recv())

        utils.validate_fields(required_fields, recv)
        response = await create_response(request, recv)
        await web_socket.send(response)
예제 #25
0
async def update_expired_roles(request, next_id):
    """Manually expire user role membership"""
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)

    conn = await create_connection()
    await roles_query.expire_role_member(conn, request.json.get("id"), next_id)
    conn.close()
    return json({"role_id": request.json.get("id")})
예제 #26
0
async def add_pack_role(request, pack_id):
    """Add roles to a pack"""
    log_request(request)
    required_fields = ["roles"]
    validate_fields(required_fields, request.json)
    conn = await create_connection()
    await packs_query.add_roles(conn, pack_id, request.json.get("roles"))
    conn.close()
    return json({"roles": request.json.get("roles")})
예제 #27
0
async def feed(request, web_socket):
    """Socket feed enabling real-time notifications"""
    LOGGER.info(request)
    while True:
        required_fields = ["next_id"]
        recv = json.loads(await web_socket.recv())

        utils.validate_fields(required_fields, recv)
        await proposal_feed(web_socket, recv)
예제 #28
0
async def add_pack_member(request, pack_id):
    """Add a member to the roles of a pack"""
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)

    head_block = await utils.get_request_block(request)
    pack_resource = await packs_query.fetch_pack_resource(
        request.app.config.DB_CONN, pack_id, head_block.get("num"))
    for role_id in pack_resource.get("roles"):
        await roles.add_role_member(request, role_id)
    return json({"pack_id": pack_id})
예제 #29
0
async def execute_action_add_role_member(request):
    """"Webhook action to create a new proposal given slot data
    collected by the chatbot engine through dialog with a user"""
    required_fields = ["reason", "resource_id"]
    utils.validate_fields(required_fields,
                          request.json["tracker"].get("slots"))

    request.json["id"] = request.json.get("sender_id")
    request.json["reason"] = request.json["tracker"]["slots"].get("reason")
    return await roles.add_role_member(
        request, request.json["tracker"]["slots"].get("resource_id"))
예제 #30
0
async def add_pack_member(request, pack_id):
    """Add a member to the roles of a pack"""
    required_fields = ["id"]
    utils.validate_fields(required_fields, request.json)

    pack_resource = await packs_query.fetch_pack_resource(
        request.app.config.DB_CONN, pack_id)
    request.json["metadata"] = ""
    request.json["pack_id"] = pack_id
    for role_id in pack_resource.get("roles"):
        await roles.add_role_member(request, role_id)
    return json({"pack_id": pack_id})