Exemple #1
0
 def _filter_members(request, group, users, ldap_user_read=None):
     members = []
     for member_dn in users:
         try:
             user = User.from_dn(member_dn, None, ldap_user_read)
         except udm_exceptions.noObject:
             MODULE.process(
                 'Could not open (foreign) user %r: no permissions/does not exists/not a user'
                 % (member_dn, ))
             continue
         if not user.schools or not set(user.schools) & {group.school}:
             continue
         if request.flavor == 'class' and not user.is_teacher(
                 ldap_user_read):
             continue  # only display teachers
         elif request.flavor == 'workgroup' and not user.is_student(
                 ldap_user_read):
             continue  # only display students
         elif request.flavor == 'workgroup-admin' and not user.is_student(
                 ldap_user_read) and not user.is_administrator(
                     ldap_user_read) and not user.is_staff(
                         ldap_user_read) and not user.is_teacher(
                             ldap_user_read):
             continue  # only display school users
         members.append({
             'id':
             user.dn,
             'label':
             Display.user(user.get_udm_object(ldap_user_read))
         })
     return members
Exemple #2
0
	def get(self, request, ldap_user_read=None, ldap_position=None):
		"""Returns the objects for the given IDs

		requests.options = [ <ID>, ... ]

		return: [ { ... }, ... ]
		"""
		# try to load all given projects
		result = []
		# list of all project properties (dicts) or None if project is not valid
		for iproject in [util.Project.load(iid) for iid in request.options]:
			# make sure that project could be loaded
			if not iproject:
				result.append(None)
				continue

			# make sure that only the project owner himself (or an admin) is able
			# to see the content of a project
			if request.flavor == 'teacher' and not compare_dn(iproject.sender.dn, self.user_dn):
				raise UMC_Error(_('Project details are only visible to the project owner himself or an administrator.'), status=403)

			# prepare date and time properties for distribution/collection of project files
			props = iproject.dict
			for jjob, jsuffix in ((iproject.atJobDistribute, 'distribute'), (iproject.atJobCollect, 'collect')):
				MODULE.info('check job: %s' % jsuffix)
				if not jjob:
					# no job is registered -> manual job distribution/collection
					MODULE.info('no existing job -> manual execution')
					props['%sType' % jsuffix] = 'manual'
					continue

				# job is registered -> prepare date and time fields
				MODULE.info('job nr #%d scheduled for %s -> automatic execution' % (jjob.nr, jjob.execTime))
				props['%sType' % jsuffix] = 'automatic'
				props['%sDate' % jsuffix] = datetime.strftime(jjob.execTime, '%Y-%m-%d')
				props['%sTime' % jsuffix] = datetime.strftime(jjob.execTime, '%H:%M')

			# adjust sender / recipients properties
			props['sender'] = props['sender'].username
			recipients = []
			for recip in props['recipients']:
				recipients.append({
					'id': recip.dn,
					'label': recip.type == util.TYPE_USER and Display.user(recip.dict) or recip.name
				})
			props['recipients'] = recipients

			# append final dict to result list
			MODULE.info('final project dict: %s' % props)
			result.append(props)
		self.finished(request.id, result)
Exemple #3
0
    def query(self, request, ldap_user_read=None, ldap_position=None):
        """Searches for students"""

        klass = request.options.get('class')
        if klass in (None, 'None'):
            klass = None
        result = [{
            'id': usr.dn,
            'name': Display.user(usr),
            'passwordexpiry': usr.get('passwordexpiry', '')
        } for usr in self._users(ldap_user_read,
                                 request.options['school'],
                                 group=klass,
                                 user_type=request.flavor,
                                 pattern=request.options.get('pattern', ''))]
        self.finished(request.id, result)
Exemple #4
0
 def json(self):
     return {
         'id':
         self.fullfilename,
         'username':
         self.username,
         'user':
         Display.user(self.owner),
         'printjob':
         self.name,
         'filename':
         self.filename,
         'date': (self.ctime.year, self.ctime.month, self.ctime.day,
                  self.ctime.hour, self.ctime.minute, self.ctime.second),
         'pages':
         self.metadata.get('pages')
     }
