Example #1
0
    def _read_user(self, userstr, ldap_user_read=None):
        match = self.USER_REGEX.match(userstr)
        if not match or not userstr:
            raise AttributeError('invalid key "%s"' % userstr)
        username = match.groupdict()['username']
        if not username:
            raise AttributeError('username missing: %s' % userstr)

        lo = ldap_user_read
        try:
            userobj = User.get_only_udm_obj(
                lo, filter_format('uid=%s', (username, )))
            if userobj is None:
                raise noObject(username)
            user = User.from_udm_obj(userobj, None, lo)
        except (noObject, MultipleObjectsError):
            MODULE.info('Unknown user "%s"' % username)
            dict.__setitem__(self, userstr, UserInfo('', ''))
            return

        blacklisted_groups = set([
            x.strip().lower() for x in ucr.get(
                'ucsschool/umc/computerroom/hide_screenshots/groups',
                'Domain Admins').split(',')
        ])
        users_groupmemberships = set(
            [explode_dn(x, True)[0].lower() for x in userobj['groups']])
        MODULE.info('UserMap: %s: hide screenshots for following groups: %s' %
                    (
                        username,
                        blacklisted_groups,
                    ))
        MODULE.info('UserMap: %s: user is member of following groups: %s' % (
            username,
            users_groupmemberships,
        ))
        hide_screenshot = bool(blacklisted_groups & users_groupmemberships)

        if ucr.is_true('ucsschool/umc/computerroom/hide_screenshots/teachers',
                       True) and user.is_teacher(lo):
            MODULE.info('UserMap: %s: is teacher hiding screenshot' %
                        (username, ))
            hide_screenshot = True

        MODULE.info('UserMap: %s: hide_screenshot=%r' %
                    (username, hide_screenshot))

        dict.__setitem__(
            self, userstr,
            UserInfo(user.dn,
                     username,
                     isTeacher=user.is_teacher(lo),
                     hide_screenshot=hide_screenshot))
Example #2
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
Example #3
0
    def query(self, request, ldap_user_read=None, ldap_position=None):
        """Searches for print jobs"""

        klass = request.options.get('class')
        if klass in (None, 'None'):
            klass = None
        students = self._users(ldap_user_read,
                               request.options['school'],
                               group=klass,
                               user_type='student',
                               pattern=request.options.get('pattern', ''))

        try:
            students.append(
                User.from_dn(self.user_dn, None,
                             ldap_user_read).get_udm_object(ldap_user_read))
        except udm_errors.noObject:
            MODULE.warn(
                'Could not get user object of teacher %r. Is it a UCS@school user?'
                % (self.user_dn, ))

        printjoblist = []

        for student in students:
            username = student.info['username']
            path_username = dict(
                (self._get_path(username, ''), username)
                for username in self._get_all_username_variants(username))
            for user_path, username in path_username.iteritems():
                printjoblist.extend(
                    Printjob(student, username, document).json()
                    for document in glob.glob(os.path.join(user_path, '*.pdf'))
                    if os.path.isfile(document))

        self.finished(request.id, printjoblist)
Example #4
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)
Example #5
0
	def delete_user(self, request, ldap_user_read=None, ldap_user_write=None, ldap_admin_write=None):
		# Bug #44641: workaround with security implications!
		if ucr.is_true('ucsschool/wizards/schoolwizards/workaround/admin-connection'):
			ldap_user_write = ldap_admin_write

		ret = []
		for obj_props in request.options:
			obj_props = obj_props['object']
			try:
				obj = User.from_dn(obj_props['$dn$'], None, ldap_user_write)
			except noObject:
				raise UMC_Error(_('The %s %r does not exists or might have been removed in the meanwhile.') % (getattr(User, 'type_name', None) or User.__name__, User.get_name_from_dn(obj_props['$dn$'])))
			school = obj_props['remove_from_school']
			success = obj.remove_from_school(school, ldap_user_write)
			# obj.old_dn is None when the ucsschool lib has deleted the user after the last school was removed from it
			if success and obj.old_dn is not None:
				success = obj.modify(ldap_user_write)
			if not success:
				success = {'result': {'message': _('Failed to remove user from school.')}}
			ret.append(success)
		return ret
