Ejemplo n.º 1
0
def store_workout_info(workout_id, unit_id, user_mail, workout_duration,
                       workout_type, networks, servers, routes, firewall_rules,
                       assessment, student_instructions_url, student_entry):
    ts = str(calendar.timegm(time.gmtime()))
    new_workout = datastore.Entity(
        ds_client.key('cybergym-workout', workout_id))

    new_workout.update({
        'unit_id': unit_id,
        'user_email': user_mail,
        'student_instructions_url': student_instructions_url,
        'expiration': workout_duration,
        'type': workout_type,
        'start_time': ts,
        'run_hours': 0,
        'timestamp': ts,
        'resources_deleted': False,
        'running': False,
        'misfit': False,
        'networks': networks,
        'servers': servers,
        'routes': routes,
        'firewall_rules': firewall_rules,
        'assessment': assessment,
        'complete': False,
        'student_entry': student_entry
    })

    ds_client.put(new_workout)
Ejemplo n.º 2
0
def unit_signup(unit_id):
    unit = ds_client.get(ds_client.key('cybergym-unit', unit_id))
    if request.method == 'POST':
        workout_query = ds_client.query(kind='cybergym-workout')
        workout_query.add_filter('unit_id', '=', unit_id)
        claimed_workout = None
        for workout in list(workout_query.fetch()):
            if 'student_name' in workout:
                if workout['student_name'] == None or workout[
                        'student_name'] == "":
                    with ds_client.transaction():
                        claimed_workout = workout
                        claimed_workout['student_name'] = request.form[
                            'student_name']
                        ds_client.put(claimed_workout)
                        if unit['build_type'] == 'arena':
                            return redirect('/student/arena_landing/%s' %
                                            claimed_workout.key.name)
                        else:
                            return redirect('/student/landing/%s' %
                                            claimed_workout.key.name)
            else:
                with ds_client.transaction():
                    claimed_workout = workout
                    claimed_workout['student_name'] = request.form[
                        'student_name']
                    ds_client.put(claimed_workout)
                    if unit['build_type'] == 'arena':
                        return redirect('/student/arena_landing/%s' %
                                        claimed_workout.key.name)
                    else:
                        return redirect('/student/landing/%s' %
                                        claimed_workout.key.name)
        return render_template('unit_signup.html', unit_full=True)
    return render_template('unit_signup.html')
 def create_dynamic_answer(self, workout, question_number, answer):
     """
     Create a dynamic answer. Typically called when the workout starts up and creates an arbitrary flag or answer
     for the assessment.
     TODO: This functions has not been used or tested.
     @param workout: DataStore Entity holding the workout.
     @type workout: Dict
     @param question_number: The number to change
     @type question_number: Int
     @param answer: The answer to the assessment question
     @type answer: String
     @return: Status
     @rtype: Boolean
     """
     workout_question = workout['assessment']['questions'][question_number]
     is_dynamic = workout_question.get('dynamic', False)
     if is_dynamic:
         workout_question = workout['assessment']['questions'][
             question_number]
         has_changed = workout_question.get('has_changed', False)
         if not has_changed:
             workout_question['answer'] = answer
             workout_question['has_changed'] = True
             ds_client.put(workout)
             return True
     return False
    def get_user_groups(self, user):
        """
        Get the groups this users is authorized under for this Arena
        @param user: Email address of authenticated user
        @type user: str
        @return: List of groups assigned to the user
        @rtype: list
        """
        user_groups = []
        for group in self.UserGroups.ALL_GROUPS:
            if user in self.admin_info[group]:
                user_groups.append(group)

        if not user_groups and user not in self.admin_info[
                self.UserGroups.PENDING]:
            cloud_log(
                LogIDs.USER_AUTHORIZATION,
                f"Unauthorized User: {user}. Adding to pending authorization",
                LOG_LEVELS.ERROR)
            self.admin_info[self.UserGroups.PENDING].append(user)
            ds_client.put(self.admin_info)

        cloud_log(LogIDs.USER_AUTHORIZATION,
                  f"{user} logged in under groups {user_groups}",
                  LOG_LEVELS.INFO)
        return user_groups
