예제 #1
0
class BofhdExtension(BofhdCommonMethods, BofhdEmailMixin):

    all_commands = {}
    external_id_mappings = {}
    parent_commands = True
    authz = BofhdAuth

    def __init__(self, *args, **kwargs):
        super(BofhdExtension, self).__init__(*args, **kwargs)
        self.external_id_mappings['fnr'] = self.const.externalid_fodselsnr

    @classmethod
    def get_help_strings(cls):
        return bofhd_core_help.get_help_strings()

    #
    # person student_info
    #
    all_commands['person_student_info'] = cmd_param.Command(
        ("person", "student_info"),
        cmd_param.PersonId(),
        fs=cmd_param.FormatSuggestion([
            ("Studieprogrammer: %s, %s, %s, %s, "
             "tildelt=%s->%s privatist: %s",
             ("studprogkode", "studieretningkode", "studierettstatkode",
              "studentstatkode", format_day("dato_tildelt"),
              format_day("dato_gyldig_til"), "privatist")),
            ("Eksamensmeldinger: %s (%s), %s", ("ekskode", "programmer",
                                                format_day("dato"))),
            ("Utd. plan: %s, %s, %d, %s",
             ("studieprogramkode", "terminkode_bekreft", "arstall_bekreft",
              format_day("dato_bekreftet"))),
            ("Semesterreg: %s, %s, FS bet. reg: %s, endret: %s",
             ("regformkode", "betformkode", format_day("dato_endring"),
              format_day("dato_regform_endret"))),
        ]),
        perm_filter='can_get_student_info')

    def person_student_info(self, operator, person_id):
        person = self._get_person(*self._map_person_id(person_id))
        self.ba.can_get_student_info(operator.get_entity_id(), person)
        fnr = person.get_external_id(id_type=self.const.externalid_fodselsnr,
                                     source_system=self.const.system_fs)
        if not fnr:
            raise CerebrumError("No matching fnr from FS")
        fodselsdato, pnum = fodselsnr.del_fnr(fnr[0]['external_id'])
        har_opptak = {}
        ret = []
        try:
            fs_db = make_fs()
        except Database.DatabaseError, e:
            self.logger.warn("Can't connect to FS (%s)" % e)
            raise CerebrumError("Can't connect to FS, try later")
        for row in fs_db.student.get_studierett(fodselsdato, pnum):
            har_opptak[str(row['studieprogramkode'])] = row['status_privatist']
            ret.append({
                'studprogkode':
                row['studieprogramkode'],
                'studierettstatkode':
                row['studierettstatkode'],
                'studentstatkode':
                row['studentstatkode'],
                'studieretningkode':
                row['studieretningkode'],
                'dato_tildelt':
                self._convert_ticks_to_timestamp(
                    row['dato_studierett_tildelt']),
                'dato_gyldig_til':
                self._convert_ticks_to_timestamp(
                    row['dato_studierett_gyldig_til']),
                'privatist':
                row['status_privatist']
            })

        for row in fs_db.student.get_eksamensmeldinger(fodselsdato, pnum):
            programmer = []
            for row2 in fs_db.info.get_emne_i_studieprogram(row['emnekode']):
                if str(row2['studieprogramkode']) in har_opptak:
                    programmer.append(row2['studieprogramkode'])
            ret.append({
                'ekskode':
                row['emnekode'],
                'programmer':
                ",".join(programmer),
                'dato':
                self._convert_ticks_to_timestamp(row['dato_opprettet'])
            })

        for row in fs_db.student.get_utdanningsplan(fodselsdato, pnum):
            ret.append({
                'studieprogramkode':
                row['studieprogramkode'],
                'terminkode_bekreft':
                row['terminkode_bekreft'],
                'arstall_bekreft':
                row['arstall_bekreft'],
                'dato_bekreftet':
                self._convert_ticks_to_timestamp(row['dato_bekreftet'])
            })

        for row in fs_db.student.get_semreg(fodselsdato, pnum):
            ret.append({
                'regformkode':
                row['regformkode'],
                'betformkode':
                row['betformkode'],
                'dato_endring':
                self._convert_ticks_to_timestamp(row['dato_endring']),
                'dato_regform_endret':
                self._convert_ticks_to_timestamp(row['dato_regform_endret'])
            })
        return ret
예제 #2
0
class BofhdExtension(BofhdCommonMethods):

    OU_class = Utils.Factory.get('OU')
    Account_class = Factory.get('Account')
    Group_class = Factory.get('Group')

    all_commands = {}
    parent_commands = True
    external_id_mappings = {}
    authz = NihAuth

    def __init__(self, *args, **kwargs):
        super(BofhdExtension, self).__init__(*args, **kwargs)
        self.external_id_mappings['fnr'] = self.const.externalid_fodselsnr

    @classmethod
    def get_help_strings(cls):
        return merge_help_strings(get_help_strings(), ({}, HELP_CMDS, {}))

    #
    # person student_info
    #
    all_commands['person_student_info'] = cmd_param.Command(
        ("person", "student_info"),
        cmd_param.PersonId(),
        fs=cmd_param.FormatSuggestion([
            ("Studieprogrammer: %s, %s, %s, %s, "
             "tildelt=%s->%s privatist: %s", (
                 "studprogkode",
                 "studieretningkode",
                 "studierettstatkode",
                 "studentstatkode",
                 format_day("dato_tildelt"),
                 format_day("dato_gyldig_til"),
                 "privatist",
             )),
            ("Eksamensmeldinger: %s (%s), %s", ("ekskode", "programmer",
                                                format_day("dato"))),
            ("Utd. plan: %s, %s, %d, %s",
             ("studieprogramkode", "terminkode_bekreft", "arstall_bekreft",
              format_day("dato_bekreftet"))),
            ("Semesterreg: %s, %s, FS bet. reg: %s, endret: %s",
             ("regformkode", "betformkode", format_day("dato_endring"),
              format_day("dato_regform_endret")))
        ]),
        perm_filter='can_get_student_info')

    def person_student_info(self, operator, person_id):
        person = self._get_person(*self._map_person_id(person_id))
        self.ba.can_get_student_info(operator.get_entity_id(), person)
        fnr = person.get_external_id(id_type=self.const.externalid_fodselsnr,
                                     source_system=self.const.system_fs)
        if not fnr:
            raise CerebrumError("No matching fnr from FS")
        fodselsdato, pnum = fodselsnr.del_fnr(fnr[0]['external_id'])
        ret = []
        try:
            fs_db = make_fs()
        except database.DatabaseError as e:
            self.logger.warn("Can't connect to FS (%s)" % e)
            raise CerebrumError("Can't connect to FS, try later")

        har_opptak = set()
        for row in fs_db.student.get_studierett(fodselsdato, pnum):
            har_opptak.add(row['studieprogramkode'])
            ret.append({
                'studprogkode':
                row['studieprogramkode'],
                'studierettstatkode':
                row['studierettstatkode'],
                'studentstatkode':
                row['studentstatkode'],
                'studieretningkode':
                row['studieretningkode'],
                'dato_tildelt':
                self._ticks_to_date(row['dato_studierett_tildelt']),
                'dato_gyldig_til':
                self._ticks_to_date(row['dato_studierett_gyldig_til']),
                'privatist':
                row['status_privatist'],
            })

        for row in fs_db.student.get_eksamensmeldinger(fodselsdato, pnum):
            programmer = []
            for row2 in fs_db.info.get_emne_i_studieprogram(row['emnekode']):
                if row2['studieprogramkode'] in har_opptak:
                    programmer.append(row2['studieprogramkode'])
            ret.append({
                'ekskode': row['emnekode'],
                'programmer': ",".join(programmer),
                'dato': self._ticks_to_date(row['dato_opprettet']),
            })

        for row in fs_db.student.get_utdanningsplan(fodselsdato, pnum):
            ret.append({
                'studieprogramkode':
                row['studieprogramkode'],
                'terminkode_bekreft':
                row['terminkode_bekreft'],
                'arstall_bekreft':
                row['arstall_bekreft'],
                'dato_bekreftet':
                self._ticks_to_date(row['dato_bekreftet']),
            })

        for row in fs_db.student.get_semreg(fodselsdato, pnum):
            ret.append({
                'regformkode':
                row['regformkode'],
                'betformkode':
                row['betformkode'],
                'dato_endring':
                self._ticks_to_date(row['dato_endring']),
                'dato_regform_endret':
                self._ticks_to_date(row['dato_regform_endret']),
            })
        return ret

    #
    # user delete
    #
    all_commands['user_delete'] = cmd_param.Command(
        ("user", "delete"),
        cmd_param.AccountName(),
        perm_filter='can_delete_user')

    def user_delete(self, operator, accountname):
        account = self._get_account(accountname)
        self.ba.can_delete_user(operator.get_entity_id(), account)
        if account.is_deleted():
            raise CerebrumError("User is already deleted")
        account.expire_date = mx.DateTime.now()
        for s in account.get_spread():
            account.delete_spread(int(s['spread']))
        account.write_db()
        return "User %s queued for deletion immediately" % account.account_name
