예제 #1
0
    def post(self, request):
        """
        POST /api/groups
        """
        group_type = request.data.get('type', None)
        if group_type is None:
            return Response({}, status=status.HTTP_400_BAD_REQUEST)
        response_data = {}
        # Group name must be unique, but we need to support dupes
        group = Group.objects.create(name=str(uuid.uuid4()))
        original_group_name = request.data.get('name', None)
        if original_group_name is None or len(original_group_name) == 0:
            return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
        group.name = '{:04d}: {}'.format(group.id, original_group_name)
        group.record_active = True
        group.save()

        # Create a corresponding relationship management record
        GroupRelationship.objects.create(group_id=group.id, parent_group=None)

        # Create a corresponding profile record (for extra meta info)
        data = request.data.get('data', {})
        profile, _ = GroupProfile.objects.get_or_create(
            group_id=group.id,
            group_type=group_type,
            name=original_group_name,
            data=json.dumps(data))

        response_data['id'] = group.id
        response_data['name'] = profile.name
        response_data['uri'] = '{}/{}'.format(generate_base_uri(request, True),
                                              group.id)
        response_status = status.HTTP_201_CREATED
        return Response(response_data, status=response_status)
예제 #2
0
    def post(self, request, group_id):
        """
        POST /api/groups/{group_id}/courses/
        """
        response_data = {}
        try:
            existing_group = Group.objects.get(id=group_id)
        except ObjectDoesNotExist:
            return Response({}, status.HTTP_404_NOT_FOUND)

        course_id = request.data.get('course_id', None)
        if not course_id:
            return Response({'message': _('course_id is missing')}, status=status.HTTP_400_BAD_REQUEST)

        base_uri = generate_base_uri(request)
        response_data['uri'] = '{}/{}'.format(base_uri, course_id)

        existing_course, course_key, course_content = get_course(request, request.user, course_id)  # pylint: disable=W0612,C0301
        if not existing_course:
            return Response({}, status.HTTP_404_NOT_FOUND)

        try:
            existing_relationship = CourseGroupRelationship.objects.get(course_id=course_id, group=existing_group)
        except ObjectDoesNotExist:
            existing_relationship = None

        if existing_relationship is None:
            new_relationship = CourseGroupRelationship.objects.create(course_id=course_id, group=existing_group)
            response_data['group_id'] = str(new_relationship.group_id)
            response_data['course_id'] = str(new_relationship.course_id)
            response_status = status.HTTP_201_CREATED
        else:
            response_data['message'] = "Relationship already exists."
            response_status = status.HTTP_409_CONFLICT
        return Response(response_data, status=response_status)
예제 #3
0
    def get(self, request, group_id, related_group_id):
        """
        GET /api/groups/{group_id}/groups/{related_group_id}
        """
        response_data = {}
        base_uri = generate_base_uri(request)
        response_data['uri'] = base_uri
        response_data['from_group_id'] = group_id
        response_data['to_group_id'] = related_group_id
        response_status = status.HTTP_404_NOT_FOUND

        try:
            from_group_relationship = GroupRelationship.objects.get(
                group__id=group_id)
            to_group_relationship = GroupRelationship.objects.get(
                group__id=related_group_id)
        except ObjectDoesNotExist:
            return Response(response_data, response_status)

        if to_group_relationship and str(
                to_group_relationship.parent_group_id) == str(group_id):
            response_data['relationship_type'] = RELATIONSHIP_TYPES[
                'hierarchical']
            response_status = status.HTTP_200_OK
        else:
            linked_group_exists = from_group_relationship.check_linked_group_relationship(
                to_group_relationship, symmetrical=True)
            if linked_group_exists:
                response_data['relationship_type'] = RELATIONSHIP_TYPES[
                    'graph']
                response_status = status.HTTP_200_OK
        return Response(response_data, response_status)
예제 #4
0
 def post(self, request, group_id):
     """
     POST /api/groups/{group_id}
     """
     response_data = {}
     try:
         existing_group = Group.objects.get(id=group_id)
     except ObjectDoesNotExist:
         return Response({}, status.HTTP_404_NOT_FOUND)
     profile, _ = GroupProfile.objects.get_or_create(group_id=group_id)
     group_name = request.data.get('name', None)
     if group_name:
         formatted_name = '{:04d}: {}'.format(existing_group.id, group_name)
         existing_group.name = formatted_name
         profile.name = group_name
     group_type = request.data.get('type', None)
     if group_type:
         profile.group_type = group_type
     data = request.data.get('data', None)
     if data:
         profile.data = json.dumps(data)
     existing_group.save()
     profile.save()
     response_data['id'] = existing_group.id
     response_data['name'] = profile.name
     response_data['type'] = profile.group_type
     response_data['uri'] = generate_base_uri(request)
     return Response(response_data, status=status.HTTP_200_OK)