Ejemplo n.º 5
0
def change_workout_state():
    if request.method == 'POST':
        request_data = request.get_json(force=True)
        response = {}
        response['workout_id'] = request_data['workout_id']
        response['new_state'] = request_data['new_state']

        workout = ds_client.get(
            ds_client.key('cybergym-workout', request_data['workout_id']))
        if 'state' in workout:
            previous_state = workout['state']
        workout['state'] = request_data['new_state']
        g_logger = log_client.logger('admin-app')
        g_logger.log_struct(
            {
                "message":
                "Workout {} state override: {} to {}".format(
                    request_data['workout_id'], previous_state,
                    request_data['new_state']),
                "previous_state":
                str(previous_state),
                "new_state":
                str(request_data['new_state']),
                "workout":
                str(request_data['workout_id'])
            },
            severity=LOG_LEVELS.INFO)
        ds_client.put(workout)
        return json.dumps(response)
def register_workout_update(project, dnszone, workout_id, old_ip, new_ip):
    service = googleapiclient.discovery.build('dns', 'v1')

    key = ds_client.key('cybergym-workout', workout_id)
    workout = ds_client.get(key)
    change_body = {
        "deletions": [{
            "kind": "dns#resourceRecordSet",
            "name": workout_id + dns_suffix + ".",
            "rrdatas": [old_ip],
            "type": "A",
            "ttl": 30
        }],
        "additions": [{
            "kind": "dns#resourceRecordSet",
            "name": workout_id + dns_suffix + ".",
            "rrdatas": [new_ip],
            "type": "A",
            "ttl": 30
        }]
    }

    request = service.changes().create(project=project,
                                       managedZone=dnszone,
                                       body=change_body)
    response = request.execute()

    workout["external_ip"] = new_ip
    ds_client.put(workout)
Ejemplo n.º 7
0
def complete_verification():
    if (request.method == 'POST'):
        workout_request = request.get_json(force=True)

        workout_id = workout_request['workout_id']
        token = workout_request['token']
        workout = ds_client.get(ds_client.key('cybergym-workout', workout_id))

        token_exists = next(item for item in workout['assessment']['questions']
                            if item['key'] == token)
        token_pos = next(
            (i for i, item in enumerate(workout['assessment']['questions'])
             if item['key'] == token), None)
        if token_exists:
            logger.info(
                "Completion token matches. Setting the workout question %d to complete."
                % token_pos)
            workout['assessment']['questions'][token_pos]['complete'] = True
            ds_client.put(workout)
            logger.info('%s workout question %d marked complete.' %
                        (workout_id, token_pos + 1))
            return 'OK', 200
        else:
            logger.info(
                "In complete_verification: Completion key %s does NOT exist in assessment dict! Aborting"
                % token)
    def commit_to_cloud(self):
        """
        Commits the parsed workout specification for multiple student builds to the cloud datastore.
        This first stores the unit to the datastore and then all the individual builds
        @return: None
        """
        cloud_log(LogIDs.MAIN_APP, f"Creating unit {self.unit_id}",
                  LOG_LEVELS.INFO)
        new_unit = datastore.Entity(
            ds_client.key('cybergym-unit', self.unit_id))
        new_unit.update(self.new_unit)

        workout_ids = []
        for cloud_ready_spec in self.cloud_ready_specs:
            workout_id = ''.join(
                random.choice(string.ascii_lowercase) for i in range(10))
            workout_ids.append(workout_id)
            new_workout = datastore.Entity(
                ds_client.key('cybergym-workout', workout_id))
            new_workout.update(cloud_ready_spec)
            ds_client.put(new_workout)
            # Store the server specifications for compute workouts
            if self.build_type == BuildTypes.COMPUTE:
                self._commit_workout_servers(workout_id, new_workout)
        new_unit['workouts'] = workout_ids
        ds_client.put(new_unit)
        # If this is an arena, then store all server configurations at this time.
        if self.build_type == BuildTypes.ARENA:
            CompetitionServerSpecToCloud(
                unit=new_unit,
                workout_ids=workout_ids,
                workout_specs=self.cloud_ready_specs).commit_to_cloud()

        return {'unit_id': self.unit_id, 'build_type': self.build_type}
