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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
def get_uri(self, course): """ Builds course detail uri """ return "{}/{}".format(generate_base_uri(self.context["request"]), course.id)
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)
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)