예제 #5
0
 def post(self, request, group_id):
     """
     POST /api/groups/{group_id}/groups/{related_group_id}
     """
     response_data = {}
     to_group_id = request.DATA['group_id']
     relationship_type = request.DATA['relationship_type']
     base_uri = generate_base_uri(request)
     response_data['uri'] = '{}/{}'.format(base_uri, to_group_id)
     response_data['group_id'] = str(to_group_id)
     response_data['relationship_type'] = relationship_type
     try:
         from_group_relationship = GroupRelationship.objects.get(group__id=group_id)
         to_group_relationship = GroupRelationship.objects.get(group__id=to_group_id)
     except ObjectDoesNotExist:
         from_group_relationship = None
         to_group_relationship = None
     if from_group_relationship and to_group_relationship:
         response_status = status.HTTP_201_CREATED
         if relationship_type == RELATIONSHIP_TYPES['hierarchical']:
             to_group_relationship.parent_group = from_group_relationship
             to_group_relationship.save()
         elif relationship_type == RELATIONSHIP_TYPES['graph']:
             from_group_relationship.add_linked_group_relationship(to_group_relationship)
         else:
             response_data['message'] = "Relationship type '%s' not currently supported" % relationship_type
             response_data['field_conflict'] = 'relationship_type'
             response_status = status.HTTP_406_NOT_ACCEPTABLE
     else:
         response_status = status.HTTP_404_NOT_FOUND
     return Response(response_data, status=response_status)
예제 #6
0
 def post(self, request, group_id):
     """
     POST /api/groups/{group_id}/users/
     """
     base_uri = generate_base_uri(request)
     try:
         existing_group = Group.objects.get(id=group_id)
     except ObjectDoesNotExist:
         return Response({}, status.HTTP_404_NOT_FOUND)
     user_id = request.DATA['user_id']
     try:
         existing_user = User.objects.get(id=user_id)
     except ObjectDoesNotExist:
         return Response({}, status.HTTP_404_NOT_FOUND)
     try:
         existing_relationship = Group.objects.filter(id=existing_group.id).get(user=existing_user)
     except ObjectDoesNotExist:
         existing_relationship = None
     response_data = {}
     if existing_relationship is None:
         existing_group.user_set.add(existing_user.id)
         response_data['uri'] = '{}/{}'.format(base_uri, existing_user.id)
         response_data['group_id'] = str(existing_group.id)
         response_data['user_id'] = str(existing_user.id)
         response_status = status.HTTP_201_CREATED
     else:
         response_data['uri'] = '{}/{}'.format(base_uri, existing_user.id)
         response_data['message'] = "Relationship already exists."
         response_status = status.HTTP_409_CONFLICT
     return Response(response_data, status=response_status)
예제 #7
0
 def get(self, request):
     """
     GET /api/system/
     """
     base_uri = generate_base_uri(request)
     response_data = {}
     response_data['name'] = "Open edX System API"
     response_data[
         'description'] = "System interface for managing groups, users, and sessions."
     response_data[
         'documentation'] = "http://docs.openedxapi.apiary.io/#get-%2Fapi%2Fsystem"
     response_data['uri'] = base_uri
     return Response(response_data, status=status.HTTP_200_OK)
예제 #8
0
 def get(self, request, group_id):
     """
     GET /api/groups/{group_id}/groups/{related_group_id}
     """
     try:
         from_group_relationship = GroupRelationship.objects.get(
             group__id=group_id)
     except ObjectDoesNotExist:
         from_group_relationship = None
     response_data = []
     if from_group_relationship:
         base_uri = generate_base_uri(request)
         group_type = request.query_params.get('type', None)
         child_groups = GroupRelationship.objects.filter(
             parent_group_id=group_id)
         linked_groups = from_group_relationship.get_linked_group_relationships(
         )
         if group_type:
             profiles = GroupProfile.objects.filter(
                 group_type=group_type).values_list('group_id', flat=True)
             if profiles:
                 child_groups = child_groups.filter(group_id__in=profiles)
                 linked_groups = linked_groups.filter(
                     to_group_relationship__in=profiles)
         if child_groups:
             for group in child_groups:
                 response_data.append({
                     "id":
                     group.group_id,
                     "relationship_type":
                     RELATIONSHIP_TYPES['hierarchical'],
                     "uri":
                     '{}/{}'.format(base_uri, group.group.id)
                 })
         if linked_groups:
             for group in linked_groups:
                 response_data.append({
                     "id":
                     group.to_group_relationship_id,
                     "relationship_type":
                     RELATIONSHIP_TYPES['graph'],
                     "uri":
                     '{}/{}'.format(base_uri,
                                    group.to_group_relationship_id)
                 })
         response_status = status.HTTP_200_OK
     else:
         response_status = status.HTTP_404_NOT_FOUND
     return Response(response_data, status=response_status)