Example #6
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)
Example #7
0
    def password_reset(self, request, ldap_user_write=None):
        '''Reset the password of the user'''
        userdn = request.options['userDN']
        pwdChangeNextLogin = request.options['nextLogin']
        newPassword = request.options['newPassword']

        try:
            user = User.from_dn(
                userdn, None, ldap_user_write).get_udm_object(ldap_user_write)
            user['password'] = newPassword
            user['overridePWHistory'] = '1'
            user['locked'] = 'none'
            user['pwdChangeNextLogin'] = '******' if pwdChangeNextLogin else '0'
            user.modify()
            self.finished(request.id, True)
        except udm_exceptions.permissionDenied as exc:
            MODULE.process('dn=%r' % (userdn, ))
            MODULE.process('exception=%s' % (type(exc), ))
            raise UMC_Error(_('permission denied'))
        except udm_exceptions.base as exc:
            MODULE.process('dn=%r' % (userdn, ))
            MODULE.process('exception=%s' % (type(exc), ))
            MODULE.process('exception=%s' % (exc.message, ))
            raise UMC_Error('%s' % (get_exception_msg(exc)))
Example #8
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)
Example #9
0
	def create_user(
		self, ou_name, schools=None, username=None, firstname=None, lastname=None, classes=None,
		mailaddress=None, is_teacher=False, is_staff=False, is_active=True, password='******',
		use_cli=False, wait_for_replication=True
	):
		"""
		Create a user in specified OU with given attributes. If attributes are not specified, random
		values will be used for username, firstname and lastname. If password is not None, the given
		password will be set for this user.

		Return value: (user_name, user_dn)
			user_name: name of the created user
			user_dn:   DN of the created user object
		"""
		if not ou_name:
			raise SchoolMissingOU('No OU name specified')

		# set default values
		if username is None:
			username = uts.random_username()
		if firstname is None:
			firstname = uts.random_string(length=10, numeric=False)
		if lastname is None:
			lastname = uts.random_string(length=10, numeric=False)
		if mailaddress is None:
			mailaddress = ''
		if schools is None:
			schools = [ou_name]

		user_dn = 'uid=%s,%s' % (username, self.get_user_container(ou_name, is_teacher, is_staff))
		if use_cli:
			if classes is None:
				classes = ''
			if classes:
				if not all(["-" in c for c in classes.split(',')]):
					utils.fail('*** Class names must be <school-ou>-<class-name>.')
			# create import file
			line = 'A\t%s\t%s\t%s\t%s\t%s\t\t%s\t%d\t%d\t%d\n' % (username, lastname, firstname, ou_name, classes, mailaddress, int(is_teacher), int(is_active), int(is_staff))
			with tempfile.NamedTemporaryFile() as tmp_file:
				tmp_file.write(line)
				tmp_file.flush()

				cmd = [self.PATH_CMD_IMPORT_USER, tmp_file.name]
				print '*** Calling following command: %r' % cmd
				retval = subprocess.call(cmd)
				if retval:
					utils.fail('create_ou failed with exitcode %s' % retval)

			if password is not None:
				self._set_password(user_dn, password)
		else:
			school_classes = defaultdict(list)
			if classes:
				for kls in classes.split(','):
					school_classes[kls.partition('-')[0]].append(kls)
			kwargs = {
				'school': ou_name,
				'schools': schools,
				'name': username,
				'firstname': firstname,
				'lastname': lastname,
				'email': mailaddress,
				'password': password,
				'disabled': not is_active,
				"school_classes": dict(school_classes)
			}
			print '*** Creating new user %r with %r.' % (username, kwargs)
			lo = self.open_ldap_connection()
			User.invalidate_all_caches()
			User.init_udm_module(lo)  # TODO FIXME has to be fixed in ucs-school-lib - should be done automatically
			cls = Student
			if is_teacher and is_staff:
				cls = TeachersAndStaff
			elif is_teacher and not is_staff:
				cls = Teacher
			elif not is_teacher and is_staff:
				cls = Staff
			result = cls(**kwargs).create(lo)
			print '*** Result of %s(...).create(): %r' % (cls.__name__, result,)

		if wait_for_replication:
			utils.wait_for_replication()

		return username, user_dn
