Ejemplo n.º 1
0
    def _setup_project_dns(self, creator_id, vlan=None):
        """Setup a new project's DNS info, like subnet and VLAN.

        :param int creator_id:
            The entity_id for the user who executes this.

        :param int vlan:
            If given, overrides what VLAN number to set for the project's
            subnets, as long as it is within one of the ranges defined
            in `cereconf.VLAN_RANGES`.
            If set to None, the first free VLAN will be chosen.
        """
        projectid = self.get_project_id()
        if not vlan:
            try:
                # Check if a VLAN is already assigned
                sub = Subnet.Subnet(self._db)
                sub.find(self.ipv4_subnets.next())
                vlan = sub.vlan_number
            except:
                vlan = self.get_next_free_vlan()
        try:
            vlan = int(vlan)
        except ValueError:
            raise Errors.CerebrumError('VLAN not valid: %s' % (vlan,))
        # Checking if the VLAN is in one of the ranges
        for min, max in getattr(cereconf, 'VLAN_RANGES', ()):
            if min <= vlan <= max:
                break
        else:
            raise Errors.CerebrumError('VLAN out of range: %s' % vlan)

        # TODO: Find a better way for mapping between project ID and VLAN:
        intpid = self.project_int
        subnetstart, subnet6start = self._generate_subnets_for_project_id(
            project_id=intpid)

        for cls, start, my_subnets in (
                (Subnet.Subnet, subnetstart, self.ipv4_subnets),
                (IPv6Subnet.IPv6Subnet, subnet6start, self.ipv6_subnets)):
            sub = cls(self._db)
            try:
                sub.find(start)
            except dnsErrors.SubnetError:
                sub.populate(start, "Subnet for project %s" % projectid, vlan)
                sub.write_db()
            else:
                if sub.entity_id not in my_subnets:
                    raise Exception("Subnet %s exists, but does not belong"
                                    " to %s" % (start, projectid))
            if not self.is_affiliated_entity(sub):
                self.affiliate_entity(sub)
Ejemplo n.º 2
0
    def generate_dictionary_passphrase(self):
        """
        Generates a random dictionary based passphrase

        :return:
            return a random passphrase
        :rtype: unicode
        """
        if not self.config.passphrase_dictionary:
            raise Errors.CerebrumError('Missing passphrase-dictionary')
        if len(self.dict_words) < self.config.amount_words:
            raise Errors.CerebrumError('Passphrase-dictionary not long enough')
        return u' '.join(
            self.lrandom.sample(self.dict_words, self.config.amount_words))
Ejemplo n.º 3
0
    def __init__(self, config=None, *args, **kw):
        """
        Constructs a PasswordGenerator.

        :param Cerebrum.config.configuration.Configuration config:
            The Configuration object for the password_generator module.
        """
        try:
            if config is None:
                self.config = load_config()
            else:
                self.config = load_config(filepath=config)
            # Create a local random object for increased randomness
            # "Use os.urandom() or SystemRandom if you require a
            # cryptographically secure pseudo-random number generator."
            # docs.python.org/2.7/library/random.html#random.SystemRandom
            self.lrandom = random.SystemRandom()
            self.dict_words = []
            if self.config.passphrase_dictionary:
                with open(self.config.passphrase_dictionary) as fp:
                    for line in fp:
                        try:
                            # assume UTF-8 encoded text-file
                            self.dict_words.append(
                                line.strip().decode('utf-8'))
                        except:
                            continue
        except Exception as e:
            raise Errors.CerebrumError('Unable to create a PasswordGenerator '
                                       'instance: {error}'.format(error=e))