def store_comment(comment_email, comment_subject, comment_text):
    new_comment = datastore.Entity(ds_client.key('cybergym-comments'))
    new_comment['comment_email'] = comment_email
    new_comment['subject'] = comment_subject
    new_comment['comment_text'] = comment_text
    new_comment['message_viewed'] = False
    new_comment['date'] = datetime.datetime.now()
    ds_client.put(new_comment)
Ejemplo n.º 10
0
def change_workout_expiration():
    if request.method == "POST":
        request_data = request.get_json(force=True)
        if 'workout_id' in request_data:
            workout = ds_client.get(
                ds_client.key('cybergym-workout', request_data['workout_id']))
            workout['expiration'] = request_data['new_expiration']
            ds_client.put(workout)
        return json.dumps(str("Test"))
def store_student_feedback(feedback, workout_id):
    student_feedback = datastore.Entity(
        ds_client.key('cybergym-student-feedback', workout_id))

    for key in feedback.keys():
        student_feedback[key] = feedback[key]
    ds_client.put(student_feedback)
    print(student_feedback)
    return student_feedback
def store_class_info(teacher_email, num_students, class_name, student_auth):
    new_class = datastore.Entity(ds_client.key('cybergym-class'))

    new_class['teacher_email'] = teacher_email
    class_roster = []
    for i in range(int(num_students)):
        class_roster.append("Student {}".format(i + 1))
    new_class['roster'] = class_roster
    new_class['class_name'] = class_name
    new_class['student_auth'] = student_auth
    ds_client.put(new_class)
Ejemplo n.º 13
0
def stop_workout(workout_id):
    result = compute.instances().list(project=project, zone=zone,
                                      filter='name = {}*'.format(workout_id)).execute()
    workout = ds_client.get(ds_client.key('cybergym-workout', workout_id))
    workout['state'] == "READY"
    ds_client.put(workout)
    if 'items' in result:
        for vm_instance in result['items']:
            response = compute.instances().stop(project=project, zone=zone,
                                                instance=vm_instance["name"]).execute()

        print("Workouts stopped")
    else:
        print("No workouts to stop")
Ejemplo n.º 14
0
def nuke_workout(workout_id):
    #Get workout information
    workout = ds_client.get(ds_client.key('cybergym-workout', workout_id))
    unit = ds_client.get(ds_client.key('cybergym-unit', workout['unit_id']))
    #Create new workout of same type
    workout_type = workout['type']
    unit_name = unit['unit_name']
    unit_id = workout['unit_id']

    submitted_answers = None
    if 'submitted_answers' in workout:
        submitted_answers = workout['submitted_answers']
    if 'expiration' in unit:
        expiration = unit['expiration']
    else:
        expiration = 0
    instructor_id = workout['user_email']
    yaml_string = parse_workout_yaml(workout_type)
    unit_id, build_type, new_id = process_workout_yaml(yaml_string,
                                                       workout_type, unit_name,
                                                       1, expiration,
                                                       instructor_id, unit_id)

    if submitted_answers:
        new_workout = ds_client.get(ds_client.key('cybergym-workout', new_id))
        new_workout['submitted_answers'] = submitted_answers

    unit_id = workout['unit_id']
    #Get new workout information
    response = {
        "unit_id": unit_id,
        "build_type": build_type,
        "workout_id": new_id
    }

    if build_type == 'compute':
        pub_build_single_workout(
            workout_id=new_id,
            topic_name=workout_globals.ps_build_workout_topic)

    if workout_id in unit['workouts']:
        unit['workouts'].remove(workout_id)
    unit['workouts'].append(new_id)
    ds_client.put(unit)

    workout['misfit'] = True
    ds_client.put(workout)

    return json.dumps(response)
 def _cloud_update_class(self):
     class_unit = {
         "unit_id": self.unit_id,
         "unit_name": self.unit_name,
         "workout_name": self.workout_name,
         "build_type": self.build_type,
         "timestamp": str(calendar.timegm(time.gmtime()))
     }
     if 'unit_list' not in self.class_record:
         unit_list = []
         unit_list.append(class_unit)
         self.class_record['unit_list'] = unit_list
     else:
         self.class_record['unit_list'].append(class_unit)
     ds_client.put(self.class_record)