Example #10
0
        def _thread():
            # make sure that a project with the same name does not exist
            directory = request.options['directory']
            # get absolute path of project file and test for existance
            fn_test_project = util.distribution.Project.sanitize_project_filename(
                directory)
            if os.path.exists(fn_test_project):
                raise UMC_Error(
                    _('An exam with the name "%s" already exists. Please choose a different name for the exam.'
                      ) % (directory, ))

            # validate the project data and save project
            my.project = util.distribution.Project(
                dict(
                    name=directory,
                    description=request.options['name'],
                    files=request.options.get('files'),
                    sender=sender,
                ))
            my.project.validate()
            my.project.save()

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

            # open a new connection to the master UMC
            try:
                master = ucr['ldap/master']
                client = Client(master)
                client.authenticate_with_machine_account()
            except (ConnectionError, HTTPError) as exc:
                MODULE.error('Could not connect to UMC on %s: %s' %
                             (master, exc))
                raise UMC_Error(
                    _('Could not connect to master server %s.') %
                    ucr.get('ldap/master'))

            # mark the computer room for exam mode
            progress.component(
                _('Preparing the computer room for exam mode...'))
            client.umc_command(
                'schoolexam-master/set-computerroom-exammode',
                dict(
                    school=request.options['school'],
                    roomdn=request.options['room'],
                )).result  # FIXME: no error handling
            progress.add_steps(5)

            # read all recipients and fetch all user objects
            users = []
            for idn in request.options['recipients']:
                ientry = util.distribution.openRecipients(idn, ldap_user_read)
                if not ientry:
                    continue
                # recipients can in theory be users or groups
                members = []
                if isinstance(ientry, util.distribution.User):
                    members = [ientry]
                elif isinstance(ientry, util.distribution.Group):
                    members = ientry.members
                for entry in members:
                    # ignore exam users
                    user = User.from_dn(entry.dn, None, ldap_user_read)
                    if not user.is_exam_student(ldap_user_read):
                        users.append(entry)

            # start to create exam user accounts
            progress.component(_('Preparing exam accounts'))
            percentPerUser = 25.0 / (1 + len(users))
            examUsers = set()
            student_dns = set()
            usersReplicated = set()
            for iuser in users:
                progress.info(
                    '%s, %s (%s)' %
                    (iuser.lastname, iuser.firstname, iuser.username))
                try:
                    ires = client.umc_command(
                        'schoolexam-master/create-exam-user',
                        dict(
                            school=request.options['school'],
                            userdn=iuser.dn,
                        )).result
                    examuser_dn = ires.get('examuserdn')
                    examUsers.add(examuser_dn)
                    student_dns.add(iuser.dn)
                    MODULE.info('Exam user has been created: %r' % examuser_dn)
                except (ConnectionError, HTTPError) as exc:
                    MODULE.warn(
                        'Could not create exam user account for %r: %s' %
                        (iuser.dn, exc))

                # indicate the the user has been processed
                progress.add_steps(percentPerUser)

            client.umc_command(
                'schoolexam-master/add-exam-users-to-groups',
                dict(
                    users=list(student_dns),
                    school=request.options['school'],
                ))

            progress.add_steps(percentPerUser)

            # wait for the replication of all users to be finished
            progress.component(_('Preparing user home directories'))
            recipients = []  # list of User objects for all exam users
            openAttempts = 30 * 60  # wait max. 30 minutes for replication
            while (len(examUsers) > len(usersReplicated)) and (openAttempts >
                                                               0):
                openAttempts -= 1
                MODULE.info(
                    'waiting for replication to be finished, %s user objects missing'
                    % (len(examUsers) - len(usersReplicated)))
                for idn in examUsers - usersReplicated:
                    try:
                        ldap_user_read.get(idn, required=True)
                    except ldap.NO_SUCH_OBJECT:
                        continue  # not replicated yet
                    iuser = util.distribution.openRecipients(
                        idn, ldap_user_read)
                    if not iuser:
                        continue  # not a users/user object
                    MODULE.info('user has been replicated: %s' % idn)

                    # call hook scripts
                    if 0 != subprocess.call([
                            '/bin/run-parts', CREATE_USER_POST_HOOK_DIR,
                            '--arg', iuser.username, '--arg', iuser.dn,
                            '--arg', iuser.homedir
                    ]):
                        raise ValueError(
                            'failed to run hook scripts for user %r' %
                            (iuser.username))

                    # store User object in list of final recipients
                    recipients.append(iuser)

                    # mark the user as replicated
                    usersReplicated.add(idn)
                    progress.info(
                        '%s, %s (%s)' %
                        (iuser.lastname, iuser.firstname, iuser.username))
                    progress.add_steps(percentPerUser)

                # wait a second
                time.sleep(1)

            progress.add_steps(percentPerUser)

            if openAttempts <= 0:
                MODULE.error(
                    'replication timeout - %s user objects missing: %r ' %
                    ((len(examUsers) - len(usersReplicated)),
                     (examUsers - usersReplicated)))
                raise UMC_Error(
                    _('Replication timeout: could not create all exam users'))

            # update the final list of recipients
            my.project.recipients = recipients
            my.project.save()

            # update local NSS group cache
            if ucr.is_true('nss/group/cachefile', True):
                cmd = ['/usr/lib/univention-pam/ldap-group-to-file.py']
                if ucr.is_true('nss/group/cachefile/check_member', False):
                    cmd.append('--check_member')
                MODULE.info('Updating local nss group cache...')
                if subprocess.call(cmd):
                    MODULE.error('Updating local nss group cache failed: %s' %
                                 ' '.join(cmd))
                else:
                    MODULE.info(
                        'Update of local nss group cache finished successfully.'
                    )

            # distribute exam files
            progress.component(_('Distributing exam files'))
            progress.info('')
            my.project.distribute()
            progress.add_steps(20)

            # prepare room settings via UMCP...
            #   first step: acquire room
            #   second step: adjust room settings
            progress.component(_('Prepare room settings'))
            try:
                user_client = Client(None, self.username, self.password)
            except (ConnectionError, HTTPError) as exc:
                MODULE.warn('Authentication failed: %s' % (exc, ))
                raise UMC_Error(_('Could not connect to local UMC server.'))

            room = request.options['room']
            MODULE.info('Acquire room: %s' % (room, ))
            user_client.umc_command('computerroom/room/acquire',
                                    dict(
                                        room=request.options['room'], )).result
            progress.add_steps(1)
            MODULE.info('Adjust room settings:\n%s' % '\n'.join(
                ['  %s=%s' % (k, v) for k, v in request.options.iteritems()]))
            user_client.umc_command(
                'computerroom/exam/start',
                dict(
                    room=room,
                    examDescription=request.options['name'],
                    exam=directory,
                    examEndTime=request.options.get('examEndTime'),
                )).result
            progress.add_steps(4)
            user_client.umc_command(
                'computerroom/settings/set',
                dict(
                    room=room,
                    internetRule=request.options['internetRule'],
                    customRule=request.options.get('customRule'),
                    shareMode=request.options['shareMode'],
                    printMode='default',
                )).result
            progress.add_steps(5)