def generate_mail_notification(quar_info, event_info, debug=False):
    if event_info['change_type'] == 'quarantine:mod':
        event_type = 'altered'
    elif event_info['change_type'] == 'quarantine:add':
        event_type = 'added'
    elif event_info['change_type'] == 'quarantine:del':
        event_type = 'deleted'
    else:
        raise Errors.CerebrumError('Unknown quarantine action: %s'
                                   % event_info['change_type'])
    subject = 'Quarantine %s %s on account %s' % \
              (quar_info['name'], event_type, event_info['subject'])
    body = ('Quarantine %s %s on %s.\n\n'
            'When: %s\n'
            'By: %s\n'
            'Change-ID: %s') % (quar_info['name'],
                                event_type,
                                event_info['subject'],
                                event_info['time_stamp'],
                                event_info['change_by'],
                                event_info['change_id'])
    return Utils.sendmail(quar_info['mail_to'],
                          quar_info['mail_from'],
                          subject,
                          body,
                          debug=debug)
Ejemplo n.º 5
0
def ldif_outfile(tree, filename=None, default=None, explicit_default=False,
                 max_change=None, module=cereconf):
    """(Open and) return LDIF outfile for <tree>.

    Use <filename> if specified,
    otherwise module.LDAP_<tree>['file'] unless <explicit_default>,
    otherwise return <default> (an open filehandle) if that is not None.
    (explicit_default should be set if <default> was opened from a
    <filename> argument and not from module.LDAP*['file'].)

    When opening a file, use SimilarSizeWriter where close() fails if
    the resulting file has changed more than <max_change>, or
    module.LDAP_<tree>['max_change'], or module.LDAP['max_change'].
    If max_change is unset or >= 100, just open the file normally.
    """
    if not (filename or explicit_default):
        filename = getattr(module, 'LDAP_' + tree).get('file')
        if filename:
            filename = os.path.join(module.LDAP['dump_dir'], filename)
    if filename:
        if max_change is None:
            max_change = ldapconf(tree, 'max_change', default=ldapconf(
                None, 'max_change', default=100, module=module),
                module=module)
        if max_change < 100:
            f = SimilarSizeWriter(filename, 'w')
            f.max_pct_change = max_change
        else:
            f = AtomicFileWriter(filename, 'w')
        return f
    if default:
        return default
    raise _Errors.CerebrumError(
        'Outfile not specified and LDAP_{0}["file"] not set'.format(tree))
Ejemplo n.º 6
0
    def create_project(self, project_name):
        """Create a new project in TSD.

        Note that this method calls `write_db`.

        :param str project_name:
            A unique, short project name to use to identify the project.
            This is not the *project ID*, which is created automatically.

        :return str:
            The generated project ID for the new project.
        """
        # Check if given project name is already in use:
        if tuple(self.search_tsd_projects(name=project_name,
                                          exact_match=True)):
            raise Errors.CerebrumError(
                'Project name already taken: %s' % project_name)
        self.populate()
        self.write_db()
        # Set a generated project ID
        self.affect_external_id(self.const.system_cached,
                                self.const.externalid_project_id)
        pid = self.get_next_free_project_id()
        self.populate_external_id(self.const.system_cached,
                                  self.const.externalid_project_id, pid)
        self.write_db()
        # Set the project name
        self.add_name_with_language(name_variant=self.const.ou_name_acronym,
                                    name_language=self.const.language_en,
                                    name=project_name)
        self.write_db()
        return pid
Ejemplo n.º 7
0
    def _autopick_homeMDB(self):
        """Return a valid homeMDB value to be used for the account.

        If the account has previously had a HomeMDB, this is reused, but only
        as long the MDB value is valid today, see
        L{cereconf.EXCHANGE_HOMEMDB_VALID}. Otherwise a random HomeMDB is
        selected. We don't care about the weight of the MDBs, ØFK wants
        everyone to be equally assigned.

        @rtype: string
        @return: One of the HomeMDB values from
            L{cereconf.EXCHANGE_HOMEMDB_VALID}.

        """
        mdb_candidates = cereconf.EXCHANGE_HOMEMDB_VALID
        # Check if account had homeMDB earlier. If so use that, as long as it
        # is in one of today's valid MDBs.
        mdb_choice = self._get_old_homeMDB()
        if mdb_choice and mdb_choice in mdb_candidates:
            return mdb_choice
        # Choose a random HomeMDB, and don't care about weights:
        mdb_choice = random.choice(mdb_candidates.keys())
        if mdb_choice is None:
            raise Errors.CerebrumError("Couldn't assign mdb for %s" %
                                       self.entity_id)
        return mdb_choice