Ejemplo n.º 16
0
def check_user_level():
    if (request.method == 'POST'):
        user_info = request.get_json(force=True)
        admin_info = ds_client.get(
            ds_client.key('cybergym-admin-info', 'cybergym'))
        response = {'authorized': False, 'admin': False}

        if user_info['user_email']:
            if user_info['user_email'] in admin_info['authorized_users']:
                response['authorized'] = True
                if user_info['user_email'] in admin_info['admins']:
                    response['admin'] = True
            else:
                if user_info['user_email'] not in admin_info['pending_users']:
                    admin_info['pending_users'].append(user_info['user_email'])
                    ds_client.put(admin_info)
        return json.dumps(response)
    def build_arena(self):
        """

        @return: Status
        @rtype: bool
        """
        self._get_build_allocation()
        if not self.allocation:
            # If there are not enough resource, delete the unit and workouts from the datastore before returning.
            ds_client.delete(self.unit_id)
            for workout in self.workouts:
                ds_client.delete(workout.key)
                servers = ds_client.query(kind='cybergym-server').add_filter(
                    'workout', '=', workout.key.name).fetch()
                for server in servers:
                    ds_client.delete(server)
            servers = ds_client.query(kind='cybergym-server').add_filter(
                'workout', '=', workout.key.name).fetch()
            for server in servers:
                ds_client.delete(server)
            return False
        _projects = list(self.allocation.keys())
        i = 0
        j = 0
        publisher = topic_path = current_project = None
        for workout in self.workouts:
            if i == 0:
                current_project = _projects[j]
                j += 1

                publisher = pubsub_v1.PublisherClient()
                topic_path = publisher.topic_path(
                    current_project, workout_globals.ps_build_workout_topic)
            workout['build_project_location'] = current_project
            ds_client.put(workout)
            publisher.publish(topic_path,
                              data=b'Cyber Gym Workout',
                              workout_id=workout.key.name)

            i += 1
            # If the index is greater than the current project's build allocation, reset the index, which triggers
            # a project change on the next iteration.
            if i >= self.allocation[current_project]:
                i = 0
        return True
Ejemplo n.º 18
0
def start_arena():
    if request.method == 'POST':
        unit_id = request.form['unit_id']
        arena_unit = ds_client.get(ds_client.key('cybergym-unit', unit_id))
        if 'time' not in request.form:
            arena_unit['arena']['run_hours'] = 2
        else:
            arena_unit['arena']['run_hours'] = min(
                int(request.form['time']), workout_globals.MAX_RUN_HOURS)
        ds_client.put(arena_unit)
        start_time = time.gmtime(time.time())
        time_string = str(start_time[3]) + ":" + str(
            start_time[4]) + ":" + str(start_time[5])
        print(time_string)
        arena_unit['start_time'] = time_string
        ds_client.put(arena_unit)
        pub_start_vm(unit_id, 'start-arena')
    return redirect('/arena_list/%s' % (unit_id))