Exemple #5
0
    def update(self, request, ldap_user_read=None):
        """Returns an update for the computers in the selected
		room. Just attributes that have changed since the last call will
		be included in the result
		"""

        if not self._italc.school or not self._italc.room:
            raise UMC_Error('no room selected')

        computers = [
            computer.dict for computer in self._italc.values()
            if computer.hasChanged
        ]
        info = _readRoomInfo(self._italc.roomDN)
        result = {
            'computers': computers,
            'room_info': info,
            'locked': info.get('user', self.user_dn) != self.user_dn,
            'user': self.user_dn,
        }

        if result['locked'] and 'pid' in info:
            result['user'] = info['user']
            # somebody else acquired the room, the room is locked
            try:
                # open the corresponding UDM object to get a displayable user name
                result['user'] = Display.user(
                    User.from_dn(
                        result['user'], None,
                        ldap_user_read).get_udm_object(ldap_user_read))
            except udm_exceptions.base as exc:
                # could not oben the LDAP object, show the DN
                MODULE.warn('Cannot open LDAP information for user %r: %s' %
                            (result['user'], exc))

        # settings info
        if self._ruleEndAt is not None:
            diff = self._positiveTimeDiff()
            if diff is not None:
                result['settingEndsIn'] = diff.seconds / 60

        MODULE.info('Update: result: %s' % str(result))
        self.finished(request.id, result)
Exemple #6
0
    def users(self, request, ldap_user_read=None, ldap_position=None):
        # parse group parameter
        group = request.options.get('group')
        user_type = None
        if not group or group == 'None':
            group = None
        elif group.lower() in ('teacher', 'student'):
            user_type = group.lower()
            group = None

        result = [{
            'id': i.dn,
            'label': Display.user(i)
        } for i in self._users(ldap_user_read,
                               request.options['school'],
                               group=group,
                               user_type=user_type,
                               pattern=request.options['pattern'])]
        self.finished(request.id, result)
Exemple #7
0
	def distribute(self, request):
		# update the sender information of the selected projects
		result = []
		for iid in request.options:
			MODULE.info('Distribute project: %s' % iid)
			try:
				# make sure that project could be loaded
				iproject = util.Project.load(iid)
				if not iproject:
					raise IOError(_('Project "%s" could not be loaded') % iid)

				# make sure that only the project owner himself (or an admin) is able
				# to distribute a project
				if request.flavor == 'teacher' and not compare_dn(iproject.sender.dn, self.user_dn):
					raise ValueError(_('Only the owner himself or an administrator may distribute a project.'))

				# project was loaded successfully... try to distribute it
				usersFailed = []
				iproject.distribute(usersFailed)

				# raise an error in case distribution failed for some users
				if usersFailed:
					MODULE.info('Failed processing the following users: %s' % usersFailed)
					usersStr = ', '.join([Display.user(i) for i in usersFailed])
					raise IOError(_('The project could not distributed to the following users: %s') % usersStr)

				# save result
				result.append(dict(
					name=iid,
					success=True
				))
			except (ValueError, IOError) as exc:
				result.append(dict(
					name=iid,
					success=False,
					details=str(exc)
				))

		# return the results
		self.finished(request.id, result)
Exemple #8
0
    def rooms(self, request, ldap_user_read=None):
        """Returns a list of all available rooms"""
        rooms = []
        try:
            all_rooms = ComputerRoom.get_all(ldap_user_read,
                                             request.options['school'])
        except udm_exceptions.noObject:
            all_rooms = []

        for room in all_rooms:
            room_info = _readRoomInfo(room.dn)
            user_dn = room_info.get('user')

            locked = user_dn and not compare_dn(user_dn, self.user_dn) and (
                'pid' in room_info or 'exam' in room_info)
            if locked:
                try:
                    # open the corresponding UDM object to get a displayable user name
                    user_dn = Display.user(
                        User.from_dn(
                            user_dn, None,
                            ldap_user_read).get_udm_object(ldap_user_read))
                except udm_exceptions.base as exc:
                    MODULE.warn(
                        'Cannot open LDAP information for user %r: %s' %
                        (user_dn, exc))

            rooms.append({
                'id': room.dn,
                'label': room.get_relative_name(),
                'user': user_dn,
                'locked': locked,
                'exam': room_info.get('exam'),
                'examDescription': room_info.get('examDescription'),
                'examEndTime': room_info.get('examEndTime'),
            })

        self.finished(request.id, rooms)