Ejemplo n.º 8
0
    def _get_affiliate_trait(self, etype):
        """Get trait used to affiliate an entity with this project.

        :type etype:
            Constants.EntityType, int, str
        :param entity_type:
            The entity type
        """
        type2trait = {
            self.const.entity_group:
                self.const.trait_project_group,
            self.const.entity_dns_owner:
                self.const.trait_project_host,
            self.const.entity_dns_subnet:
                self.const.trait_project_subnet,
            self.const.entity_dns_ipv6_subnet:
                self.const.trait_project_subnet6,
        }
        entity_type = (
            etype if isinstance(etype, self.const.EntityType)
            else self.const.human2constant(etype, self.const.EntityType))
        try:
            return type2trait[entity_type]
        except KeyError:
            raise Errors.CerebrumError(
                u"Entity type %s (%r) cannot be affilated with project." %
                (entity_type, etype))
Ejemplo n.º 9
0
    def __init__(self, config=None, logger=None, *args, **kw):
        """ Constructs a PasswordGenerator.

        :param Cerebrum.config.configuration.Configuration config:
            The Configuration object for the password_generator module.

        :param logging.Logger logger:
            Logger object to use. If `None`, this object will fetch a new
            logger with `Factory.get_logger('crontab')`. This is the default.
        """
        try:
            if config is None:
                self.config = load_config()
            else:
                self.config = load_config(filepath=config)
            # Create a local random object for increased randomness
            # "Use os.urandom() or SystemRandom if you require a
            # cryptographically secure pseudo-random number generator."
            # docs.python.org/2.7/library/random.html#random.SystemRandom
            self.lrandom = random.SystemRandom()
            self.logger = logger or Utils.Factory.get_logger('console')
            self.dict_words = []
            if self.config.passphrase_dictionary:
                with open(self.config.passphrase_dictionary) as fp:
                    for line in fp:
                        try:
                            # assume UTF-8 encoded text-file
                            self.dict_words.append(
                                line.strip().decode('utf-8'))
                        except:
                            continue
            self.logger.debug('PasswordGenerator initialized')
        except Exception as e:
            raise Errors.CerebrumError('Unable to create a PasswordGenerator '
                                       'instance: {error}'.format(error=e))
Ejemplo n.º 10
0
    def _validate_project_name(self, name):
        """Check if a given project name is valid.

        Project names are used to identify project entities in other systems,
        so we need to enforce some limits to avoid potential problems.

        Requirements
        ------------
        Can not contain spaces
            TODO: Why?

        Can not contain commas.
            This is due to AD's way of identifying objects. Example:
            CN=username,OU=projectname,DC=tsd,DC=uio,DC=no.

        Can not contain the SQL wildcards ? and %.
            This is a convenience limit, to be able to use Cerebrum's existing
            API without modifications.

        Can not contain control characters.

        Can not contain characters outside of ASCII.
            This is to avoid unexpected encoding issues.

        TBD: A maximum length in the name?
            AD probably has a limit. As a prefix, it should be a bit less than
            AD's limit.

        In practice, we only accept regular alphanumeric characters in ASCII,
        in addition to some punctuation characters, like colon, dash and
        question marks. This would need to be extended in the future.

        :param str name:
            The name to check.

        :raise Errors.CerebrumError:
            If the given project name was not accepted
        """
        m = re.search('[^A-Za-z0-9_\-:;\*"\'\#\&\=!\?]', name)
        if m:
            raise Errors.CerebrumError(
                'Invalid characters in projectname: %s' % m.group())
        if len(name) < 3:
            raise Errors.CerebrumError('Project name too short')
        if len(name) > 8:  # TBD: or 6?
            raise Errors.CerebrumError('Project name is too long')
        return True