Ejemplo n.º 19
0
def start_all():
    if (request.method == 'POST'):
        unit_id = request.form['unit_id']
        workout_list = get_unit_workouts(unit_id)
        t_list = []
        for workout_id in workout_list:
            workout = ds_client.get(
                ds_client.key('cybergym-workout', workout_id['name']))
            if 'time' not in request.form:
                workout['run_hours'] = 2
            else:
                workout['run_hours'] = min(int(request.form['time']),
                                           workout_globals.MAX_RUN_HOURS)
            ds_client.put(workout)

            pub_start_vm(workout_id['name'])

        return redirect("/workout_list/%s" % (unit_id))
 def __init__(self):
     self.admin_info = ds_client.get(
         ds_client.key('cybergym-admin-info', 'cybergym'))
     if self.UserGroups.ADMINS not in self.admin_info:
         admin_email = myconfig.get_variable.config('admin_email')
         if not admin_email:
             cloud_log(LogIDs.MAIN_APP,
                       f"Error: Admin Email is not set for this project!",
                       LOG_LEVELS.ERROR)
         else:
             self.admin_info[self.UserGroups.ADMINS] = [admin_email]
     if self.UserGroups.AUTHORIZED not in self.admin_info:
         self.admin_info[self.UserGroups.AUTHORIZED] = []
     if self.UserGroups.STUDENTS not in self.admin_info:
         self.admin_info[self.UserGroups.STUDENTS] = []
     if self.UserGroups.PENDING not in self.admin_info:
         self.admin_info[self.UserGroups.PENDING] = []
     ds_client.put(self.admin_info)
Ejemplo n.º 21
0
def store_unit_info(id, email, unit_name, workout_name, build_type, ts,
                    workout_url_path, teacher_instructions_url,
                    workout_description):
    new_unit = datastore.Entity(ds_client.key('cybergym-unit', id))

    new_unit.update({
        "unit_name": unit_name,
        "build_type": build_type,
        "workout_name": workout_name,
        "instructor_id": email,
        "timestamp": ts,
        'workout_url_path': workout_url_path,
        "description": workout_description,
        "teacher_instructions_url": teacher_instructions_url,
        "workouts": []
    })

    ds_client.put(new_unit)
Ejemplo n.º 22
0
 def _update_build_spec(self):
     """
     Add credentials for the student entry to the workout, and add a default firewall rule to allow access
     to the student entry server.
     When the build type is an arena, each workout in the arena needs to have added the recently generated
     credentials.
     @param credentials: Credentials for users in a given build
     @type credentials: list of dict
     @param network_name: The name of the network the student entry server resides in
     @type: str
     @return: Status
     @rtype: bool
     """
     # Build the firewall rule to allow external access to the student entry.
     firewall_rule = {
         'name': 'allow-student-entry',
         'network': self.student_entry_network,
         'target_tags': ['student-entry'],
         'protocol': None,
         'ports': ['tcp/80,8080,443'],
         'source_ranges': ['0.0.0.0/0']
     }
     if self.type == BuildTypes.COMPUTE:
         if len(self.student_credentials) > 1:
             self.build['workout_credentials'] = self.student_credentials
         else:
             self.build['workout_user'] = self.student_credentials[0][
                 'workout_user']
             self.build['workout_password'] = self.student_credentials[0][
                 'workout_password']
         self.build['firewall_rules'].append(firewall_rule)
     elif self.type == BuildTypes.ARENA:
         for credential in self.student_credentials:
             workout_id = credential['workout_id']
             student_user = credential['workout_user']
             student_password = credential['workout_password']
             workout = ds_client.get(
                 ds_client.key('cybergym-workout', workout_id))
             workout['workout_user'] = student_user
             workout['workout_password'] = student_password
             ds_client.put(workout)
         self.build['arena']['firewall_rules'].append(firewall_rule)
     ds_client.put(self.build)
     return True