Exemple #9
0
    def put(self,
            request,
            ldap_machine_write=None,
            ldap_user_read=None,
            ldap_position=None):
        """Returns the objects for the given IDs

		requests.options = [ { object : ..., options : ... }, ... ]

		return: True|<error message>
		"""

        if request.flavor == 'teacher':
            request.options = request.options[0]['object']
            return self.add_teacher_to_classes(request)

        klass = get_group_class(request)
        for group_from_umc in request.options:
            group_from_umc = group_from_umc['object']
            group_from_umc_dn = group_from_umc['$dn$']
            break

        try:
            group_from_ldap = klass.from_dn(group_from_umc_dn, None,
                                            ldap_machine_write)
        except udm_exceptions.noObject:
            raise UMC_Error('unknown group object')

        old_members = self._filter_members(request, group_from_ldap,
                                           group_from_ldap.users,
                                           ldap_user_read)
        removed_members = set(o['id'] for o in old_members) - set(
            group_from_umc['members'])

        MODULE.info('Modifying group "%s" with members: %s' %
                    (group_from_ldap.dn, group_from_ldap.users))
        MODULE.info('New members: %s' % group_from_umc['members'])
        MODULE.info('Removed members: %s' % (removed_members, ))

        if request.flavor == 'workgroup-admin':
            # do not allow groups to be renamed in order to avoid conflicts with shares
            # grp.name = '%(school)s-%(name)s' % group
            group_from_ldap.description = group_from_umc['description']

        # Workgroup admin view → update teachers, admins, students, (staff)
        # Class view → update only the group's teachers (keep all non teachers)
        # Workgroup teacher view → update only the group's students

        users = []
        # keep specific users from the group
        for userdn in group_from_ldap.users:
            try:
                user = User.from_dn(userdn, None, ldap_machine_write)
            except udm_exceptions.noObject:  # no permissions/is not a user/does not exists → keep the old value
                users.append(userdn)
                continue
            if not user.schools or not set(user.schools) & set(
                [group_from_ldap.school]):
                users.append(userdn)
                continue
            if (request.flavor == 'class'
                    and not user.is_teacher(ldap_machine_write)) or (
                        request.flavor == 'workgroup'
                        and not user.is_student(ldap_machine_write)
                    ) or request.flavor == 'workgroup-admin':
                users.append(userdn)

        # add only certain users to the group
        for userdn in group_from_umc['members']:
            try:
                user = User.from_dn(userdn, None, ldap_machine_write)
            except udm_exceptions.noObject as exc:
                MODULE.error('Not adding not existing user %r to group: %r.' %
                             (userdn, exc))
                continue
            if not user.schools or not set(user.schools) & set(
                [group_from_ldap.school]):
                raise UMC_Error(
                    _('User %s does not belong to school %r.') %
                    (Display.user(user.get_udm_object(ldap_machine_write)),
                     group_from_ldap.school))
            if request.flavor == 'workgroup-admin' and not user.is_student(
                    ldap_machine_write) and not user.is_administrator(
                        ldap_machine_write) and not user.is_staff(
                            ldap_machine_write) and not user.is_teacher(
                                ldap_machine_write):
                raise UMC_Error(
                    _('User %s does not belong to school %r.') %
                    (Display.user(user.get_udm_object(ldap_machine_write)),
                     group_from_ldap.school))
            if request.flavor == 'class' and not user.is_teacher(
                    ldap_machine_write):
                raise UMC_Error(
                    _('User %s is not a teacher.') %
                    (Display.user(user.get_udm_object(ldap_machine_write)), ))
            if request.flavor == 'workgroup' and not user.is_student(
                    ldap_machine_write):
                raise UMC_Error(
                    _('User %s is not a student.') %
                    (Display.user(user.get_udm_object(ldap_machine_write)), ))
            users.append(user.dn)

        group_from_ldap.users = list(set(users) - removed_members)
        try:
            success = group_from_ldap.modify(ldap_machine_write)
            MODULE.info('Modified, group has now members: %s' %
                        (group_from_ldap.users, ))
        except udm_exceptions.base as exc:
            MODULE.process('An error occurred while modifying "%s": %s' %
                           (group_from_umc['$dn$'], exc.message))
            raise UMC_Error(_('Failed to modify group (%s).') % exc.message)

        self.finished(request.id, success)