Ejemplo n.º 11
0
 def __init__(self, db):
     """
     Override __init__ to set up AD spesific constants.
     """
     self.__super.__init__(db)
     # Setup AD spread constants
     if not hasattr(cereconf, 'AD_ACCOUNT_SPREADS'):
         raise Errors.CerebrumError("Missing AD_ACCOUNT_SPREADS in cereconf")
     self.ad_account_spreads = {}
     for spread_str in cereconf.AD_ACCOUNT_SPREADS:
         c = self.const.Spread(spread_str)
         self.ad_account_spreads[int(c)] = c
     # Setup defined ad traits
     if not hasattr(cereconf, 'AD_TRAIT_TYPES'):
         raise Errors.CerebrumError("Missing AD_TRAIT_TYPES in cereconf")
     self.ad_trait_types = []
     for trait_str in cereconf.AD_TRAIT_TYPES:
         self.ad_trait_types.append(self.const.EntityTrait(trait_str))
Ejemplo n.º 12
0
 def populate(self, birth_date, gender, description=None,
              deceased_date=None, parent=None):
     try:
         db_deceased = self.deceased_date
     except AttributeError:
         pass
     else:
         if db_deceased is not None and deceased_date is None:
             raise Errors.CerebrumError('Populate called with deceased_date'
                                        '=None, but deceased_date already '
                                        'set on person')
     self.__super.populate(birth_date, gender, description, deceased_date,
                           parent)
Ejemplo n.º 13
0
    def regenerate_otpkey(self, tokentype=None):
        """Create a new OTP key for the account.

        Note that we do not store the OTP key in Cerebrum. We only pass it on
        to the Gateway, so it's only stored one place. Other requirements could
        change this in the future.

        The OTP type, e.g. hotp or totp, is retrieved from the person's trait.

        @type tokentype: str
        @param tokentype:
            What token type the OTP should become, e.g. 'totp' or 'hotp'. Note
            that it could also be translated by L{cereconf.OTP_MAPPING_TYPES}
            if it matches a value there.

            If this parameter is None, the person's default OTP type will be
            used, or 'totp' by default if no value is set for the person.

        @rtype: string (unicode)
        @return:
            The full URI of otpauth, as defined in cereconf.OTP_URI_FORMAT,
            filled with the proper data. The format should follow
            https://code.google.com/p/google-authenticator/wiki/KeyUriFormat

        """
        # Generate a new key:
        secret = base64.b32encode(
            self._generate_otpkey(getattr(cereconf, 'OTP_KEY_LENGTH',
                                          160))).decode()
        # Get the tokentype
        if not tokentype:
            tokentype = 'totp'
            if self.owner_type == self.const.entity_person:
                pe = Factory.get('Person')(self._db)
                pe.find(self.owner_id)
                typetrait = pe.get_trait(self.const.trait_otp_device)
                if typetrait:
                    tokentype = typetrait['strval']

        # A mapping from e.g. Nettskjema's smartphone_yes -> topt:
        mapping = getattr(cereconf, 'OTP_MAPPING_TYPES', {})
        try:
            tokentype = mapping[tokentype]
        except KeyError:
            raise Errors.CerebrumError('Invalid tokentype: %s' % tokentype)
        return cereconf.OTP_URI_FORMAT % {
            'secret': secret,
            'user': '******' %
            (self.account_name, cereconf.INSTITUTION_DOMAIN_NAME),
            'type': tokentype,
        }