Ejemplo n.º 23
0
def start_vm():
    if (request.method == 'POST'):
        data = request.get_json(force=True)
        workout_id = None
        if 'workout_id' in data:
            workout_id = data['workout_id']
        g_logger = log_client.logger(str(workout_id))
        g_logger.log_text(str('Starting workout ' + workout_id))
        workout = ds_client.get(ds_client.key('cybergym-workout', workout_id))
        if 'time' not in request.form:
            workout['run_hours'] = 2
        else:
            workout['run_hours'] = min(int(request.form['time']),
                                       workout_globals.MAX_RUN_HOURS)
        # workout['running'] = True
        ds_client.put(workout)

        pub_start_vm(workout_id)
        return 'VM Started'
Ejemplo n.º 24
0
def store_workout_container(unit_id, workout_id, workout_type,
                            student_instructions_url, container_info,
                            assessment):
    ts = str(calendar.timegm(time.gmtime()))
    new_workout = datastore.Entity(
        ds_client.key('cybergym-workout', workout_id))

    new_workout.update({
        'unit_id': unit_id,
        'build_type': 'container',
        'type': workout_type,
        'student_instructions_url': student_instructions_url,
        'timestamp': ts,
        'complete': False,
        'container_info': container_info,
        'assessment': assessment,
        'state': 'RUNNING'
    })

    ds_client.put(new_workout)
Ejemplo n.º 25
0
def update_workout_list():
    if request.method == 'POST':
        if 'new_workout_name' and 'new_workout_display' in request.form:
            admin_info = ds_client.get(
                ds_client.key('cybergym-admin-info', 'cybergym'))
            workout_list = admin_info['workout_list']
            if not any(
                    workout['workout_name'] == request.form['new_workout_name']
                    for workout in workout_list):
                new_workout_info = {
                    'workout_name': request.form['new_workout_name'],
                    'workout_display_name': request.form['new_workout_display']
                }
                workout_list.append(new_workout_info)
                ds_client.put(admin_info)
                return 'Complete'
            else:
                return 'Failed'
        else:
            return 'Failed'
Ejemplo n.º 26
0
def store_arena_workout(workout_id, unit_id, user_mail, timestamp,
                        student_servers, student_instructions_url, assessment):
    new_workout = datastore.Entity(
        ds_client.key('cybergym-workout', workout_id))
    new_workout.update({
        'unit_id': unit_id,
        'type': 'arena',
        'build_type': 'arena',
        'student_instructions_url': student_instructions_url,
        'start_time': timestamp,
        'run_hours': 0,
        'timestamp': timestamp,
        'running': False,
        'misfit': False,
        'assessment': assessment,
        'student_servers': student_servers,
        'instructor_id': user_mail,
        'teacher_email': user_mail
    })

    ds_client.put(new_workout)
Ejemplo n.º 27
0
def add_arena_to_unit(unit_id, workout_duration, timestamp, networks,
                      user_mail, servers, routes, firewall_rules,
                      student_entry, student_entry_type, network_type,
                      student_entry_username, student_entry_password):
    """
    Adds the necessary build components to the unit to build a common network in which all students can interact.
    :param unit_id: The unit in which to add the arena build
    :param workout_duration: The number of days before deleting this arena
    :param start_time: Used for calculating how long an arena has run
    :param networks: The network to build for this arena
    :param servers: The servers to build for this arena
    :param routes: The routes to include for this arena
    :param firewall_rules: The firewall rules to add for the arena
    :param student_entry*: Parameters for building the guacamole connections through startup scripts
    :return: None
    """
    unit = ds_client.get(ds_client.key('cybergym-unit', unit_id))
    teams = []
    teams.append(str(user_mail))
    unit['arena'] = {
        'expiration': workout_duration,
        'start_time': timestamp,
        'run_hours': 0,
        'timestamp': timestamp,
        'resources_deleted': False,
        'running': False,
        'misfit': False,
        'networks': networks,
        'servers': servers,
        'routes': routes,
        'firewall_rules': firewall_rules,
        'student_entry': student_entry,
        'student_entry_type': student_entry_type,
        'student_entry_username': student_entry_username,
        'student_entry_password': student_entry_password,
        'student_network_type': network_type
    }
    unit['teams'] = teams

    ds_client.put(unit)