예제 #9
0
 def get(self, request, group_id, user_id):
     """
     GET /api/groups/{group_id}/users/{user_id}
     """
     response_data = {}
     base_uri = generate_base_uri(request)
     try:
         existing_group = Group.objects.get(id=group_id)
         existing_relationship = existing_group.user_set.get(id=user_id)
     except ObjectDoesNotExist:
         existing_group = None
         existing_relationship = None
     if existing_group and existing_relationship:
         response_data['group_id'] = existing_group.id
         response_data['user_id'] = existing_relationship.id
         response_data['uri'] = base_uri
         response_status = status.HTTP_200_OK
     else:
         response_status = status.HTTP_404_NOT_FOUND
     return Response(response_data, status=response_status)
예제 #10
0
 def get(self, request):
     """
     GET /api/
     """
     base_uri = generate_base_uri(request)
     response_data = {}
     response_data['name'] = "Open edX API"
     response_data[
         'description'] = "Machine interface for interactions with Open edX."
     response_data['documentation'] = "http://docs.openedxapi.apiary.io"
     response_data['uri'] = base_uri
     response_data['csrf_token'] = get_token(request)
     response_data['resources'] = []
     response_data['resources'].append({'uri': base_uri + 'courses'})
     response_data['resources'].append({'uri': base_uri + 'groups'})
     response_data['resources'].append({'uri': base_uri + 'projects'})
     response_data['resources'].append({'uri': base_uri + 'sessions'})
     response_data['resources'].append({'uri': base_uri + 'system'})
     response_data['resources'].append({'uri': base_uri + 'users'})
     return Response(response_data, status=status.HTTP_200_OK)
예제 #11
0
 def get(self, request, session_id):
     """
     Returns session
     """
     response_data = {}
     base_uri = generate_base_uri(request)
     engine = import_module(settings.SESSION_ENGINE)
     session = engine.SessionStore(session_id)
     try:
         user_id = session[SESSION_KEY]
         backend_path = session[BACKEND_SESSION_KEY]
         backend = load_backend(backend_path)
         user = backend.get_user(user_id) or AnonymousUser()
     except KeyError:
         user = AnonymousUser()
     if user.is_authenticated:
         response_data['token'] = session.session_key
         response_data['expires'] = session.get_expiry_age()
         response_data['uri'] = base_uri
         response_data['user_id'] = user.id
         return Response(response_data, status=status.HTTP_200_OK)
     else:
         return Response(response_data, status=status.HTTP_404_NOT_FOUND)
예제 #12
0
 def get(self, request, group_id):
     """
     GET /api/groups/{group_id}
     """
     response_data = {}
     base_uri = generate_base_uri(request)
     try:
         existing_group = Group.objects.get(id=group_id)
     except ObjectDoesNotExist:
         return Response({}, status.HTTP_404_NOT_FOUND)
     response_data['id'] = existing_group.id
     response_data['uri'] = base_uri
     response_data['resources'] = []
     resource_uri = '{}/users'.format(base_uri)
     response_data['resources'].append({'uri': resource_uri})
     resource_uri = '{}/groups'.format(base_uri)
     response_data['resources'].append({'uri': resource_uri})
     resource_uri = '{}/courses'.format(base_uri)
     response_data['resources'].append({'uri': resource_uri})
     try:
         group_profile = GroupProfile.objects.get(group_id=group_id)
     except ObjectDoesNotExist:
         group_profile = None
     if group_profile:
         if group_profile.name:
             response_data['name'] = group_profile.name
         else:
             response_data['name'] = existing_group.name
         if group_profile.group_type:
             response_data['type'] = group_profile.group_type
         data = group_profile.data
         if data:
             response_data['data'] = json.loads(data)
     else:
         response_data['name'] = existing_group.name
     return Response(response_data, status=status.HTTP_200_OK)