Ejemplo n.º 14
0
    def populate_external_id(self, source_system, id_type, external_id):
        """Sets external id for project.

        This overrides the parent method to add uniqueness for project IDs.

        :see: EntityExternalId.populate_external_id
        """
        # Check that the ID is not in use
        if id_type == self.const.externalid_project_id:
            for row in self.search_external_ids(id_type=id_type,
                                                external_id=external_id,
                                                fetchall=False):
                raise Errors.CerebrumError("Project ID already in use")

        return super(TsdProjectMixin, self).populate_external_id(
            source_system, id_type, external_id)
Ejemplo n.º 15
0
def _decode_const(constname, value):
    global _const, _Constants
    try:
        return int(value)
    except ValueError:
        if not _const:
            from Cerebrum import Constants as _Constants
            _const = Factory.get('Constants')(
                Factory.get('Database')())
        try:
            return int(getattr(_const, value))
        except AttributeError:
            try:
                return int(getattr(_Constants, constname)(value))
            except _Errors.NotFoundError:
                raise _Errors.CerebrumError(
                    'Invalid {0}: {1}'.format(constname, value))
Ejemplo n.º 16
0
def ldapconf(tree, attr, default=_dummy, module=cereconf):
    """Return module.LDAP_<tree>[<attr>] with default.

    Fetch module.LDAP_<tree>[<attr>], or LDAP[<attr>] if <tree> is None.
    If <default> is given, it is used if the setting is absent or None.
    """
    var = tree is None and 'LDAP' or 'LDAP_' + tree
    val = getattr(module, var).get(attr, default)
    if val is _dummy:
        raise _Errors.CerebrumError(
            '{0}.{1}["{2}"] is not set'.format(
                module.__name__,
                var,
                attr))
    if val is None and default is not _dummy:
        val = default
    return val
Ejemplo n.º 17
0
    def set_account_type(self, ou_id, affiliation, priority=None):
        """Subclass setting of the account_type.

        Since OUs are treated as separate projects in TSD, we need to add some
        protection to them. An account should only be allowed to be part of a
        single OU, no others, to avoid mixing different projects' data.
        """
        if affiliation == self.const.affiliation_project:
            for row in self.list_accounts_by_type(
                    account_id=self.entity_id,
                    affiliation=self.const.affiliation_project,
                    # We want the deleted ones too, as the account
                    # could have previously been part of a project:
                    filter_expired=False):
                if row['ou_id'] != ou_id:
                    raise Errors.CerebrumError('Account already part of other '
                                               'project OUs')
        return self.__super.set_account_type(ou_id, affiliation, priority)
Ejemplo n.º 18
0
def process_kursdata(role_mapping):
    logger.info("Starting retrieval of groups and participants from FS")
    #importer.get_emner()
    importer.get_undervisningsenheter()
    importer.get_undervisningsaktiviteter()

    for k in importer.UndervEnhet.keys():
        populate_enhet_groups(k, role_mapping)

    # Update level 2 groups, i.e. the group that contains all groups
    # pertaining to a particular "enhet"
    logger.info("Updating 'enhet'-groups:")
    for kurs_id in AffiliatedGroups.keys():
        if kurs_id == fs_supergroup:
            continue
        rest = kurs_id.split(":")
        enhet_type = rest.pop(0).lower()
        if enhet_type == 'kurs':
            instnr, emnekode, versjon, termk, aar = rest
            sync_group(subject_supergroup, mk_gname(kurs_id),
                       importer.enhet_names[kurs_id], constants.entity_group,
                       AffiliatedGroups[kurs_id])
        else:
            logger.warn("Unknown type <%s> for 'enhet' <%s>" % (type, k))

        # Done processing this group; remove it from later iterations
        del AffiliatedGroups[kurs_id]

    logger.info(" ... done")

    # Now the only remaining group is the root supergroup
    logger.info("Updating course supergroup")
    try:
        sync_group(
            None, subject_supergroup,
            "Root of the course-tree containing all course-based groups",
            constants.entity_group, AffiliatedGroups[subject_supergroup])
    except KeyError, ke:
        # This really shouldn't happen during normal operations...
        logger.error(
            "Unable to find course supergroup among groups to be synced. "
            "This can only happen if no courses are to be sync'ed, "
            "which sounds very very wrong.")
        raise Errors.CerebrumError("No courses are set to being sync'ed")