def reset_workout(workout_id):
    key = ds_client.key('cybergym-workout', workout_id)
    workout = ds_client.get(key)

    result = compute.instances().list(
        project=project, zone=zone,
        filter='name = {}*'.format(workout_id)).execute()
    try:
        if 'items' in result:
            for vm_instance in result['items']:
                response = compute.instances().reset(
                    project=project, zone=zone,
                    instance=vm_instance["name"]).execute()
                workout_globals.extended_wait(project, zone, response["id"])

                started_vm = compute.instances().get(
                    project=project, zone=zone,
                    instance=vm_instance["name"]).execute()
                if 'accessConfigs' in started_vm['networkInterfaces'][0]:
                    if 'natIP' in started_vm['networkInterfaces'][0][
                            'accessConfigs'][0]:
                        tags = started_vm['tags']
                        if tags:
                            for item in tags['items']:
                                if item == 'labentry':
                                    ip_address = started_vm[
                                        'networkInterfaces'][0][
                                            'accessConfigs'][0]['natIP']
                                    register_workout_update(
                                        project, dnszone, workout_id,
                                        workout["external_ip"], ip_address)
            time.sleep(30)
            workout['running'] = True
            workout['start_time'] = str(calendar.timegm(time.gmtime()))
            ds_client.put(workout)
            print("Finished resetting %s" % workout_id)
        return True
    except ():
        print("Error in resetting VM for %s" % workout_id)
        return False
Ejemplo n.º 29
0
def admin_page():
    admin_info = ds_client.get(ds_client.key('cybergym-admin-info',
                                             'cybergym'))

    if request.method == "POST":
        response = {'action_completed': 'false'}
        data = request.get_json(force=True)
        if data['action'] == 'approve_user':
            if data['user_email'] in admin_info['pending_users']:
                admin_info['authorized_users'].append(data['user_email'])
                admin_info['pending_users'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
        elif data['action'] == 'deny_user':
            if data['user_email'] in admin_info['pending_users']:
                admin_info['pending_users'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
        elif data['action'] == 'promote_user':
            if data['user_email'] in admin_info['authorized_users']:
                admin_info['admins'].append(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
        elif data['action'] == 'remove_user':
            if data['user_email'] in admin_info['authorized_users']:
                admin_info['authorized_users'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
        elif data['action'] == 'revoke_access':
            if data['user_email'] in admin_info['admins']:
                admin_info['admins'].remove(data['user_email'])
                ds_client.put(admin_info)
                response['action_completed'] = 'true'
        return json.dumps(response)

    return render_template('admin_page.html',
                           auth_config=auth_config,
                           admin_info=admin_info)
    def _upload_file(self, file, prompt, index):
        """
            Handles storage of image files for workout assessment questions involving screenshots. 
            Stores images in GCP Storage Bucket {project}_assessment_upload/{workout_id}
            @param file: Blob object for the image file
            @param prompt: Used to designate the question to which the image pertains
            @param index: The index of the question in the assessment object. Used to provide a unique name to the image upload in GCP
        """
        from utilities.datastore_functions import store_student_upload
        upload_url = store_student_upload(self.workout_id, file[0], index)

        if upload_url:
            for prev_upload in self.uploaded_files:
                if prev_upload['question'] == prompt:
                    prev_upload['storage_url'] = upload_url
                    self.workout['uploaded_files'] = self.uploaded_files
                    ds_client.put(self.workout)
                    return upload_url
            user_input = {"question": prompt, "storage_url": upload_url}
            self.uploaded_files.append(user_input)
            self.workout['uploaded_files'] = self.uploaded_files
        ds_client.put(self.workout)
        return json.dumps(user_input)