예제 #13
0
    def login_user(request, session_id=None):
        """ Create a new session and login the user, or upgrade an existing session """
        response_data = {}
        # Add some rate limiting here by re-using the RateLimitMixin as a helper class
        limiter = BadRequestRateLimiter()
        if limiter.is_rate_limit_exceeded(request):
            response_data['message'] = _('Rate limit exceeded in api login.')
            return Response(response_data, status=status.HTTP_403_FORBIDDEN)

        base_uri = generate_base_uri(request)

        username = request.data.get('username', None)
        if username is None:
            return Response({'message': _('username is missing')},
                            status=status.HTTP_400_BAD_REQUEST)

        password = request.data.get('password', None)
        if password is None:
            return Response({'message': _('password is missing')},
                            status=status.HTTP_400_BAD_REQUEST)

        try:
            existing_user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            existing_user = None

        # see if account has been locked out due to excessive login failures
        if existing_user and LoginFailures.is_feature_enabled():
            if LoginFailures.is_user_locked_out(existing_user):
                response_status = status.HTTP_403_FORBIDDEN
                response_data['message'] = _(
                    'This account has been temporarily locked due to excessive login failures. '  # pylint: disable=C0301
                    'Try again later.')
                return Response(response_data, status=response_status)

        # see if the user must reset his/her password due to any policy settings
        if existing_user and PasswordHistory.should_user_reset_password_now(
                existing_user):
            response_status = status.HTTP_403_FORBIDDEN
            response_data['message'] = _(
                'Your password has expired due to password policy on this account. '
                'You must reset your password before you can log in again.')
            return Response(response_data, status=response_status)

        if existing_user:
            user = authenticate(username=existing_user.username,
                                password=password)
            if user is not None:

                # successful login, clear failed login attempts counters, if applicable
                if LoginFailures.is_feature_enabled():
                    LoginFailures.clear_lockout_counter(user)

                if user.is_active:
                    #
                    # Create a new session directly with the SESSION_ENGINE
                    # We don't call the django.contrib.auth login() method
                    # because it is bound with the HTTP request.
                    #
                    # Since we are a server-to-server API, we shouldn't
                    # be stateful with respect to the HTTP request
                    # and anything that might come with it, as it could
                    # violate our RESTfulness
                    #
                    engine = import_module(settings.SESSION_ENGINE)
                    if session_id is None:
                        session = engine.SessionStore()
                        session.create()
                        success_status = status.HTTP_201_CREATED
                    else:
                        session = engine.SessionStore(session_id)
                        success_status = status.HTTP_200_OK
                        if SESSION_KEY in session:
                            # Someone is already logged in. The user ID of whoever is logged in
                            # now might be different than the user ID we've been asked to login,
                            # which would be bad. But even if it is the same user, we should not
                            # be asked to login a user who is already logged in. This likely
                            # indicates some sort of programming/validation error and possibly
                            # even a potential security issue - so return 403.
                            return Response({},
                                            status=status.HTTP_403_FORBIDDEN)

                    # These values are expected to be set in any new session
                    session[SESSION_KEY] = user.id
                    session[BACKEND_SESSION_KEY] = user.backend
                    if hasattr(user, 'get_session_auth_hash'):
                        session_auth_hash = user.get_session_auth_hash()
                    else:
                        session_auth_hash = ''
                    session[HASH_SESSION_KEY] = session_auth_hash

                    session.save()

                    response_data['token'] = session.session_key
                    response_data['expires'] = session.get_expiry_age()
                    user_dto = SimpleUserSerializer(user)
                    response_data['user'] = user_dto.data
                    response_data['uri'] = '{}/{}'.format(
                        base_uri, session.session_key)
                    response_status = success_status

                    # generate a CSRF tokens for any web clients that may need to
                    # call into the LMS via Ajax (for example Notifications)
                    response_data['csrftoken'] = str(
                        csrf(request)['csrf_token'])

                    # update the last_login fields in the auth_user table for this user
                    user.last_login = timezone.now()
                    user.save()

                    # add to audit log
                    AUDIT_LOG.info(
                        "API::User logged in successfully with user-id - {}".
                        format(user.id))  # pylint: disable=W1202
                else:
                    response_status = status.HTTP_403_FORBIDDEN
            else:
                limiter.tick_request_counter(request)

                # tick the failed login counters if the user exists in the database
                if LoginFailures.is_feature_enabled():
                    LoginFailures.increment_lockout_counter(existing_user)

                response_status = status.HTTP_401_UNAUTHORIZED
                AUDIT_LOG.warn(
                    "API::User authentication failed with user-id - {}".format(
                        existing_user.id))  # pylint: disable=W1202
        else:
            AUDIT_LOG.warn(
                "API::Failed login attempt with unknown email/username")
            response_status = status.HTTP_404_NOT_FOUND
        return Response(response_data, status=response_status)
예제 #14
0
 def get_uri(self, course):
     """
     Builds course detail uri
     """
     return "{}/{}".format(generate_base_uri(self.context['request']),
                           course.id)