Ejemplo n.º 19
0
 def _UiO_default_spam_settings(self, email_target):
     t_id = email_target.entity_id
     tt_str = str(Email._EmailTargetCode(email_target.email_target_type))
     # Set default spam settings if none found on this target
     esf = Email.EmailSpamFilter(self._db)
     if tt_str in cereconf.EMAIL_DEFAULT_SPAM_SETTINGS:
         if not len(cereconf.EMAIL_DEFAULT_SPAM_SETTINGS[tt_str]) == 2:
             raise Errors.CerebrumError(
                 "Error in cereconf.EMAIL_DEFAULT_SPAM_SETTINGS. "
                 "Expected 'key': ('val', 'val')")
         l, a = cereconf.EMAIL_DEFAULT_SPAM_SETTINGS[tt_str]
         lvl = int(Email._EmailSpamLevelCode(l))
         act = int(Email._EmailSpamActionCode(a))
         try:
             esf.find(t_id)
         except Errors.NotFoundError:
             esf.clear()
             esf.populate(lvl, act, parent=email_target)
             esf.write_db()
Ejemplo n.º 20
0
    def add_name_with_language(self, name_variant, name_language, name):
        """Adds name to project.

        This overrides the parent method to add verification of names.

        :see: EntityNameWithLanguage.add_name_with_language
        """
        if name_variant == self.const.ou_name_acronym:
            # TODO: Do we accept *changing* project names?

            # Validate the format of the acronym. The project name is used as a
            # prefix for other entities, like accounts, groups and machines, so
            # we need to be careful.
            self._validate_project_name(name)
            matched = self.search_tsd_projects(name=name, exact_match=True)
            if any(r['name'] == name for r in matched):
                raise Errors.CerebrumError('Acronym already in use: %s' % name)
        return super(TsdProjectMixin, self).add_name_with_language(
            name_variant, name_language, name)
Ejemplo n.º 21
0
def map_constants(constname, values, return_type=None):
    """Convert a constant-name/code or sequence of such to an int or list.

    constname is a Constants.py subclass of _CerebrumCode, e.g. '_SpreadCode'.
    The input values may be Constants.py names or Cerebrum names/codes.
    if return_type is None, return an int if 1 value, a list otherwise.
    If return_type is int or list, return that type, but fail if
    return_type is int and there is not exactly one input value.
    """
    arg = values
    if values is not None:
        if not isinstance(values, (list, tuple)):
            values = (values,)
        values = map(_decode_const, (constname,) * len(values), values)
        if return_type is not list and len(values) == 1:
            values = values[0]
    if return_type is int and not isinstance(values, (int, long)):
        raise _Errors.CerebrumError(
            'Expected 1 {0}: {1}'.format(constname, arg))
    return values
Ejemplo n.º 22
0
    def _generate_subnets_for_project_id(self, project_id):
        """Calculate which IPv4 and IPv6 subnets should be assigned
        to a project.

        :param int project_id:
            The entity ID of the project.

        :rtype: tuple of strings
        :return: The (ipv4, ipv6) subnet.
        """
        # This algorithm will only work until we hit project number 32768,
        # at that point the subnets will be invalid, like: 10.256.0.0/24
        if project_id > 32767:
            raise Errors.CerebrumError(
                'Project ID cannot be higher than 32767')
        # we start at 10.128.0.0/24 for project_id=0
        n = 32768 + project_id
        # second octet, third octet
        quotient, remainder = divmod(n, 256)
        return (cereconf.SUBNET_START % (quotient, remainder),
                cereconf.SUBNET_START_6 % hex(n)[2:])