예제 #3
0
class BofhdExtension(BofhdCommonMethods):

    external_id_mappings = {}
    all_commands = {}
    parent_commands = True
    authz = BofhdAuth

    def __init__(self, *args, **kwargs):
        super(BofhdExtension, self).__init__(*args, **kwargs)
        self.external_id_mappings['fnr'] = self.const.externalid_fodselsnr

        # Quick fix to replace the `person_find_uio` hack
        self.__uio_impl = bofhd_uio_cmds.BofhdExtension(*args, **kwargs)

    @property
    def person(self):
        try:
            return self.__person
        except AttributeError:
            self.__person = Factory.get('Person')(self.db)
            return self.__person

    @property
    def ou(self):
        try:
            return self.__ou
        except AttributeError:
            self.__ou = Factory.get('OU')(self.db)
            return self.__ou

    @staticmethod
    def get_help_strings():
        return (bofhd_go_help.group_help, bofhd_go_help.command_help,
                bofhd_go_help.arg_help)

    # IVR 2007-03-12 We override UiO's behaviour (since there are no
    # PosixUsers in Indigo by default). Ideally, UiO's bofhd should be split
    # into manageable units that can be plugged in on demand
    all_commands['_group_remove_entity'] = None

    def _group_remove_entity(self, operator, member, group):
        self.ba.can_alter_group(operator.get_entity_id(), group)
        member_name = self._get_name_from_object(member)
        if not group.has_member(member.entity_id):
            return ("%s isn't a member of %s" %
                    (member_name, group.group_name))
        try:
            group.remove_member(member.entity_id)
        except self.db.DatabaseError as m:
            raise CerebrumError("Database error: %s" % m)
        return "OK, removed '%s' from '%s'" % (member_name, group.group_name)

    #
    # group info
    #
    all_commands['group_info'] = cmd_param.Command(
        ("group", "info"),
        cmd_param.GroupName(help_ref="id:gid:name"),
        fs=cmd_param.FormatSuggestion([
            ("Name:         %s\n"
             "Spreads:      %s\n"
             "Description:  %s\n"
             "Expire:       %s\n"
             "Entity id:    %i", ("name", "spread", "description",
                                  format_day("expire_date"), "entity_id")),
            ("Admin:        %s %s", ('admin_type', 'admin')),
            ("Moderator:    %s %s", ('mod_type', 'mod')),
            ("Gid:          %i", ('gid', )),
            ("Members:      %s", ("members", ))
        ]))

    def group_info(self, operator, groupname):
        grp = self._get_group(groupname)
        co = self.const
        ret = [
            self._entity_info(grp),
        ]
        roles = GroupRoles(self.db)
        # find admins
        for row in roles.search_admins(group_id=grp.entity_id):
            id = int(row['admin_id'])
            en = self._get_entity(ident=id)
            if en.entity_type == co.entity_account:
                admin = en.account_name
            elif en.entity_type == co.entity_group:
                admin = en.group_name
            else:
                admin = '#%d' % id
            ret.append({
                'admin_type': text_type(co.EntityType(en.entity_type)),
                'admin': admin,
            })
        # find moderators
        for row in roles.search_moderators(group_id=grp.entity_id):
            id = int(row['moderator_id'])
            en = self._get_entity(ident=id)
            if en.entity_type == co.entity_account:
                mod = en.account_name
            elif en.entity_type == co.entity_group:
                mod = en.group_name
            else:
                mod = '#%d' % id
            ret.append({
                'mod_type': text_type(co.EntityType(en.entity_type)),
                'mod': mod,
            })

        # Count group members of different types
        members = list(grp.search_members(group_id=grp.entity_id))
        tmp = {}
        for ret_pfix, entity_type in (('c_group', int(co.entity_group)),
                                      ('c_account', int(co.entity_account))):
            tmp[ret_pfix] = len(
                [x for x in members if int(x["member_type"]) == entity_type])
        ret.append(tmp)
        return ret

    #
    # group user
    #
    all_commands['group_user'] = cmd_param.Command(
        ('group', 'user'),
        cmd_param.AccountName(),
        fs=cmd_param.FormatSuggestion("%-9s %-18s", ("memberop", "group")))

    def group_user(self, operator, accountname):
        return self.group_memberships(operator, 'account', accountname)

    #
    # get_auth_level
    #
    all_commands['get_auth_level'] = None

    def get_auth_level(self, operator):
        if self.ba.is_superuser(operator.get_entity_id()):
            return cereconf.INDIGO_AUTH_LEVEL['super']

        if self.ba.is_schoolit(operator.get_entity_id(), True):
            return cereconf.INDIGO_AUTH_LEVEL['schoolit']

        return cereconf.INDIGO_AUTH_LEVEL['other']

    #
    # list_defined_spreads
    #
    all_commands['list_defined_spreads'] = None

    def list_defined_spreads(self, operator):
        return [{
            'code_str': text_type(y),
            'desc': y.description,
            'entity_type': text_type(self.const.EntityType(y.entity_type)),
        } for y in self.const.fetch_constants(self.const.Spread)]

    #
    # get_entity_spreads
    #
    all_commands['get_entity_spreads'] = None

    def get_entity_spreads(self, operator, entity_id):
        entity = self._get_entity(ident=int(entity_id))
        to_spread = self.const.Spread
        return [{
            'spread': text_type(to_spread(row['spread'])),
            'spread_desc': to_spread(row['spread']).description,
        } for row in entity.get_spread()]

    #
    # get_default_email
    #
    all_commands['get_default_email'] = None

    def get_default_email(self, operator, entity_id):
        account = self._get_account(entity_id)
        try:
            return account.get_primary_mailaddress()
        except Errors.NotFoundError:
            return "No e-mail addresse available for %s" % account.account_name

    #
    # get_create_date
    #
    all_commands['get_create_date'] = None

    def get_create_date(self, operator, entity_id):
        account = self._get_account(entity_id)
        return account.created_at

    #
    # user_get_pwd
    #
    all_commands['user_get_pwd'] = None

    def user_get_pwd(self, operator, id_):
        if not self.ba.is_superuser(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to superusers")
        try:
            account = self._get_account(id_, 'id')
        except Errors.NotFoundError:
            raise CerebrumError('Cannot find user with id:{}'.format(id_))
        try:
            pwd = account.get_account_authentication(
                self.const.auth_type_plaintext)
        except Errors.NotFoundError:
            raise CerebrumError('Cannot get user password, password is hashed')
        return {'password': pwd, 'uname': account.account_name}

    #
    # list_active
    #
    all_commands['list_active'] = None

    def list_active(self, operator):
        active = list()
        # IVR 2007-03-11 fetch the source system, which determines people that
        # are considered 'active'.
        source = int(getattr(self.const, cereconf.INDIGO_ACTIVE_SOURCE_SYSTEM))
        for row in self.person.list_affiliations(source_system=source):
            active.append(row['person_id'])
        return active

    #
    # user info [username]
    #
    all_commands['user_info'] = cmd_param.Command(
        ("user", "info"),
        cmd_param.AccountName(),
        fs=cmd_param.FormatSuggestion(
            "Entity id:      %d\n"
            "Owner id:       %d\n"
            "Owner type:     %d\n", ("entity_id", "owner_id", "owner_type")),
        perm_filter='can_view_user')

    def user_info(self, operator, entity_id):
        """ Account info. """
        account = self._get_account(entity_id)
        self.ba.can_view_user(operator.get_entity_id(), account=account)
        return {
            'entity_id': account.entity_id,
            'owner_id': account.owner_id,
            'owner_type': account.owner_type
        }

    #
    # person_info <id>
    #
    all_commands['person_info'] = cmd_param.Command(
        ("person", "info"),
        cmd_param.PersonId(help_ref="id:target:person"),
        fs=cmd_param.FormatSuggestion([
            ("Name:          %s\n"
             "Entity-id:     %i\n"
             "Export-id:     %s\n"
             "Birth:         %s", ("name", "entity_id", "export_id", "birth")),
            ("Affiliation:   %s@%s (%i) [from %s]",
             ("affiliation", "aff_sted_desc", "ou_id", "source_system")),
            ("Fnr:           %s [from %s]", ("fnr", "fnr_src")),
        ]))

    def person_info(self, operator, person_id):
        """ Person info for Cweb. """
        try:
            person = self._get_person(*self._map_person_id(person_id))
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        data = [{
            'name':
            person.get_name(self.const.system_cached,
                            getattr(self.const, cereconf.DEFAULT_GECOS_NAME)),
            'export_id':
            person.export_id,
            'birth':
            person.birth_date,
            'entity_id':
            person.entity_id
        }]

        for row in person.get_affiliations():
            ou = self._get_ou(ou_id=row['ou_id'])
            data.append({
                'aff_sted_desc':
                ou.get_name_with_language(name_variant=self.const.ou_name,
                                          name_language=self.const.language_nb,
                                          default=""),
                'aff_type':
                text_type(self.const.PersonAffiliation(row['affiliation'])),
                'aff_status':
                text_type(self.const.PersonAffStatus(row['status'])),
                'ou_id':
                row['ou_id'],
                'affiliation':
                text_type(self.const.PersonAffStatus(row['status'])),
                'source_system':
                text_type(self.const.AuthoritativeSystem(
                    row['source_system'])),
            })

        account = self.Account_class(self.db)
        account_ids = [
            int(r['account_id'])
            for r in account.list_accounts_by_owner_id(person.entity_id)
        ]
        if (self.ba.is_schoolit(operator.get_entity_id(), True)
                or operator.get_entity_id() in account_ids):
            for row in person.get_external_id(
                    id_type=self.const.externalid_fodselsnr):
                data.append({
                    'fnr':
                    row['external_id'],
                    'fnr_src':
                    text_type(
                        self.const.AuthoritativeSystem(row['source_system'])),
                })
        return data

    #
    # person_find
    #
    all_commands['person_find'] = None

    def person_find(self, operator, search_type, value, filter=None):
        """Indigo-specific wrapper and filter around UiO's open person_find."""

        if not self.ba.is_schoolit(operator.get_entity_id(), True):
            raise PermissionDenied("Limited to school IT and superusers")

        results = self.__uio_impl.person_find(operator, search_type, value,
                                              filter)
        return self._filter_resultset_by_operator(operator, results, "id")

    #
    # person_accounts
    #
    all_commands['person_accounts'] = None

    def person_accounts(self, operator, id):
        """person_accounts with restrictions for Indigo.

        This is a copy of UiO's method, except for result
        filtering/permission check.
        """

        person = self.util.get_target(id, restrict_to=['Person', 'Group'])
        if not (self.ba.is_schoolit(operator.get_entity_id(), True)
                or operator.get_owner_id() == person.entity_id):
            raise PermissionDenied("Limited to school IT and superusers")

        if not self._operator_sees_person(operator, person.entity_id):
            return []

        account = self.Account_class(self.db)
        ret = []
        for r in account.list_accounts_by_owner_id(
                person.entity_id,
                owner_type=person.entity_type,
                filter_expired=False):
            account = self._get_account(r['account_id'], idtype='id')

            ret.append({
                'account_id': r['account_id'],
                'name': account.account_name,
                'expire': account.expire_date
            })
        ret.sort(key=lambda d: d['name'])
        return ret

    #
    # user_create
    #
    all_commands['user_create'] = None

    def user_create(self, operator, uname, owner_id):
        if not self.ba.is_superuser(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to superusers")
        account = self.Account_class(self.db)
        entity = self._get_entity(ident=int(owner_id))
        if entity.entity_type == int(self.const.entity_person):
            np_type = None
        else:
            # TODO: What value?  Or drop-down?
            np_type = self.const.account_program

        account.populate(uname, entity.entity_type, owner_id, np_type,
                         operator.get_entity_id(), None)
        passwd = account.make_passwd(uname)
        account.set_password(passwd)
        account.write_db()
        operator.store_state("new_account_passwd", {
            'account_id': int(account.entity_id),
            'password': passwd
        })
        return "Ok, user created"

    #
    # user_suggest_name
    #
    all_commands['user_suggest_uname'] = None

    def user_suggest_uname(self, operator, owner_id):
        person = self._get_person("entity_id", owner_id)
        account = self.Account_class(self.db)
        return account.suggest_unames(person)

    #
    # user_find
    #
    all_commands['user_find'] = None

    def user_find(self, operator, search_type, search_value):
        "Locate users whose unames loosely matches 'search_value'."

        if not self.ba.is_schoolit(operator.get_entity_id(), True):
            raise PermissionDenied("Limited to superusers and school IT"
                                   " admins")

        if search_type != 'uname':
            raise CerebrumError("Unknown search type (%s)" % search_type)

        if len(search_value.strip(" \t%_*?")) < 3:
            raise CerebrumError("You must specify at least three non-wildcard"
                                " letters")

        # if there are no wildcards in the pattern, add them
        if not [wildcard for wildcard in "_%?*" if wildcard in search_value]:
            search_value = '*' + search_value.replace(' ', '*') + '*'

        account = Factory.get("Account")(self.db)
        matches = list(
            account.search(name=search_value,
                           owner_type=int(self.const.entity_person)))
        # prepare the return value
        ret = list()
        seen = dict()
        if len(matches) > 250:
            raise CerebrumError("More than 250 (%d) matches, please narrow "
                                "search criteria" % len(matches))

        for row in matches:
            account_id = row['account_id']
            if account_id in seen:
                continue

            seen[account_id] = True
            account.clear()
            account.find(account_id)
            person = self._get_person("entity_id", int(account.owner_id))
            owner_name = person.get_name(
                self.const.system_cached,
                getattr(self.const, cereconf.DEFAULT_GECOS_NAME))
            ret.append({
                'account_id': account_id,
                'name': row['name'],
                'owner_id': account.owner_id,
                'owner_name': owner_name,
                'birth': person.birth_date
            })

        # school lita can see their own schools only!
        ret = self._filter_resultset_by_operator(operator, ret, "owner_id")

        ret.sort(key=lambda d: d['name'])
        return ret

    def _operator_sees_person(self, operator, person_id):
        """Decide if operator can obtain information about person_id.

        Superusers can see information about everyone. People can see their
        own information as well.

        Additionally, school IT may see everyone who is affiliated with an OU
        that they have permissions for.
        """

        # superusers and own information
        if (self.ba.is_superuser(operator.get_entity_id(), True)
                or operator.get_owner_id() == person_id):
            return True

        # non-LITAs cannot see anyone else
        if not self.ba.is_schoolit(operator.get_entity_id(), True):
            return False

        # ... but LITAs can
        operators_ou = set(self._operators_ou(operator))
        targets_ou = set([
            x['ou_id']
            for x in self.person.list_affiliations(person_id=int(person_id))
        ])
        return bool(operators_ou.intersection(targets_ou))

    def _operator_sees_ou(self, operator, ou_id):
        """Decide if operator can obtain information about ou_id.

        Superusers can see information about anything. School IT can only see
        info about the schools where they have school IT permissions.
        """

        if self.ba.is_superuser(operator.get_entity_id(), True):
            return True

        if not self.ba.is_schoolit(operator.get_entity_id(), True):
            return False

        operators_ou = set(self._operators_ou(operator))
        return int(ou_id) in operators_ou

    def _filter_resultset_by_operator(self, operator, results, person_key):
        """Remove elements from results to which operator has no access.

        In general, a school lita should not 'see' any results outside of his
        school. This means that the list of users and people returned to
        him/her has to be filtered.

        operator    operator (person_id)
        results     a sequency of dictionary-like objects where each object
                    represents a database row. These are to be filtered.
        person_key  name of the key in each element of results that
                    designates the owner.

        Caveats:
        * This method is quite costly. It gets more so, the larger the schools
          are.
        * This method will not help with group filtering.
        """

        # never filter superusers' results
        if self.ba.is_superuser(operator.get_entity_id(), True):
            return results

        # The operation is performed in three steps:
        # 1) fetch all OUs where that the operator can "see".
        # 2) fetch all people affiliated with OUs in #1
        # 3) intersect results with #2

        # Find operator's OUs
        operators_ou = self._operators_ou(operator)

        # Find all people affiliated with operator's OUs
        operators_people = set([
            x['person_id']
            for x in self.person.list_affiliations(ou_id=operators_ou)
        ])
        # Filter the results...
        filtered_set = list()
        for element in results:
            if element[person_key] in operators_people:
                filtered_set.append(element)

        return type(results)(filtered_set)

    def _operators_ou(self, operator):
        """Return a sequence of OUs that operator can 'see'.

        Superusers see everything.
        School IT see only the OUs where they have privileges.
        Everyone else sees nothing.
        """
        def grab_all_ous():
            return [
                int(x['ou_id'])
                for x in self.ou.search(filter_quarantined=False)
            ]

        if self.ba.is_superuser(operator.get_entity_id(), True):
            return grab_all_ous()

        if not self.ba.is_schoolit(operator.get_entity_id(), True):
            return []

        group = self.Group_class(self.db)
        # fetch all groups where operator is a member
        op_groups = [
            x['group_id']
            for x in group.search(member_id=operator.get_entity_id(),
                                  indirect_members=False)
        ]
        # fetch all permissions that these groups have
        op_targets = [
            x['op_target_id']
            for x in BofhdAuthRole(self.db).list(entity_ids=op_groups)
        ]

        # Now, finally, the permissions:
        result = list()
        for permission in BofhdAuthOpTarget(self.db).list(
                target_id=op_targets,
                target_type=self.const.auth_target_type_ou,
        ):
            if permission["entity_id"] is not None:
                result.append(int(permission["entity_id"]))
            else:
                # AHA! We have a general OU permission. Grab them all!
                return grab_all_ous()

        return result

    #
    # misc history <num-days>
    #
    all_commands['misc_history'] = cmd_param.Command(('misc', 'history'),
                                                     cmd_param.SimpleString())

    def misc_history(self, operator, days):
        if not self.ba.is_superuser(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to superusers")

        types = (self.clconst.account_create, self.clconst.account_password,
                 self.clconst.ou_create, self.clconst.person_create)
        sdate = mx.DateTime.now() - mx.DateTime.oneDay * int(days)
        # Collect in a dict to remove duplicates etc.
        tmp = {}
        for r in self.db.get_log_events(sdate=sdate, types=types):
            tmp.setdefault(int(r['subject_entity']),
                           {})[int(r['change_type_id'])] = r

        ret = []
        for entity_id, changes in tmp.items():
            if (int(self.clconst.account_password) in changes
                    and int(self.clconst.account_create) not in changes):
                # TBD: naa er det OK aa vise passordet?
                del (changes[int(self.clconst.account_password)])

            for k, v in changes.items():
                change_type = self.clconst.ChangeType(int(k))
                params = ''
                if k == self.const.account_password:
                    if v['change_params']:
                        params = json.loads(v['change_params'])
                        params = params.get('password', '')
                tmp = {
                    'tstamp': v['tstamp'],
                    # 'change_type': str(cl),
                    'change_type': text_type(change_type),
                    'misc': params,
                }
                entity = self._get_entity(ident=int(v['subject_entity']))
                if entity.entity_type == int(self.const.entity_person):
                    person = self._get_person("entity_id", entity.entity_id)
                    name = person.get_name(self.const.system_cached,
                                           self.const.name_full)
                    tmp['person_id'] = int(person.entity_id)
                elif entity.entity_type == int(self.const.entity_account):
                    account = self.Account_class(self.db)
                    account.find(entity.entity_id)
                    name = account.account_name
                    tmp['person_id'] = int(account.owner_id)
                else:
                    self.ou.clear()
                    self.ou.find(entity.entity_id)
                    name = self.ou.get_name_with_language(
                        name_variant=self.const.ou_name,
                        name_language=self.const.language_nb,
                        default="")
                tmp['name'] = name
                ret.append(tmp)
        return ret

    #
    # find_school <name>
    #
    all_commands['find_school'] = None

    def find_school(self, operator, name):

        if not self.ba.is_schoolit(operator.get_entity_id(), True):
            raise PermissionDenied("Currently limited to superusers and"
                                   " school IT")

        # name could be an acronym or a "regular" name
        result = set()
        for name_variant in (self.const.ou_name, self.const.ou_name_acronym):
            result.update(r["entity_id"]
                          for r in self.ou.search_name_with_language(
                              entity_type=self.const.entity_ou,
                              name_variant=name_variant,
                              name=name,
                              name_language=self.const.language_nb,
                              exact_match=False))

        if len(result) == 0:
            raise CerebrumError("Could not find school matching %s" % name)
        elif len(result) > 1:
            raise CerebrumError("Found several schools with matching names")

        # Now there is just one left. But can the operator see it?
        ou_id = result.pop()
        # filter the results for school IT
        if not self._operator_sees_ou(operator, ou_id):
            raise CerebrumError("School information is unavailable for this"
                                " user")
        else:
            return ou_id

    #
    # get_password_information <entity_id>
    #
    all_commands["get_password_information"] = None

    def get_password_information(self, operator, entity_id):
        """Retrieve information about password changes for entity_id.

        This function helps implement a command in Giske's cweb.
        """

        self.logger.debug("Processing for id=%s", entity_id)
        entity_id = int(entity_id)
        result = {}
        for row in operator.get_state():
            if row["state_data"] is None:
                continue
            if entity_id != row["state_data"]["account_id"]:
                continue
            if row["state_type"] not in ("new_account_passwd", "user_passwd"):
                continue

            result = {
                "account_id":
                entity_id,
                "uname":
                self._get_entity_name(entity_id, self.const.entity_account),
                "password":
                row["state_data"]["password"],
            }
            account = self._get_entity(ident=entity_id)
            owner = self._get_entity(ident=account.owner_id)
            result["name"] = self._get_entity_name(owner.entity_id,
                                                   owner.entity_type)
            if owner.entity_type == self.const.entity_person:
                result["birth_date"] = owner.birth_date
                # Main affiliation points to school.
                affs = account.list_accounts_by_type(
                    primary_only=True,
                    person_id=owner.entity_id,
                    account_id=account.entity_id)
                if affs:
                    ou = self._get_entity(ident=affs[0]["ou_id"])
                    ou_name = ou.get_name_with_language(
                        name_variant=self.const.ou_name,
                        name_language=self.const.language_nb,
                        default="")
                    result["institution_name"] = ou_name
                else:
                    result["institution_name"] = "n/a"
            else:
                result["birth_date"] = "n/a"
                result["institution_name"] = "n/a"
        return result

    @classmethod
    def get_format_suggestion(cls, cmd):
        return cls.all_commands[cmd].get_fs()

    def _format_ou_name(self, ou):
        binds = {"name_language": self.const.language_nb, "default": ""}
        return (ou.get_name_with_language(
            name_variant=self.const.ou_name_short, **binds)
                or ou.get_name_with_language(name_variant=self.const.ou_name,
                                             **binds))

    def _email_info_detail(self, acc):
        """ Get quotas from Cerebrum, and usage from Cyrus. """
        # NOTE: Very similar to hiof and uio

        info = []
        eq = Email.EmailQuota(self.db)

        # Get quota and usage
        try:
            eq.find_by_target_entity(acc.entity_id)
            et = Email.EmailTarget(self.db)
            et.find_by_target_entity(acc.entity_id)
            es = Email.EmailServer(self.db)
            es.find(et.email_server_id)

            if es.email_server_type == self.const.email_server_type_cyrus:
                used = 'N/A'
                limit = None
                pw = self.db._read_password(cereconf.CYRUS_HOST,
                                            cereconf.CYRUS_ADMIN)
                try:
                    cyrus = imaplib.IMAP4(es.name)
                    # IVR 2007-08-29 If the server is too busy, we do not want
                    # to lock the entire bofhd.
                    # 5 seconds should be enough
                    cyrus.socket().settimeout(5)
                    cyrus.login(cereconf.CYRUS_ADMIN, pw)
                    res, quotas = cyrus.getquota("user." + acc.account_name)
                    cyrus.socket().settimeout(None)
                    if res == "OK":
                        for line in quotas:
                            try:
                                folder, qtype, qused, qlimit = line.split()
                                if qtype == "(STORAGE":
                                    used = str(int(qused) / 1024)
                                    limit = int(qlimit.rstrip(")")) / 1024
                            except ValueError:
                                # line.split fails e.g. because quota isn't set
                                # on server
                                folder, junk = line.split()
                                self.logger.warning(
                                    "No IMAP quota set for '%s'" %
                                    acc.account_name)
                                used = "N/A"
                                limit = None
                except (bofhd_uio_cmds.TimeoutException, socket.error):
                    used = 'DOWN'
                except bofhd_uio_cmds.ConnectException as e:
                    used = text_type(e)
                except imaplib.IMAP4.error:
                    used = 'DOWN'
                info.append({
                    'quota_hard': eq.email_quota_hard,
                    'quota_soft': eq.email_quota_soft,
                    'quota_used': used
                })
                if limit is not None and limit != eq.email_quota_hard:
                    info.append({'quota_server': limit})
            else:
                # Just get quotas
                info.append({
                    'dis_quota_hard': eq.email_quota_hard,
                    'dis_quota_soft': eq.email_quota_soft
                })
        except Errors.NotFoundError:
            pass
        return info

    # Commands for Exchange migration:

    #
    # user migrate_exchange [username] [mdb]
    #
    all_commands['user_migrate_exchange'] = cmd_param.Command(
        ("user", "migrate_exchange"),
        cmd_param.AccountName(help_ref="account_name", repeat=False),
        cmd_param.SimpleString(help_ref='string_mdb'),
        perm_filter='is_superuser')

    def user_migrate_exchange(self, operator, uname, mdb):
        """Tagging a user as under migration, and setting the new MDB.

        The new MDB value should not be used until the user is tagged as
        successfully migrated.

        """
        if not self.ba.is_superuser(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to superusers")
        account = self._get_account(uname)
        # TODO: check the new MDB value?

        # Set new mdb value
        account.populate_trait(self.const.trait_homedb_info, strval=mdb)
        # Mark that account is being migrated
        account.populate_trait(self.const.trait_exchange_under_migration)
        account.write_db()
        return "OK, mdb stored for user %s" % uname

    #
    # user migrate_exchange_finished
    #
    all_commands['user_migrate_exchange_finished'] = cmd_param.Command(
        ("user", "migrate_exchange_finished"),
        cmd_param.AccountName(help_ref="account_name", repeat=True),
        perm_filter='is_superuser')

    def user_migrate_exchange_finished(self, operator, uname):
        """Tagging a user as migrated to a newer Exchange version."""
        if not self.ba.is_superuser(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to superusers")
        account = self._get_account(uname)
        if not account.get_trait(self.const.trait_exchange_under_migration):
            raise CerebrumError("Account %s not under migration" % uname)
        # Mark that account is successfully migrated to new exchange server
        account.populate_trait(self.const.trait_exchange_migrated)
        account.write_db()
        # Remove trait for being under migration
        account.delete_trait(self.const.trait_exchange_under_migration)
        account.write_db()
        return "OK, deleted trait for user %s" % uname
예제 #4
0
class BofhdExtension(BofhdCommonMethods):
    """ Extends bofhd with a 'ephorte' command group. """

    all_commands = {}
    parent_commands = False
    authz = UiOEphorteAuth

    @property
    def ephorte_role(self):
        try:
            return self.__ephorte_role_util
        except AttributeError:
            self.__ephorte_role_util = EphorteRole(self.db)
            return self.__ephorte_role_util

    @property
    def ephorte_perm(self):
        try:
            return self.__ephorte_perm_util
        except AttributeError:
            self.__ephorte_perm_util = EphortePermission(self.db)
            return self.__ephorte_perm_util

    @classmethod
    def get_help_strings(cls):
        const = Factory.get('Constants')()
        group_help = {
            'ephorte': "Commands for administrating ePhorte data"
        }
        command_help = {
            'ephorte': {
                'ephorte_add_role':
                    'Add an ePhorte role for a person',
                'ephorte_history':
                    'Show the ePhorte related history for a person',
                'ephorte_remove_role':
                    'Remove an ePhorte role from a person',
                'ephorte_list_roles':
                    'List a persons ePhorte roles',
                'ephorte_set_standard_role':
                    'Set given role as standard role',
                'ephorte_add_perm':
                    'Add an ePhorte permission for a person',
                'ephorte_remove_perm':
                    'Remove an ePhorte permission from a person',
                'ephorte_list_perm':
                    'List a persons ePhorte permissions'
            }
        }
        arg_help = {
            'journalenhet': [
                'journalenhet', 'Enter journalenhet',
                'Legal values are: \n%s' % "\n".join([
                    "  %-8s : %s" % (text_type(c), c.description)
                    for c in const.fetch_constants(const.EphorteJournalenhet)
                ])
            ],
            'arkivdel': [
                'arkivdel', 'Enter arkivdel',
                'Legal values are: \n%s' % "\n".join([
                    "  %-8s : %s" % (text_type(c), c.description)
                    for c in const.fetch_constants(const.EphorteArkivdel)
                ])
            ],
            'rolle': [
                'rolle', 'Enter rolle',
                'Legal values are: \n%s' % "\n".join([
                    "  %-8s : %s" % (text_type(c), c.description)
                    for c in const.fetch_constants(const.EphorteRole)
                ])
            ],
            'tilgang': [
                'tilgang', 'Enter perm ("tilgang")',
                'Legal values are: \n%s' % "\n".join([
                    "  %-8s : %s" % (text_type(c), c.description)
                    for c in const.fetch_constants(const.EphortePermission)
                ])
            ],
        }

        # liste lovlige arkivdel/journalenhet
        return (group_help, command_help, arg_help)

    def _get_role_type(self, code_str):
        try:
            c = self.const.EphorteRole(code_str)
            int(c)
            return c
        except Errors.NotFoundError:
            raise CerebrumError("Unknown role")

    def _get_arkivdel(self, code_str):
        try:
            c = self.const.EphorteArkivdel(code_str)
            int(c)
            return c
        except Errors.NotFoundError:
            raise CerebrumError("Unknown arkivdel")

    def _get_journalenhet(self, code_str):
        try:
            c = self.const.EphorteJournalenhet(code_str)
            int(c)
            return c
        except Errors.NotFoundError:
            raise CerebrumError("Unknown journalenhet")

    def _get_tilgang(self, code_str):
        try:
            c = self.const.EphortePermission(code_str)
            int(c)
            return c
        except Errors.NotFoundError:
            raise CerebrumError("Unknown ePhorte auth. permission"
                                " (tilgangskode)")

    #
    # Add, remove or list ePhorte-roles
    #
    all_commands['ephorte_add_role'] = cmd_param.Command(
        ("ephorte", "add_role"),
        cmd_param.PersonId(),
        Rolle(),
        cmd_param.OU(),
        Arkivdel(),
        Journalenhet(),
        perm_filter='can_add_ephorte_role')

    def ephorte_add_role(self, operator,
                         person_id, role, sko, arkivdel, journalenhet):
        not_ephorte_ou = False
        if not self.ba.can_add_ephorte_role(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        ou = self._get_ou(stedkode=sko)
        if not ou.has_spread(self.const.spread_ephorte_ou):
            not_ephorte_ou = True
        extra_msg = ""
        if not person.has_spread(self.const.spread_ephorte_person):
            person.add_spread(self.const.spread_ephorte_person)
            extra_msg = " (implicitly added ephorte-spread)"

        arkivdel = self._get_arkivdel(arkivdel)
        journalenhet = self._get_journalenhet(journalenhet)
        role = self._get_role_type(role)
        self.ephorte_role.add_role(person.entity_id, role,
                                   ou.entity_id, arkivdel, journalenhet,
                                   auto_role='F')
        if not_ephorte_ou:
            return ("Warning: Added %s role for %s%s to a"
                    " non-archive OU %r") % (text_type(role), person_id,
                                             extra_msg, sko)
        return "OK, added %s role for %s%s" % (text_type(role), person_id,
                                               extra_msg)

    #
    # ephorte remove_role
    #
    all_commands['ephorte_remove_role'] = cmd_param.Command(
        ("ephorte", "remove_role"),
        cmd_param.PersonId(),
        Rolle(),
        cmd_param.OU(),
        Arkivdel(),
        Journalenhet(),
        perm_filter='can_remove_ephorte_role')

    def ephorte_remove_role(self, operator,
                            person_id, role, sko, arkivdel, journalenhet):
        if not self.ba.can_remove_ephorte_role(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        ou = self._get_ou(stedkode=sko)
        arkivdel = self._get_arkivdel(arkivdel)
        journalenhet = self._get_journalenhet(journalenhet)
        role = self._get_role_type(role)
        # Check that the person has the given role.
        if not self.ephorte_role.get_role(
                person.entity_id,
                role,
                ou.entity_id,
                arkivdel,
                journalenhet):
            raise CerebrumError("Person has no such role")
        # Check if role is a standard role
        _list_roles = self.ephorte_role.list_roles
        if (self.ephorte_role.is_standard_role(person.entity_id,
                                               role,
                                               ou.entity_id,
                                               arkivdel,
                                               journalenhet)
                and len(_list_roles(person_id=person.entity_id)) > 1):
            raise CerebrumError("Cannot delete standard role.")
        self.ephorte_role.remove_role(person.entity_id,
                                      role,
                                      ou.entity_id,
                                      arkivdel,
                                      journalenhet)
        return "OK, removed %s role for %s" % (text_type(role), person_id)

    #
    # ephorte list_roles
    #
    all_commands['ephorte_list_roles'] = cmd_param.Command(
        ("ephorte", "list_roles"),
        cmd_param.PersonId(),
        perm_filter='can_list_ephorte_roles',
        fs=cmd_param.FormatSuggestion(
            "%-5s %-30s %-15s %-13s %s",
            ('role', 'adm_enhet', 'arkivdel', 'journalenhet', 'std_role'),
            hdr="%-5s %-30s %-15s %-13s %s" % (
                "Rolle", "Adm enhet", "Arkivdel", "Journalenhet",
                "Standardrolle")
        ))

    def ephorte_list_roles(self, operator, person_id):
        if not self.ba.can_list_ephorte_roles(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        ret = []

        def to_text(getter, value):
            if not value:
                return ''
            return text_type(getter(value))

        for row in self.ephorte_role.list_roles(person_id=person.entity_id):
            ou = self._get_ou(ou_id=row['adm_enhet'])
            ret.append({
                'role': to_text(self._get_role_type, row['role_type']),
                'adm_enhet': self._format_ou_name(ou),
                'arkivdel': to_text(self._get_arkivdel, row['arkivdel']),
                'journalenhet': to_text(self._get_journalenhet,
                                        row['journalenhet']),
                'std_role': row['standard_role'] or '',
            })
        return ret

    #
    # ephorte set_standard_role
    #
    all_commands['ephorte_set_standard_role'] = cmd_param.Command(
        ("ephorte", "set_standard_role"),
        cmd_param.PersonId(),
        Rolle(),
        cmd_param.OU(),
        Arkivdel(),
        Journalenhet(),
        perm_filter='can_add_ephorte_role')

    def ephorte_set_standard_role(self, operator, person_id, role, sko,
                                  arkivdel, journalenhet):
        """
        Set given role as standard role.

        There can be only one standard role, thus if another role is
        marked as standard role, it will no longer be the persons
        standard role.
        """
        # Check operators permissions
        if not self.ba.can_add_ephorte_role(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        ou = self._get_ou(stedkode=sko)
        # Check that the person has the given role.
        tmp = self.ephorte_role.get_role(person.entity_id,
                                         self._get_role_type(role),
                                         ou.entity_id,
                                         self._get_arkivdel(arkivdel),
                                         self._get_journalenhet(journalenhet))
        # Some sanity checks
        if not tmp:
            raise CerebrumError("Person has no such role")
        elif len(tmp) > 1:
            raise Errors.TooManyRowsError("Unexpectedly found more than one"
                                          " role")
        new_std_role = tmp[0]
        if new_std_role['standard_role'] == 'T':
            return "Role is already standard role"
        # There can be only one standard role
        for row in self.ephorte_role.list_roles(person_id=person.entity_id):
            if row['standard_role'] == 'T':
                self.logger.debug("Unset role %s at %s as standard_role" % (
                    row['role_type'], row['adm_enhet']))
                self.ephorte_role.set_standard_role_val(person.entity_id,
                                                        row['role_type'],
                                                        row['adm_enhet'],
                                                        row['arkivdel'],
                                                        row['journalenhet'],
                                                        'F')
        # Finally set the new standard role
        self.ephorte_role.set_standard_role_val(
            person.entity_id,
            self._get_role_type(role),
            ou.entity_id,
            self._get_arkivdel(arkivdel),
            self._get_journalenhet(journalenhet),
            'T')
        return "OK, set new standard role"

    def _lookup_perm_tr(self, tilgang):
        key = text_type(tilgang)
        res = (cereconf.EPHORTE_NEW2OLD_PERMISSIONS.get(key, None) or
               cereconf.EPHORTE_OLD2NEW_PERMISSIONS.get(key, None))
        if res:
            return self._get_tilgang(res)
        return None

    #
    # Add, remove or list auth. permissions ("tilgangskoder") for ePhorte
    # TBD: we should consider supporting permissions starting in the future
    #
    all_commands['ephorte_add_perm'] = cmd_param.Command(
        ("ephorte", "add_perm"),
        cmd_param.PersonId(),
        Tilgang(),
        cmd_param.OU(repeat=True),
        perm_filter='can_add_ephorte_perm')

    def ephorte_add_perm(self, operator, person_id, tilgang, sko):
        if not self.ba.can_add_ephorte_perm(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        operator_id = operator.get_entity_id()
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        if not person.has_spread(self.const.spread_ephorte_person):
            raise CerebrumError("Person has no ephorte roles")
        ou = self._get_ou(stedkode=sko)
        if not (sko == cereconf.EPHORTE_EGNE_SAKER_SKO or
                ou.has_spread(self.const.spread_ephorte_ou)):
            raise CerebrumError("Cannot assign permission to a non-ephorte OU")

        if self.ephorte_perm.has_permission(person.entity_id,
                                            self._get_tilgang(tilgang),
                                            ou.entity_id):
            raise CerebrumError("Person %r already has perm %r"
                                " (remove first)" % (person_id, tilgang))

        tilgang = self._get_tilgang(tilgang)
        # This is a hack needed by the archivists.
        # If one of the new permissions, defined in
        # EPHORTE_NEW2OLD_PERMISSIONS.values() is to be added, the old
        # (expired) one must be added to. And vice versa.
        corresponding_perm = self._lookup_perm_tr(tilgang)
        if corresponding_perm:
            # Add the corresponding permission
            self.ephorte_perm.add_permission(
                person.entity_id,
                corresponding_perm,
                ou.entity_id,
                operator_id)
            ret_msg_suffix = " Also added 'tilgang' %s" % (
                text_type(corresponding_perm))
        else:
            ret_msg_suffix = ""
        # Add new permission
        self.ephorte_perm.add_permission(person.entity_id,
                                         tilgang,
                                         ou.entity_id,
                                         operator_id)
        return "OK, added 'tilgang' %s for %s.%s" % (text_type(tilgang),
                                                     person_id,
                                                     ret_msg_suffix)

    #
    # ephorte remove_perm
    #
    all_commands['ephorte_remove_perm'] = cmd_param.Command(
        ("ephorte", "remove_perm"),
        cmd_param.PersonId(),
        Tilgang(),
        cmd_param.OU(repeat=True),
        perm_filter='can_remove_ephorte_perm')

    def ephorte_remove_perm(self, operator, person_id, tilgang, sko):
        if not self.ba.can_remove_ephorte_perm(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        ou = self._get_ou(stedkode=sko)
        # This is a hack needed by the archivists.
        # If one of the new permissions, defined in
        # EPHORTE_NEW2OLD_PERMISSIONS.values() is to be added, the old
        # (expired) one must be added to. And vice versa.
        tilgang = self._get_tilgang(tilgang)

        corresponding_perm = self._lookup_perm_tr(tilgang)
        if corresponding_perm:
            # Remove old permission
            self.ephorte_perm.remove_permission(
                person.entity_id,
                corresponding_perm,
                ou.entity_id)
            ret_msg_suffix = " Also removed 'tilgang' %s" % (
                text_type(corresponding_perm))
        else:
            ret_msg_suffix = ""
        # Remove new permission
        self.ephorte_perm.remove_permission(person.entity_id,
                                            tilgang,
                                            ou.entity_id)
        return "OK, removed 'tilgang' %s for %s.%s" % (text_type(tilgang),
                                                       person_id,
                                                       ret_msg_suffix)

    #
    # ephorte list_perm
    #
    all_commands['ephorte_list_perm'] = cmd_param.Command(
        ("ephorte", "list_perm"),
        cmd_param.PersonId(),
        perm_filter='can_list_ephorte_perm',
        fs=cmd_param.FormatSuggestion(
            "%-10s %-34s %-18s %-10s",
            ('tilgang', 'adm_enhet', 'requestee', 'end_date'),
            hdr="%-10s %-34s %-18s %-10s" % (
                "Tilgang", "Adm.enhet", "Tildelt av", "Sluttdato")
        ))

    def ephorte_list_perm(self, operator, person_id):
        if not self.ba.can_list_ephorte_perm(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")
        ret = []
        for row in self.ephorte_perm.list_permission(
                person_id=person.entity_id):
            ou = self._get_ou(ou_id=row['adm_enhet'])
            requestee = self.util.get_target(int(row['requestee_id']))
            if row['end_date']:
                end_date = row['end_date'].date
            else:
                end_date = ''
            ret.append({
                'tilgang': text_type(self._get_tilgang(row['perm_type'])),
                'adm_enhet': self._format_ou_name(ou),
                'requestee': requestee.get_names()[0][0],
                'end_date': end_date,
            })
        return ret

    #
    # ephorte history
    #
    all_commands['ephorte_history'] = cmd_param.Command(
        ("ephorte", "history"),
        cmd_param.PersonId(),
        cmd_param.Integer(optional=True, help_ref="limit_number_of_results"),
        fs=cmd_param.FormatSuggestion(
            "%s [%s]: %s", ("timestamp", "change_by", "message")),
        # perm_filter='can_add_ephorte_perm')
        # Only available for superusers for now.
        perm_filter='is_superuser')

    def ephorte_history(self, operator, person_id, limit=100):
        if not self.ba.can_list_ephorte_perm(operator.get_entity_id()):
            raise PermissionDenied("Currently limited to ephorte admins")
        try:
            person = self.util.get_target(person_id, restrict_to=['Person'])
        except Errors.TooManyRowsError:
            raise CerebrumError("Unexpectedly found more than one person")

        try:
            limit = int(limit)
        except ValueError:
            raise CerebrumError("Limit must be a number")

        # Only certain tyeps of changes are relevant for ephorte history
        types = ["ephorte_role_add", "ephorte_role_upd", "ephorte_role_rem",
                 "ephorte_perm_add", "ephorte_perm_rem", "person_aff_add",
                 "person_aff_del", "person_aff_mod", "person_aff_src_add",
                 "person_aff_src_del", "person_aff_src_mod", "person_create"]
        ret = []

        rows = list(self.db.get_log_events(0,
                                           subject_entity=person.entity_id,
                                           types=[getattr(self.clconst, t)
                                                  for t in types]))
        for r in rows[-limit:]:
            ret.append(self._format_changelog_entry(r))
        return ret
예제 #5
0
    def user_info(self, operator, entity_id):
        """ Account info. """
        account = self._get_account(entity_id)
        return {'entity_id': account.entity_id,
                'owner_id': account.owner_id,
                'owner_type': account.owner_type}

    # TODO: Interessant... FormatSuggestion stemmer overhode ikke overens med
    #       data som returneres fra person_info ...
    #       Denne vil gi NullPointerException i jBofh - fhl
    #
    # person_info <id>
    #
    all_commands['person_info'] = cmd_param.Command(
        ("person", "info"),
        cmd_param.PersonId(help_ref="id:target:person"),
        fs=cmd_param.FormatSuggestion([
            ("Name:          %s\n" +
             "Entity-id:     %i\n" +
             "Birth:         %s\n" +
             "Spreads:       %s\n" +
             "Affiliations:  %s [from %s]", ("name", "entity_id", "birth",
                                             "spreads", "affiliation_1",
                                             "source_system_1")),
            ("               %s [from %s]", ("affiliation", "source_system")),
            ("Names:         %s[from %s]", ("names", "name_src")),
            ("Fnr:           %s [from %s]", ("fnr", "fnr_src")),
            ("Contact:       %s: %s [from %s]", ("contact_type", "contact",
                                                 "contact_src")),
            ("External id:   %s [from %s]", ("extid", "extid_src"))
        ]))