Exemple #10
0
	def _save(self, iprops, doUpdate=True, ldap_user_read=None, ldap_position=None):
		# try to open the UDM user object of the current user
		sender = self._get_sender()

		try:
			# remove keys that may not be set from outside
			for k in ('atJobNumCollect', 'atJobNumDistribute'):
				iprops.pop(k, None)

			# transform filenames into bytestrings
			iprops['files'] = [f.encode('UTF-8') for f in iprops.get('files', [])]

			# load the project or create a new one
			project = None
			orgProject = None
			if doUpdate:
				# try to load the given project
				orgProject = util.Project.load(iprops.get('name', ''))
				if not orgProject:
					raise UMC_Error(_('The specified project does not exist: %s') % iprops['name'])

				# create a new project with the updated values
				project = util.Project(orgProject.dict)
				project.update(iprops)
			else:
				# create a new project
				project = util.Project(iprops)

			# make sure that the project owner himself is modifying the project
			if doUpdate and not compare_dn(project.sender.dn, self.user_dn):
				raise UMC_Error(_('The project can only be modified by the owner himself'))

			# handle time settings for distribution/collection of project files
			for jsuffix, jprop, jname in (('distribute', 'starttime', _('Project distribution')), ('collect', 'deadline', _('Project collection'))):
				if '%sType' % jsuffix in iprops:
					# check the distribution/collection type: manual/automat
					jtype = (iprops['%sType' % jsuffix]).lower()
					if jtype == 'automatic':
						try:
							# try to parse the given time parameters
							strtime = '%s %s' % (iprops['%sDate' % jsuffix], iprops['%sTime' % jsuffix])
							jdate = datetime.strptime(strtime, '%Y-%m-%d %H:%M')
							setattr(project, jprop, jdate)
						except ValueError:
							raise UMC_Error(_('Could not set date for: %s') % jname)

						# make sure the execution time lies sufficiently in the future
						if getattr(project, jprop) - datetime.now() < timedelta(minutes=1):
							raise UMC_Error(_('The specified time needs to lie in the future for: %s') % jname)
					else:
						# manual distribution/collection
						setattr(project, jprop, None)

			if project.starttime and project.deadline:
				# make sure distributing happens before collecting
				if project.deadline - project.starttime < timedelta(minutes=3):
					raise UMC_Error(_('Distributing the data needs to happen sufficiently long enough before collecting them'))

			if 'recipients' in iprops:
				# lookup the users in LDAP and save them to the project
				project.recipients = [util.openRecipients(idn, ldap_user_read) for idn in iprops.get('recipients', [])]
				project.recipients = [x for x in project.recipients if x]
				MODULE.info('recipients: %s' % (project.recipients,))

			if not doUpdate:
				# set the sender (i.e., owner) of the project
				project.sender = sender

			# initiate project and validate its values
			project.validate()

			# make sure that there is no other project with the same directory name
			# if we add new projects
			if not doUpdate and project.isNameInUse():
				MODULE.error('The project name is already in use: %s' % (project.name))
				raise UMC_Error(_('The specified project directory name "%s" is already in use by a different project.') % (project.name))

			# try to save project to disk
			project.save()

			# move new files into project directory
			if self._tmpDir:
				for ifile in project.files:
					isrc = os.path.join(self._tmpDir, ifile)
					itarget = os.path.join(project.cachedir, ifile)
					if os.path.exists(isrc):
						# mv file to cachedir
						shutil.move(isrc, itarget)
						os.chown(itarget, 0, 0)

			# remove files that have been marked for removal
			if doUpdate:
				for ifile in set(orgProject.files) - set(project.files):
					itarget = os.path.join(project.cachedir, ifile)
					try:
						os.remove(itarget)
					except OSError:
						pass

			# re-distribute the project in case it has already been distributed
			if doUpdate and project.isDistributed:
				usersFailed = []
				project.distribute(usersFailed)

				if usersFailed:
					# not all files could be distributed
					MODULE.info('Failed processing the following users: %s' % usersFailed)
					usersStr = ', '.join([Display.user(i) for i in usersFailed])
					raise UMC_Error(_('The project could not distributed to the following users: %s') % usersStr)
		except (IOError, OSError, UMC_Error):  # TODO: catch only UMC_Error
			etype, exc, etraceback = sys.exc_info()
			# data not valid... create error info
			MODULE.info('data for project "%s" is not valid: %s' % (iprops.get('name'), exc))

			if not doUpdate:
				# remove eventually created project file and cache dir
				for ipath in (project.projectfile, project.cachedir):
					if os.path.basename(ipath) not in os.listdir(util.DISTRIBUTION_DATA_PATH):
						# no file / directory has been created yet
						continue
					try:
						MODULE.info('cleaning up... removing: %s' % ipath)
						shutil.rmtree(ipath)
					except (IOError, OSError):
						pass
			raise UMC_Error, exc, etraceback
		self._cleanTmpDir()
		return {'success': True, 'name': iprops.get('name')}