Loading exercise/lib/exercise_manager.py +17 −27 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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 Loading Loading @@ -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, Loading running_exercise/lib/opensearch_client.py +62 −98 Original line number Diff line number Diff line Loading @@ -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 Loading
exercise/lib/exercise_manager.py +17 −27 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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 Loading Loading @@ -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, Loading
running_exercise/lib/opensearch_client.py +62 −98 Original line number Diff line number Diff line Loading @@ -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