Ejemplo n.º 23
0
def get_group(groupname, description):
    """Return a group instance for a group with the given name. If the group
    doesn't exist, it gets created. Also, the group is checked for proper
    settings, like the expire_date and lms-spread. It is in addition marked for
    being updated, so it won't be disabled.

    """
    group.clear()
    logger.debug("Getting group: %s", groupname)
    try:
        group.find_by_name(groupname)
    except Errors.NotFoundError:
        logger.info("Could not find group %s, creating it", groupname)
        # all groups are created/owned by bootstrap_account, id = 2
        group.populate(creator_id=2,
                       visibility=const.group_visibility_all,
                       name=groupname, 
                       description=description)
        try:
            group.write_db()
        except db.DatabaseError, m:
            raise Errors.CerebrumError("Database error: %s" % m)
Ejemplo n.º 24
0
    def get_next_free_vlan(self):
        """Get the first VLAN number that is not in use.

        :rtype: int
        :return: An available VLAN number not used by anyone.

        :raise Errors.CerebrumError: If no VLAN is available.
        """
        taken_vlans = set()
        subnet = Subnet.Subnet(self._db)
        for row in subnet.search():
            taken_vlans.add(row['vlan_number'])
        subnet6 = IPv6Subnet.IPv6Subnet(self._db)
        for row in subnet6.search():
            taken_vlans.add(row['vlan_number'])
        # TODO: Do we need a max value?
        for min, max in getattr(cereconf, 'VLAN_RANGES', ()):
            i = min
            while i <= max:
                if i not in taken_vlans:
                    return i
                i += 1
        raise Errors.CerebrumError("No free VLAN left")
Ejemplo n.º 25
0
    def setup_project(self, creator_id, vlan=None):
        """Set up an approved project properly.

        By setting up a project we mean:

         - Create the required project groups, according to config.
         - Reserve a vlan and subnet for the project.
         - Create the required project machines.

        More setup should be added here in the future, as this method should be
        called from all imports and jobs that creates TSD projects.

        Note that the given OU must have been set up with a proper project ID,
        stored as an `external_id`, and a project name, stored as an acronym,
        before this method could be called. The project must already be
        approved for this to happen, i.e. not in quarantine.

        :param int creator_id:
            The creator of the project. Either the `entity_id` of the
            administrator that created the project or a system user.

        :param int vlan:
            If given, sets the VLAN number to give to the project's subnets.
        """
        if not self.is_approved():
            raise Errors.CerebrumError("Project is not approved, cannot setup")

        # DNS and hosts
        self._setup_project_dns(creator_id, vlan)
        self._setup_project_hosts(creator_id)

        # Users and groups
        self._setup_project_users(creator_id)
        self._setup_project_groups(creator_id)

        # Posix?
        self._setup_project_posix(creator_id)
Ejemplo n.º 26
0
 def debug_raise_cerebrum_error(self, operator, strval="Foo Bar"):
     """ Raise a generic Cerebrum.Errors.CerebrumError. """
     raise Errors.CerebrumError(strval)
