Verified Commit 064f6352 authored by Marek Veselý's avatar Marek Veselý
Browse files

data stream

parent 035e464d
Loading
Loading
Loading
Loading
+17 −27
Original line number Diff line number Diff line
@@ -201,14 +201,11 @@ def create_exercise(
        exercise.name = f"Exercise {exercise.id}"
        exercise.save()

    opensearch_users = None

    teams = create_teams(
        exercise,
        create_exercise_input.team_count,
        team_names,
        create_exercise_input.allowed_start_interval,
        opensearch_users=opensearch_users,
    )
    create_team_inject_states(teams, definition)
    create_email_participants(exercise, definition)
@@ -216,10 +213,25 @@ def create_exercise(
    create_drive_permissions(teams, definition.files.filter(is_drive=True))

    if exercise.technical:
        from running_exercise.lib.opensearch_client import create_data_stream
        from running_exercise.lib.opensearch_client import create_opensearch_exercise

        opensearch_credentials = create_opensearch_exercise([team.id for team in teams])
        # TODO: save the credentials of opensearch users in the team models
        create_data_stream(exercise.id, [team.id for team in teams])

        print("=== credentials ===")
        print(opensearch_credentials)

        # if len(opensearch_credentials) != len(teams):
        #     print("Failed to create OpenSearch users for all teams")
        #     return exercise
        
        # opensearch_users = OpenSearchUser.objects.bulk_create(
        #     OpenSearchUser(username=user["username"], password=user["password"])
        #     for user in opensearch_credentials
        # )
        # for team, user in zip(teams, opensearch_users):
        #     team.opensearch_user = user
        #     team.save()

    return exercise

@@ -310,28 +322,6 @@ def create_teams_basic(
    )
    team_state_ids = create_team_states(exercise, team_count)

    if opensearch_users is not None:
        if len(opensearch_users) != team_count:
            raise ExerciseOperationException(
                f"Expected {team_count} opensearch users, but got {len(opensearch_users)}"
            )
        
        opensearch_users_created = OpenSearchUser.objects.bulk_create(
            OpenSearchUser(username=user["username"], password=user["password"])
            for user in opensearch_users
        )

        return Team.objects.bulk_create(
            Team(
                exercise=exercise,
                name=team_name,
                state_id=team_state_ids[i],
                exercise_state_id=exercise_state_ids[i],
                opensearch_user=opensearch_users_created[i],
            )
            for i, team_name in enumerate(team_names)
        )

    return Team.objects.bulk_create(
        Team(
            exercise=exercise,
+62 −98
Original line number Diff line number Diff line
@@ -13,122 +13,86 @@ client = OpenSearch(
)


def create_data_stream(exercise_id, team_ids):
def create_opensearch_exercise(team_ids):
    """
    1. Create a data stream for the given exercise ID.
    2. Create a user role with write access to the data stream.
    3. Create a user for each team.
    4. Assign each user the role with write access to the data stream.
    For each team:

    1. Create an index template for a data stream.
    2. Create the data stream.
    3. Create an OpenSearch user.
    4. Create a role for the user with write permissions to the data stream.
    5. Map the user to the role.
    6. Save the credentials of the user and return them later.

    https://docs.opensearch.org/docs/latest/im-plugin/data-streams/
    https://docs.opensearch.org/docs/latest/security/access-control/users-roles/
    https://docs.opensearch.org/docs/latest/security/access-control/api/#create-user
    https://opensearch-project.github.io/opensearch-py/api-ref/clients/security_client.html
    """
    data_stream_name = f"exercise-{exercise_id}"
    index_template_name = f"exercise-{exercise_id}-template"
    credentials = []

    for team_id in team_ids:
        data_stream_name = f"team-{team_id}--data-stream"
        
        index_template_name = f"team-{team_id}--index-template"
        index_template_body = {
            "index_patterns": [data_stream_name],
            "data_stream": {},
        }

    try:
        # https://docs.opensearch.org/docs/latest/im-plugin/data-streams/#step-1-create-an-index-template
        index_template = client.indices.put_index_template(name=index_template_name, body=index_template_body)
        print(f"Index template '{index_template_name}' created successfully: {index_template}")

        # https://docs.opensearch.org/docs/latest/im-plugin/data-streams/#step-2-create-a-data-stream
        data_stream = client.indices.create_data_stream(name=data_stream_name)
        print(f"Data stream '{data_stream_name}' created successfully: {data_stream}")
    except Exception as e:
        print(f"Failed to create data stream: {str(e)}")
        return False
    
    for team_id in team_ids:
        username = f"team-{team_id}"
        role_name = f"team-{team_id}-role"

        try:
            # https://docs.opensearch.org/docs/latest/security/access-control/users-roles/#defining-users
            # https://docs.opensearch.org/docs/latest/security/access-control/api/#create-user
            user = client.security.create_user(
                username=username,
                body={
                    # TODO:
                    "password": f"v&ery6#7st*ong78288732-pass889329word-aVUfg9",
                },
            )
            print(f"User '{username}' created successfully: {user}")
        # TODO: Generate a strong password for each user
        password = f"v&ery6#7st*ong78288732-pass889329word-aVUfg9"
        credentials.append({
            "team_id": team_id,
            "username": username,
            "password": password,
        })
        user_body = {
            "password": password,
        }

            # https://docs.opensearch.org/docs/latest/security/access-control/users-roles/#defining-roles
            # https://docs.opensearch.org/docs/latest/security/access-control/api/#create-role
            role = client.security.create_role(
                role=role_name,
                body={
        role_name = f"team-{team_id}--role"
        role_body = {
            "index_permissions": [
                {
                    "index_patterns": [data_stream_name],
                    "allowed_actions": ["write"],
                            # https://docs.opensearch.org/docs/latest/security/access-control/document-level-security/#updating-roles-by-accessing-the-rest-api
                            # TODO: https://docs.opensearch.org/docs/2.19/security/access-control/document-level-security/#dls-and-write-permissions
                            # "dls": "{\"term\": {\"team_id\": \"" + str(team_id) + "\"}}",
                }
            ]
        }
            )
            print(f"Role '{role_name}' created successfully: {role}")

            # https://docs.opensearch.org/docs/latest/security/access-control/users-roles/#mapping-users-to-roles
            # https://docs.opensearch.org/docs/latest/security/access-control/api/#create-role-mapping
            role_mapping = client.security.create_role_mapping(
                role=role_name,
                body={
        role_mapping_body = {
            "users": [username],
        }
            )
            print(f"Role mapping for user '{username}' to role '{role_name}' created successfully: {role_mapping}")
        except Exception as e:
            print(f"Failed to create user or role for team {team_id}: {str(e)}")
            return False
        
    print("\n=== Testing user permissions ===")
    test_user_permissions(exercise_id, team_ids)

    return True
        print(f"=== {team_id} ===")

        try:
            # https://docs.opensearch.org/docs/latest/im-plugin/data-streams/#step-1-create-an-index-template
            index_template = client.indices.put_index_template(name=index_template_name, body=index_template_body)
            print(f"✓ Index template")

def test_user_permissions(exercise_id, team_ids):
    for team_id in team_ids:
        username = f"team-{team_id}"
        password = f"v&ery6#7st*ong78288732-pass889329word-aVUfg9"
            # https://docs.opensearch.org/docs/latest/im-plugin/data-streams/#step-2-create-a-data-stream
            data_stream = client.indices.create_data_stream(name=data_stream_name)
            print(f"✓ Data stream")

        team_client = OpenSearch(
            hosts=[{"host": host, "port": port}],
            http_auth=(username, password),
            use_ssl=True,
            verify_certs=False,
        )
            # https://docs.opensearch.org/docs/latest/security/access-control/users-roles/#defining-users
            # https://docs.opensearch.org/docs/latest/security/access-control/api/#create-user
            user = client.security.create_user(username=username, body=user_body)
            print(f"✓ User")

        doc = {
            "@timestamp": datetime.now().isoformat(),
        }
            # https://docs.opensearch.org/docs/latest/security/access-control/users-roles/#defining-roles
            # https://docs.opensearch.org/docs/latest/security/access-control/api/#create-role
            role = client.security.create_role(role=role_name, body=role_body)
            print(f"✓ Role")

        try:
            data_stream_name = f"exercise-{exercise_id}"
            response = team_client.index(
                index=data_stream_name,
                body=doc
            )
            print(f"✓ Test 1: team_id {team_id}: PASS")
            # https://docs.opensearch.org/docs/latest/security/access-control/users-roles/#mapping-users-to-roles
            # https://docs.opensearch.org/docs/latest/security/access-control/api/#create-role-mapping
            role_mapping = client.security.create_role_mapping(role=role_name, body=role_mapping_body)
            print(f"✓ Role mapping")
        except Exception as e:
            print(f"✗ Test 1: team_id {team_id}: FAIL - {str(e)}")
            print(f"✗ Error")
            return credentials

        try:
            data_stream_name = f"exercise-{exercise_id - 1}"
            response = team_client.index(
                index=data_stream_name,
                body=doc
            )
            print(f"✗ Test 2: team_id {team_id}: FAIL - should not be able to write to exercise-{exercise_id - 1}")
        except Exception as e:
            print(f"✓ Test 2: team_id {team_id}: PASS - {str(e)}")
    return credentials