Beispiel #1
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['course_id']

        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
        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)
Beispiel #2
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)
Beispiel #3
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)
Beispiel #4
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
     from_group_relationship = GroupRelationship.objects.get(group__id=group_id)
     if from_group_relationship:
         try:
             to_group_relationship = GroupRelationship.objects.get(group__id=related_group_id)
         except ObjectDoesNotExist:
             return Response(response_data, status=status.HTTP_404_NOT_FOUND)
         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:
             to_group = Group.objects.get(id=to_group_relationship.group_id)
             linked_group_exists = from_group_relationship.check_linked_group_relationship(to_group, 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)
Beispiel #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)
Beispiel #6
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)
Beispiel #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)
Beispiel #8
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)
Beispiel #9
0
 def get(self, request, group_id, course_id):
     """
     GET /api/groups/{group_id}/courses/{course_id}
     """
     response_data = {}
     base_uri = generate_base_uri(request)
     response_data['uri'] = base_uri
     try:
         existing_group = Group.objects.get(id=group_id)
         existing_relationship = CourseGroupRelationship.objects.get(course_id=course_id, group=existing_group)
     except ObjectDoesNotExist:
         existing_group = None
         existing_relationship = None
     if existing_group and existing_relationship:
         response_data['group_id'] = existing_group.id
         response_data['course_id'] = existing_relationship.course_id
         response_status = status.HTTP_200_OK
     else:
         response_status = status.HTTP_404_NOT_FOUND
     return Response(response_data, status=response_status)
Beispiel #10
0
 def get(self, request, session_id):
     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)
Beispiel #11
0
 def get(self, request):
     """
     GET /api/groups
     """
     response_data = []
     group_type = request.QUERY_PARAMS.get('type', None)
     if group_type is None:
         return Response({}, status=status.HTTP_400_BAD_REQUEST)
     profiles = GroupProfile.objects.filter(group_type=request.GET['type']).select_related('group')
     for profile in profiles:
         item_data = {}
         item_data['id'] = profile.group_id
         if profile.name and len(profile.name):
             group_name = profile.name
         else:
             group_name = profile.group.name
         item_data['name'] = group_name
         item_data['type'] = profile.group_type
         if profile.data:
             item_data['data'] = json.loads(profile.data)
         item_data['uri'] = '{}/{}'.format(generate_base_uri(request, True), profile.group_id)
         response_data.append(item_data)
     return Response(response_data, status=status.HTTP_200_OK)
Beispiel #12
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)
Beispiel #13
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)
Beispiel #14
0
 def get_uri(self, course):
     """
     Builds course detail uri
     """
     return "{}/{}".format(generate_base_uri(self.context["request"]), course.id)
Beispiel #15
0
    def post(self, request):
        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)
        try:
            existing_user = User.objects.get(username=request.DATA['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. '
                                             '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=request.DATA['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:
                    login(request, user)
                    response_data['token'] = request.session.session_key
                    response_data['expires'] = request.session.get_expiry_age()
                    user_dto = UserSerializer(user)
                    response_data['user'] = user_dto.data
                    response_data['uri'] = '{}/{}'.format(base_uri, request.session.session_key)
                    response_status = status.HTTP_201_CREATED

                    # add to audit log
                    AUDIT_LOG.info(u"API::User logged in successfully with user-id - {0}".format(user.id))
                else:
                    response_status = status.HTTP_403_FORBIDDEN
            else:
                limiter.tick_bad_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(u"API::User authentication failed with user-id - {0}".format(existing_user.id))
        else:
            AUDIT_LOG.warn(u"API::Failed login attempt with unknown email/username")
            response_status = status.HTTP_404_NOT_FOUND
        return Response(response_data, status=response_status)
Beispiel #16
0
    def post(self, request):
        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)
        try:
            existing_user = User.objects.get(username=request.DATA['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. '
                                             '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=request.DATA['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)
                    new_session = engine.SessionStore()
                    new_session.create()

                    # These values are expected to be set in any new session
                    new_session[SESSION_KEY] = user.id
                    new_session[BACKEND_SESSION_KEY] = user.backend

                    new_session.save()

                    response_data['token'] = new_session.session_key
                    response_data['expires'] = new_session.get_expiry_age()
                    user_dto = UserSerializer(user)
                    response_data['user'] = user_dto.data
                    response_data['uri'] = '{}/{}'.format(base_uri, new_session.session_key)
                    response_status = status.HTTP_201_CREATED

                    # generate a CSRF tokens for any web clients that may need to
                    # call into the LMS via Ajax (for example Notifications)
                    response_data['csrftoken'] = RequestContext(request, {}).get('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(u"API::User logged in successfully with user-id - {0}".format(user.id))
                else:
                    response_status = status.HTTP_403_FORBIDDEN
            else:
                limiter.tick_bad_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(u"API::User authentication failed with user-id - {0}".format(existing_user.id))
        else:
            AUDIT_LOG.warn(u"API::Failed login attempt with unknown email/username")
            response_status = status.HTTP_404_NOT_FOUND
        return Response(response_data, status=response_status)