Ejemplo n.º 27
0
    def create_project_user(self, creator_id, basename,
                            fname=None, lname=None, gender=None, bdate=None,
                            shell='bash', affiliation=None):
        """Create a personal account affiliated with this project.

        Will also create a person to own the account.

        Names
            The special values "<pid>" and "<pname>" for `fname` or `lname`
            will be replaced with the project id and project name,
            respectively.  If a name is not given, the `basename` value will be
            used.

        :param int creator_id:
            The entity_id of the account that runs this method.

        :param str basename:
            The username base name. The actual username will be prefixed with
            the project ID of this project.

        :param str fname:
            The given name of the person that should own this account.

        :param str lname:
            The surname of the person that should own this account.

        :type gender: Gender, str, NoneType
        :param gender:
            The gender of the person that should own this account. If None,
            "X" (unknown) will be used.

        :type: bdate: mx.DateTime, str, NoneType
        :param bdate:
            The birth date for the person that should own this acocunt. If
            given as string, the expected format is yyyy-mm-dd.

        :type shell: PosixShell, str, NoneType
        :param shell:
            The shell for the account. If None, 'bash' will be set.

        :type affiliation: PersonAffiliation, PersonAffStatus, str, NoneType
        :param affiliation:
            An affiliation to give the person that owns the account. If string,
            the expected format is "AFFILIATION" or "AFFILIATION/status".

        :return PosixUser:
            Returns the created account.
        """
        names = {'<pid>': self.project_id, '<pname>': self.project_name}

        # Check arguments
        username = self._project_entity_name(basename)
        fname = names.get(fname) or fname or basename
        lname = names.get(lname) or lname or basename
        gender = (self.const.human2constant(six.text_type(gender),
                                            self.const.Gender) or
                  self.const.gender_unknown)
        shell = (self.const.human2constant(six.text_type(shell),
                                           self.const.PosixShell) or
                 self.const.posix_shell_bash)
        bdate = (DateTime.Parser.DateFromString(bdate, formats=('ymd1', ))
                 if bdate and not isinstance(bdate, DateTime.DateTimeType)
                 else bdate)
        aff, status = self.const.get_affiliation(affiliation)

        # TODO: Should this not be in Account.illegal_name?
        if username != username.lower():
            raise Errors.CerebrumError(
                "Account names cannot contain capital letters")

        person = Factory.get('Person')(self._db)
        user = Factory.get('PosixUser')(self._db)
        try:
            user.find_by_name(username)
        except Errors.NotFoundError:
            person.populate(bdate, gender,
                            description='Owner of %r' % username)
            person.write_db()

            uid = user.get_free_uid()
            user.populate(
                uid, None, None, shell,
                name=username,
                owner_type=self.const.entity_person,
                owner_id=person.entity_id,
                np_type=None,
                creator_id=creator_id,
                expire_date=None)
            user.write_db()
        else:
            person.find(user.owner_id)

        # Update person names and affiliations.
        person.affect_names(self.const.system_manual,
                            self.const.name_first,
                            self.const.name_last)
        person.populate_name(self.const.name_first, fname)
        person.populate_name(self.const.name_last, lname)
        if affiliation:
            person.populate_affiliation(self.const.system_manual,
                                        ou_id=self.entity_id,
                                        affiliation=aff,
                                        status=status)
        person.write_db()

        # Update user aff/account type
        user.set_account_type(self.entity_id, self.const.affiliation_project)
        user.write_db()

        if not self.is_affiliated_entity(user.pg):
            self.affiliate_entity(user.pg)
        return user
Ejemplo n.º 28
0
                       name=groupname, 
                       description=description)
        try:
            group.write_db()
        except db.DatabaseError, m:
            raise Errors.CerebrumError("Database error: %s" % m)
    touched_groups.add(group.entity_id)

    # check if the group is tagged for export to LMS, tag if not
    if not group.has_spread(const.spread_lms_group):
        logger.debug("Adding LMS-spread to group: %s", groupname)
        group.add_spread(const.spread_lms_group)
        try:
            group.write_db()
        except db.DatabaseError, m:
            raise Errors.CerebrumError("Database error: %s" % m)

    # check and remove the expire_date if set
    if group.expire_date:
        logger.debug("Removing expire_date for group: %s" % groupname)
        group.expire_date = None
        group.write_db()
    return group

def fill_group(gr, members, remove_others=False):
    """Add the given members to the given group. If L{remove_others} is True,
    existing members of the group that is not mentioned in L{members} are
    removed from the group."""
    logger.debug("Processing group %s, %d members given", gr.group_name,
                                                          len(members))
    existing_members = set(row['member_id'] for row in