Exemplo n.º 1
0
def subscription_valid(auth, username):
    """
    Valid a subscription

    Keyword argument:
        username -- Must be unique

    """
    from yunohost.user import user_create
    from yunohost.domain import domain_list
    from yunohost.app import app_ssowatconf

    info = subscription_info(auth, username)
    user_create(auth, info['username'], info['firstname'], info['lastname'],
                info['mail'], info['password'])

    #Update password
    new_attr_dict = {'userPassword': info['password']}

    if auth.update('uid=%s,ou=users' % username, new_attr_dict):
        app_ssowatconf(auth)
    else:
        raise MoulinetteError(169, m18n.n('user_creation_failed'))

    #Remove subscription from database
    cur = _get_db()
    cur.execute("DELETE FROM prefix_subscriptions WHERE `username`=%s",
                [username])
Exemplo n.º 2
0
def user_delete(auth, username, purge=False):
    """
    Delete user

    Keyword argument:
        username -- Username to delete
        purge

    """
    from yunohost.app import app_ssowatconf
    from yunohost.hook import hook_callback

    if auth.remove('uid=%s,ou=users' % username):
        # Invalidate passwd to take user deletion into account
        subprocess.call(['nscd', '-i', 'passwd'])

        # Update SFTP user group
        memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
        try: memberlist.remove(username)
        except: pass
        if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }):
            if purge:
                subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
    else:
        raise MoulinetteError(169, m18n.n('user_deletion_failed'))

    app_ssowatconf(auth)

    hook_callback('post_user_delete', args=[username, purge])

    logger.success(m18n.n('user_deleted'))
Exemplo n.º 3
0
def subscription_valid(auth, username):
    """
    Valid a subscription

    Keyword argument:
        username -- Must be unique

    """
    from yunohost.user import user_create
    from yunohost.domain import domain_list
    from yunohost.app import app_ssowatconf
    
    info=subscription_info(auth, username)
    user_create(auth, info['username'], info['firstname'], info['lastname'], info['mail'], info['password'])
    
    #Update password
    new_attr_dict = {'userPassword':info['password']}  

    if auth.update('uid=%s,ou=users' % username, new_attr_dict):
       app_ssowatconf(auth)
    else:
       raise MoulinetteError(169, m18n.n('user_creation_failed'))
    
    #Remove subscription from database
    cur = _get_db()     
    cur.execute("DELETE FROM prefix_subscriptions WHERE `username`=%s",[username])
Exemplo n.º 4
0
def user_delete(auth, username, purge=False):
    """
    Delete user

    Keyword argument:
        username -- Username to delete
        purge

    """
    from yunohost.app import app_ssowatconf
    from yunohost.hook import hook_callback

    if auth.remove('uid=%s,ou=users' % username):
        # Invalidate passwd to take user deletion into account
        subprocess.call(['nscd', '-i', 'passwd'])

        # Update SFTP user group
        memberlist = auth.search(filter='cn=sftpusers',
                                 attrs=['memberUid'])[0]['memberUid']
        try:
            memberlist.remove(username)
        except:
            pass
        if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
            if purge:
                subprocess.call(['rm', '-rf', '/home/{0}'.format(username)])
    else:
        raise MoulinetteError(169, m18n.n('user_deletion_failed'))

    app_ssowatconf(auth)

    hook_callback('post_user_delete', args=[username, purge])

    logger.success(m18n.n('user_deleted'))
Exemplo n.º 5
0
def user_delete(auth, username, purge=False):
    """
    Delete user

    Keyword argument:
        username -- Username to delete
        purge

    """
    from yunohost.app import app_ssowatconf

    if auth.remove('uid=%s,ou=users' % username):
        # Update SFTP user group
        memberlist = auth.search(filter='cn=sftpusers',
                                 attrs=['memberUid'])[0]['memberUid']
        try:
            memberlist.remove(username)
        except:
            pass
        if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
            if purge:
                os.system('rm -rf /home/%s' % username)
    else:
        raise MoulinetteError(169, m18n.n('user_deletion_failed'))

    app_ssowatconf(auth)
    msignals.display(m18n.n('user_deleted'), 'success')
Exemplo n.º 6
0
def permission_sync_to_user():
    """
    Sychronise the inheritPermission attribut in the permission object from the
    user<->group link and the group<->permission link
    """
    import os
    from yunohost.app import app_ssowatconf
    from yunohost.user import user_group_list
    from yunohost.utils.ldap import _get_ldap_interface

    ldap = _get_ldap_interface()

    groups = user_group_list(full=True)["groups"]
    permissions = user_permission_list(full=True)["permissions"]

    for permission_name, permission_infos in permissions.items():

        # These are the users currently allowed because there's an 'inheritPermission' object corresponding to it
        currently_allowed_users = set(permission_infos["corresponding_users"])

        # These are the users that should be allowed because they are member of a group that is allowed for this permission ...
        should_be_allowed_users = set([
            user for group in permission_infos["allowed"]
            for user in groups[group]["members"]
        ])

        # Note that a LDAP operation with the same value that is in LDAP crash SLAP.
        # So we need to check before each ldap operation that we really change something in LDAP
        if currently_allowed_users == should_be_allowed_users:
            # We're all good, this permission is already correctly synchronized !
            continue

        new_inherited_perms = {
            "inheritPermission": [
                "uid=%s,ou=users,dc=yunohost,dc=org" % u
                for u in should_be_allowed_users
            ],
            "memberUid":
            should_be_allowed_users,
        }

        # Commit the change with the new inherited stuff
        try:
            ldap.update("cn=%s,ou=permission" % permission_name,
                        new_inherited_perms)
        except Exception as e:
            raise YunohostError("permission_update_failed",
                                permission=permission_name,
                                error=e)

    logger.debug("The permission database has been resynchronized")

    app_ssowatconf()

    # Reload unscd, otherwise the group ain't propagated to the LDAP database
    os.system("nscd --invalidate=passwd")
    os.system("nscd --invalidate=group")
Exemplo n.º 7
0
def domain_main_domain(operation_logger, new_main_domain=None):
    """
    Check the current main domain, or change it

    Keyword argument:
        new_main_domain -- The new domain to be set as the main domain

    """
    from yunohost.tools import _set_hostname

    # If no new domain specified, we return the current main domain
    if not new_main_domain:
        return {'current_main_domain': _get_maindomain()}

    # Check domain exists
    if new_main_domain not in domain_list()['domains']:
        raise YunohostError('domain_name_unknown', domain=new_main_domain)

    operation_logger.related_to.append(('domain', new_main_domain))
    operation_logger.start()

    # Apply changes to ssl certs
    ssl_key = "/etc/ssl/private/yunohost_key.pem"
    ssl_crt = "/etc/ssl/private/yunohost_crt.pem"
    new_ssl_key = "/etc/yunohost/certs/%s/key.pem" % new_main_domain
    new_ssl_crt = "/etc/yunohost/certs/%s/crt.pem" % new_main_domain

    try:
        if os.path.exists(ssl_key) or os.path.lexists(ssl_key):
            os.remove(ssl_key)
        if os.path.exists(ssl_crt) or os.path.lexists(ssl_crt):
            os.remove(ssl_crt)

        os.symlink(new_ssl_key, ssl_key)
        os.symlink(new_ssl_crt, ssl_crt)

        _set_maindomain(new_main_domain)
    except Exception as e:
        logger.warning("%s" % e, exc_info=1)
        raise YunohostError('main_domain_change_failed')

    _set_hostname(new_main_domain)

    # Generate SSOwat configuration file
    app_ssowatconf()

    # Regen configurations
    try:
        with open('/etc/yunohost/installed', 'r'):
            regen_conf()
    except IOError:
        pass

    logger.success(m18n.n('main_domain_changed'))
Exemplo n.º 8
0
def _configure_for_acme_challenge(auth, domain):

    nginx_conf_folder = "/etc/nginx/conf.d/%s.d" % domain
    nginx_conf_file = "%s/000-acmechallenge.conf" % nginx_conf_folder

    nginx_configuration = '''
location '/.well-known/acme-challenge'
{
        default_type "text/plain";
        alias %s;
}
    ''' % WEBROOT_FOLDER

    # Check there isn't a conflicting file for the acme-challenge well-known
    # uri
    for path in glob.glob('%s/*.conf' % nginx_conf_folder):

        if path == nginx_conf_file:
            continue

        with open(path) as f:
            contents = f.read()

        if '/.well-known/acme-challenge' in contents:
            raise MoulinetteError(errno.EINVAL, m18n.n(
                'certmanager_conflicting_nginx_file', filepath=path))

    # Write the conf
    if os.path.exists(nginx_conf_file):
        logger.info(
            "Nginx configuration file for ACME challenge already exists for domain, skipping.")
        return

    logger.info(
        "Adding Nginx configuration file for Acme challenge for domain %s.", domain)

    with open(nginx_conf_file, "w") as f:
        f.write(nginx_configuration)

    # Assume nginx conf is okay, and reload it
    # (FIXME : maybe add a check that it is, using nginx -t, haven't found
    # any clean function already implemented in yunohost to do this though)
    _run_service_command("reload", "nginx")

    app_ssowatconf(auth)
Exemplo n.º 9
0
def domain_main_domain(operation_logger, new_main_domain=None):
    """
    Check the current main domain, or change it

    Keyword argument:
        new_main_domain -- The new domain to be set as the main domain

    """
    from yunohost.tools import _set_hostname

    # If no new domain specified, we return the current main domain
    if not new_main_domain:
        return {"current_main_domain": _get_maindomain()}

    # Check domain exists
    if new_main_domain not in domain_list()["domains"]:
        raise YunohostValidationError("domain_name_unknown",
                                      domain=new_main_domain)

    operation_logger.related_to.append(("domain", new_main_domain))
    operation_logger.start()

    # Apply changes to ssl certs
    try:
        write_to_file("/etc/yunohost/current_host", new_main_domain)

        _set_hostname(new_main_domain)
    except Exception as e:
        logger.warning("%s" % e, exc_info=1)
        raise YunohostError("main_domain_change_failed")

    # Generate SSOwat configuration file
    app_ssowatconf()

    # Regen configurations
    if os.path.exists("/etc/yunohost/installed"):
        regen_conf()

    logger.success(m18n.n("main_domain_changed"))
Exemplo n.º 10
0
def clean():

    # Make sure we have a ssowat
    os.system("mkdir -p /etc/ssowat/")
    app_ssowatconf()

    test_apps = ["break_yo_system", "legacy_app", "legacy_app__2", "full_domain_app"]

    for test_app in test_apps:

        if _is_installed(test_app):
            app_remove(test_app)

        for filepath in glob.glob("/etc/nginx/conf.d/*.d/*%s*" % test_app):
            os.remove(filepath)
        for folderpath in glob.glob("/etc/yunohost/apps/*%s*" % test_app):
            shutil.rmtree(folderpath, ignore_errors=True)
        for folderpath in glob.glob("/var/www/*%s*" % test_app):
            shutil.rmtree(folderpath, ignore_errors=True)

        os.system(
            "bash -c \"mysql -u root --password=$(cat /etc/yunohost/mysql) 2>/dev/null <<< 'DROP DATABASE %s' \""
            % test_app
        )
        os.system(
            "bash -c \"mysql -u root --password=$(cat /etc/yunohost/mysql) 2>/dev/null <<< 'DROP USER %s@localhost'\""
            % test_app
        )

    os.system(
        "systemctl reset-failed nginx"
    )  # Reset failed quota for service to avoid running into start-limit rate ?
    os.system("systemctl start nginx")

    # Clean permissions
    for permission_name in user_permission_list(short=True)["permissions"]:
        if any(test_app in permission_name for test_app in test_apps):
            permission_delete(permission_name, force=True)
Exemplo n.º 11
0
def user_delete(auth, username, purge=False):
    """
    Delete user

    Keyword argument:
        username -- Username to delete
        purge

    """
    from yunohost.app import app_ssowatconf

    if auth.remove('uid=%s,ou=users' % username):
        # Update SFTP user group
        memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
        try: memberlist.remove(username)
        except: pass
        if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }):
            if purge:
                os.system('rm -rf /home/%s' % username)
    else:
        raise MoulinetteError(169, m18n.n('user_deletion_failed'))

    app_ssowatconf(auth)
    msignals.display(m18n.n('user_deleted'), 'success')
Exemplo n.º 12
0
def user_update(auth, username, firstname=None, lastname=None, mail=None,
        change_password=None, add_mailforward=None, remove_mailforward=None,
        add_mailalias=None, remove_mailalias=None, mailbox_quota=None):
    """
    Update user informations

    Keyword argument:
        lastname
        mail
        firstname
        add_mailalias -- Mail aliases to add
        remove_mailforward -- Mailforward addresses to remove
        username -- Username of user to update
        add_mailforward -- Mailforward addresses to add
        change_password -- New password to set
        remove_mailalias -- Mail aliases to remove

    """
    from yunohost.domain import domain_list
    from yunohost.app import app_ssowatconf

    attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
    new_attr_dict = {}
    domains = domain_list(auth)['domains']

    # Populate user informations
    result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch)
    if not result:
        raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username))
    user = result[0]

    # Get modifications from arguments
    if firstname:
        new_attr_dict['givenName'] = firstname # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict['displayName'] = firstname + ' ' + user['sn'][0]

    if lastname:
        new_attr_dict['sn'] = lastname # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict['displayName'] = user['givenName'][0] + ' ' + lastname

    if lastname and firstname:
        new_attr_dict['cn'] = new_attr_dict['displayName'] = firstname + ' ' + lastname

    if change_password:
        char_set = string.ascii_uppercase + string.digits
        salt = ''.join(random.sample(char_set,8))
        salt = '$1$' + salt + '$'
        new_attr_dict['userPassword'] = '******' + crypt.crypt(str(change_password), salt)

    if mail:
        auth.validate_uniqueness({ 'mail': mail })
        if mail[mail.find('@')+1:] not in domains:
            raise MoulinetteError(errno.EINVAL,
                                  m18n.n('mail_domain_unknown',
                                          domain=mail[mail.find('@')+1:]))
        del user['mail'][0]
        new_attr_dict['mail'] = [mail] + user['mail']

    if add_mailalias:
        if not isinstance(add_mailalias, list):
            add_mailalias = [ add_mailalias ]
        for mail in add_mailalias:
            auth.validate_uniqueness({ 'mail': mail })
            if mail[mail.find('@')+1:] not in domains:
                raise MoulinetteError(errno.EINVAL,
                                      m18n.n('mail_domain_unknown',
                                             domain=mail[mail.find('@')+1:]))
            user['mail'].append(mail)
        new_attr_dict['mail'] = user['mail']

    if remove_mailalias:
        if not isinstance(remove_mailalias, list):
            remove_mailalias = [ remove_mailalias ]
        for mail in remove_mailalias:
            if len(user['mail']) > 1 and mail in user['mail'][1:]:
                user['mail'].remove(mail)
            else:
                raise MoulinetteError(errno.EINVAL,
                                      m18n.n('mail_alias_remove_failed', mail=mail))
        new_attr_dict['mail'] = user['mail']

    if add_mailforward:
        if not isinstance(add_mailforward, list):
            add_mailforward = [ add_mailforward ]
        for mail in add_mailforward:
            if mail in user['maildrop'][1:]:
                continue
            user['maildrop'].append(mail)
        new_attr_dict['maildrop'] = user['maildrop']

    if remove_mailforward:
        if not isinstance(remove_mailforward, list):
            remove_mailforward = [ remove_mailforward ]
        for mail in remove_mailforward:
            if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
                user['maildrop'].remove(mail)
            else:
                raise MoulinetteError(errno.EINVAL,
                                      m18n.n('mail_forward_remove_failed', mail=mail))
        new_attr_dict['maildrop'] = user['maildrop']

    if mailbox_quota is not None:
        new_attr_dict['mailuserquota'] = mailbox_quota

    if auth.update('uid=%s,ou=users' % username, new_attr_dict):
       logger.success(m18n.n('user_updated'))
       app_ssowatconf(auth)
       return user_info(auth, username)
    else:
       raise MoulinetteError(169, m18n.n('user_update_failed'))
Exemplo n.º 13
0
def domain_add(operation_logger, domain, dyndns=False):
    """
    Create a custom domain

    Keyword argument:
        domain -- Domain name to add
        dyndns -- Subscribe to DynDNS

    """
    from yunohost.hook import hook_callback
    from yunohost.app import app_ssowatconf
    from yunohost.utils.ldap import _get_ldap_interface

    if domain.startswith("xmpp-upload."):
        raise YunohostError("domain_cannot_add_xmpp_upload")

    ldap = _get_ldap_interface()

    try:
        ldap.validate_uniqueness({"virtualdomain": domain})
    except MoulinetteError:
        raise YunohostError("domain_exists")

    operation_logger.start()

    # Lower domain to avoid some edge cases issues
    # See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765
    domain = domain.lower()

    # DynDNS domain
    if dyndns:

        # Do not allow to subscribe to multiple dyndns domains...
        if os.path.exists("/etc/cron.d/yunohost-dyndns"):
            raise YunohostError("domain_dyndns_already_subscribed")

        from yunohost.dyndns import dyndns_subscribe, _dyndns_provides

        # Check that this domain can effectively be provided by
        # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st)
        if not _dyndns_provides("dyndns.yunohost.org", domain):
            raise YunohostError("domain_dyndns_root_unknown")

        # Actually subscribe
        dyndns_subscribe(domain=domain)

    try:
        import yunohost.certificate

        yunohost.certificate._certificate_install_selfsigned([domain], False)

        attr_dict = {
            "objectClass": ["mailDomain", "top"],
            "virtualdomain": domain,
        }

        try:
            ldap.add("virtualdomain=%s,ou=domains" % domain, attr_dict)
        except Exception as e:
            raise YunohostError("domain_creation_failed",
                                domain=domain,
                                error=e)

        # Don't regen these conf if we're still in postinstall
        if os.path.exists("/etc/yunohost/installed"):
            # Sometime we have weird issues with the regenconf where some files
            # appears as manually modified even though they weren't touched ...
            # There are a few ideas why this happens (like backup/restore nginx
            # conf ... which we shouldnt do ...). This in turns creates funky
            # situation where the regenconf may refuse to re-create the conf
            # (when re-creating a domain..)
            # So here we force-clear the has out of the regenconf if it exists.
            # This is a pretty ad hoc solution and only applied to nginx
            # because it's one of the major service, but in the long term we
            # should identify the root of this bug...
            _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain])
            regen_conf(
                names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd"])
            app_ssowatconf()

    except Exception:
        # Force domain removal silently
        try:
            domain_remove(domain, force=True)
        except Exception:
            pass
        raise

    hook_callback("post_domain_add", args=[domain])

    logger.success(m18n.n("domain_created"))
Exemplo n.º 14
0
def domain_remove(operation_logger, domain, remove_apps=False, force=False):
    """
    Delete domains

    Keyword argument:
        domain -- Domain to delete
        remove_apps -- Remove applications installed on the domain
        force -- Force the domain removal and don't not ask confirmation to
                 remove apps if remove_apps is specified

    """
    from yunohost.hook import hook_callback
    from yunohost.app import app_ssowatconf, app_info, app_remove
    from yunohost.utils.ldap import _get_ldap_interface

    # the 'force' here is related to the exception happening in domain_add ...
    # we don't want to check the domain exists because the ldap add may have
    # failed
    if not force and domain not in domain_list()['domains']:
        raise YunohostError('domain_name_unknown', domain=domain)

    # Check domain is not the main domain
    if domain == _get_maindomain():
        other_domains = domain_list()["domains"]
        other_domains.remove(domain)

        if other_domains:
            raise YunohostError(
                "domain_cannot_remove_main",
                domain=domain,
                other_domains="\n * " + ("\n * ".join(other_domains)),
            )
        else:
            raise YunohostError("domain_cannot_remove_main_add_new_one",
                                domain=domain)

    # Check if apps are installed on the domain
    apps_on_that_domain = []

    for app in _installed_apps():
        settings = _get_app_settings(app)
        label = app_info(app)["name"]
        if settings.get("domain") == domain:
            apps_on_that_domain.append(
                (app, "    - %s \"%s\" on https://%s%s" %
                 (app, label, domain, settings["path"])
                 if "path" in settings else app))

    if apps_on_that_domain:
        if remove_apps:
            if msettings.get('interface') == "cli" and not force:
                answer = msignals.prompt(m18n.n(
                    'domain_remove_confirm_apps_removal',
                    apps="\n".join([x[1] for x in apps_on_that_domain]),
                    answers='y/N'),
                                         color="yellow")
                if answer.upper() != "Y":
                    raise YunohostError("aborting")

            for app, _ in apps_on_that_domain:
                app_remove(app)
        else:
            raise YunohostError('domain_uninstall_app_first',
                                apps="\n".join(
                                    [x[1] for x in apps_on_that_domain]))

    operation_logger.start()
    ldap = _get_ldap_interface()
    try:
        ldap.remove("virtualdomain=" + domain + ",ou=domains")
    except Exception as e:
        raise YunohostError("domain_deletion_failed", domain=domain, error=e)

    os.system("rm -rf /etc/yunohost/certs/%s" % domain)

    # Sometime we have weird issues with the regenconf where some files
    # appears as manually modified even though they weren't touched ...
    # There are a few ideas why this happens (like backup/restore nginx
    # conf ... which we shouldnt do ...). This in turns creates funky
    # situation where the regenconf may refuse to re-create the conf
    # (when re-creating a domain..)
    #
    # So here we force-clear the has out of the regenconf if it exists.
    # This is a pretty ad hoc solution and only applied to nginx
    # because it's one of the major service, but in the long term we
    # should identify the root of this bug...
    _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain])
    # And in addition we even force-delete the file Otherwise, if the file was
    # manually modified, it may not get removed by the regenconf which leads to
    # catastrophic consequences of nginx breaking because it can't load the
    # cert file which disappeared etc..
    if os.path.exists("/etc/nginx/conf.d/%s.conf" % domain):
        _process_regen_conf("/etc/nginx/conf.d/%s.conf" % domain,
                            new_conf=None,
                            save=True)

    regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix"])
    app_ssowatconf()

    hook_callback("post_domain_remove", args=[domain])

    logger.success(m18n.n("domain_deleted"))
Exemplo n.º 15
0
def user_create(auth,
                username,
                firstname,
                lastname,
                mail,
                password,
                mailbox_quota="0"):
    """
    Create user

    Keyword argument:
        firstname
        lastname
        username -- Must be unique
        mail -- Main mail address must be unique
        password
        mailbox_quota -- Mailbox size quota

    """
    import pwd
    from yunohost.domain import domain_list
    from yunohost.hook import hook_callback
    from yunohost.app import app_ssowatconf

    # Validate uniqueness of username and mail in LDAP
    auth.validate_uniqueness({'uid': username, 'mail': mail})

    # Validate uniqueness of username in system users
    try:
        pwd.getpwnam(username)
    except KeyError:
        pass
    else:
        raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists'))

    # Check that the mail domain exists
    if mail[mail.find('@') + 1:] not in domain_list(auth)['domains']:
        raise MoulinetteError(
            errno.EINVAL,
            m18n.n('mail_domain_unknown', domain=mail[mail.find('@') + 1:]))

    # Get random UID/GID
    uid_check = gid_check = 0
    while uid_check == 0 and gid_check == 0:
        uid = str(random.randint(200, 99999))
        uid_check = os.system("getent passwd %s" % uid)
        gid_check = os.system("getent group %s" % uid)

    # Adapt values for LDAP
    fullname = '%s %s' % (firstname, lastname)
    rdn = 'uid=%s,ou=users' % username
    char_set = string.ascii_uppercase + string.digits
    salt = ''.join(random.sample(char_set, 8))
    salt = '$1$' + salt + '$'
    user_pwd = '{CRYPT}' + crypt.crypt(str(password), salt)
    attr_dict = {
        'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount'],
        'givenName': firstname,
        'sn': lastname,
        'displayName': fullname,
        'cn': fullname,
        'uid': username,
        'mail': mail,
        'maildrop': username,
        'mailuserquota': mailbox_quota,
        'userPassword': user_pwd,
        'gidNumber': uid,
        'uidNumber': uid,
        'homeDirectory': '/home/' + username,
        'loginShell': '/bin/false'
    }

    # If it is the first user, add some aliases
    if not auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'):
        with open('/etc/yunohost/current_host') as f:
            main_domain = f.readline().rstrip()
        aliases = [
            'root@' + main_domain,
            'admin@' + main_domain,
            'webmaster@' + main_domain,
            'postmaster@' + main_domain,
        ]
        attr_dict['mail'] = [attr_dict['mail']] + aliases

        # If exists, remove the redirection from the SSO
        try:
            with open('/etc/ssowat/conf.json.persistent') as json_conf:
                ssowat_conf = json.loads(str(json_conf.read()))
        except ValueError as e:
            raise MoulinetteError(
                errno.EINVAL,
                m18n.n('ssowat_persistent_conf_read_error', error=e.strerror))
        except IOError:
            ssowat_conf = {}

        if 'redirected_urls' in ssowat_conf and '/' in ssowat_conf[
                'redirected_urls']:
            del ssowat_conf['redirected_urls']['/']
            try:
                with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
                    json.dump(ssowat_conf, f, sort_keys=True, indent=4)
            except IOError as e:
                raise MoulinetteError(
                    errno.EPERM,
                    m18n.n('ssowat_persistent_conf_write_error',
                           error=e.strerror))

    if auth.add(rdn, attr_dict):
        # Invalidate passwd to take user creation into account
        subprocess.call(['nscd', '-i', 'passwd'])

        # Update SFTP user group
        memberlist = auth.search(filter='cn=sftpusers',
                                 attrs=['memberUid'])[0]['memberUid']
        memberlist.append(username)
        if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}):
            try:
                # Attempt to create user home folder
                subprocess.check_call(['su', '-', username, '-c', "''"])
            except subprocess.CalledProcessError:
                if not os.path.isdir('/home/{0}'.format(username)):
                    logger.warning(m18n.n('user_home_creation_failed'),
                                   exc_info=1)
            app_ssowatconf(auth)
            # TODO: Send a welcome mail to user
            logger.success(m18n.n('user_created'))
            hook_callback('post_user_create',
                          args=[username, mail, password, firstname, lastname])

            return {'fullname': fullname, 'username': username, 'mail': mail}

    raise MoulinetteError(169, m18n.n('user_creation_failed'))
Exemplo n.º 16
0
def user_update(auth,
                username,
                firstname=None,
                lastname=None,
                mail=None,
                change_password=None,
                add_mailforward=None,
                remove_mailforward=None,
                add_mailalias=None,
                remove_mailalias=None,
                mailbox_quota=None):
    """
    Update user informations

    Keyword argument:
        lastname
        mail
        firstname
        add_mailalias -- Mail aliases to add
        remove_mailforward -- Mailforward addresses to remove
        username -- Username of user to update
        add_mailforward -- Mailforward addresses to add
        change_password -- New password to set
        remove_mailalias -- Mail aliases to remove

    """
    from yunohost.domain import domain_list
    from yunohost.app import app_ssowatconf

    attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
    new_attr_dict = {}
    domains = domain_list(auth)['domains']

    # Populate user informations
    result = auth.search(base='ou=users,dc=yunohost,dc=org',
                         filter='uid=' + username,
                         attrs=attrs_to_fetch)
    if not result:
        raise MoulinetteError(errno.EINVAL,
                              m18n.n('user_unknown', user=username))
    user = result[0]

    # Get modifications from arguments
    if firstname:
        new_attr_dict['givenName'] = firstname  # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict[
            'displayName'] = firstname + ' ' + user['sn'][0]

    if lastname:
        new_attr_dict['sn'] = lastname  # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict[
            'displayName'] = user['givenName'][0] + ' ' + lastname

    if lastname and firstname:
        new_attr_dict['cn'] = new_attr_dict[
            'displayName'] = firstname + ' ' + lastname

    if change_password:
        char_set = string.ascii_uppercase + string.digits
        salt = ''.join(random.sample(char_set, 8))
        salt = '$1$' + salt + '$'
        new_attr_dict['userPassword'] = '******' + crypt.crypt(
            str(change_password), salt)

    if mail:
        auth.validate_uniqueness({'mail': mail})
        if mail[mail.find('@') + 1:] not in domains:
            raise MoulinetteError(
                errno.EINVAL,
                m18n.n('mail_domain_unknown',
                       domain=mail[mail.find('@') + 1:]))
        del user['mail'][0]
        new_attr_dict['mail'] = [mail] + user['mail']

    if add_mailalias:
        if not isinstance(add_mailalias, list):
            add_mailalias = [add_mailalias]
        for mail in add_mailalias:
            auth.validate_uniqueness({'mail': mail})
            if mail[mail.find('@') + 1:] not in domains:
                raise MoulinetteError(
                    errno.EINVAL,
                    m18n.n('mail_domain_unknown',
                           domain=mail[mail.find('@') + 1:]))
            user['mail'].append(mail)
        new_attr_dict['mail'] = user['mail']

    if remove_mailalias:
        if not isinstance(remove_mailalias, list):
            remove_mailalias = [remove_mailalias]
        for mail in remove_mailalias:
            if len(user['mail']) > 1 and mail in user['mail'][1:]:
                user['mail'].remove(mail)
            else:
                raise MoulinetteError(
                    errno.EINVAL, m18n.n('mail_alias_remove_failed',
                                         mail=mail))
        new_attr_dict['mail'] = user['mail']

    if add_mailforward:
        if not isinstance(add_mailforward, list):
            add_mailforward = [add_mailforward]
        for mail in add_mailforward:
            if mail in user['maildrop'][1:]:
                continue
            user['maildrop'].append(mail)
        new_attr_dict['maildrop'] = user['maildrop']

    if remove_mailforward:
        if not isinstance(remove_mailforward, list):
            remove_mailforward = [remove_mailforward]
        for mail in remove_mailforward:
            if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
                user['maildrop'].remove(mail)
            else:
                raise MoulinetteError(
                    errno.EINVAL,
                    m18n.n('mail_forward_remove_failed', mail=mail))
        new_attr_dict['maildrop'] = user['maildrop']

    if mailbox_quota is not None:
        new_attr_dict['mailuserquota'] = mailbox_quota

    if auth.update('uid=%s,ou=users' % username, new_attr_dict):
        logger.success(m18n.n('user_updated'))
        app_ssowatconf(auth)
        return user_info(auth, username)
    else:
        raise MoulinetteError(169, m18n.n('user_update_failed'))
Exemplo n.º 17
0
def tools_postinstall(domain, password, ignore_dyndns=False):
    """
    YunoHost post-install

    Keyword argument:
        domain -- YunoHost main domain
        ignore_dyndns -- Do not subscribe domain to a DynDNS service
        password -- YunoHost admin password

    """
    dyndns = not ignore_dyndns

    # Do some checks at first
    if os.path.isfile('/etc/yunohost/installed'):
        raise MoulinetteError(errno.EPERM,
                              m18n.n('yunohost_already_installed'))
    if len(domain.split('.')) >= 3 and not ignore_dyndns:
        try:
            r = requests.get('https://dyndns.yunohost.org/domains')
        except requests.ConnectionError:
            pass
        else:
            dyndomains = json.loads(r.text)
            dyndomain  = '.'.join(domain.split('.')[1:])
            if dyndomain in dyndomains:
                if requests.get('https://dyndns.yunohost.org/test/%s' % domain).status_code == 200:
                    dyndns = True
                else:
                    raise MoulinetteError(errno.EEXIST,
                                          m18n.n('dyndns_unavailable'))

    logger.info(m18n.n('yunohost_installing'))

    # Instantiate LDAP Authenticator
    auth = init_authenticator(('ldap', 'default'),
                              {'uri': "ldap://localhost:389",
                               'base_dn': "dc=yunohost,dc=org",
                               'user_rdn': "cn=admin" })
    auth.authenticate('yunohost')

    # Initialize LDAP for YunoHost
    # TODO: Improve this part by integrate ldapinit into conf_regen hook
    tools_ldapinit(auth)

    # Create required folders
    folders_to_create = [
        '/etc/yunohost/apps',
        '/etc/yunohost/certs',
        '/var/cache/yunohost/repo',
        '/home/yunohost.backup',
        '/home/yunohost.app'
    ]

    for folder in folders_to_create:
        try: os.listdir(folder)
        except OSError: os.makedirs(folder)

    # Change folders permissions
    os.system('chmod 755 /home/yunohost.app')

    # Set hostname to avoid amavis bug
    if os.system('hostname -d') != 0:
        os.system('hostname yunohost.yunohost.org')

    # Add a temporary SSOwat rule to redirect SSO to admin page
    try:
        with open('/etc/ssowat/conf.json.persistent') as json_conf:
            ssowat_conf = json.loads(str(json_conf.read()))
    except IOError:
        ssowat_conf = {}

    if 'redirected_urls' not in ssowat_conf:
        ssowat_conf['redirected_urls'] = {}

    ssowat_conf['redirected_urls']['/'] = domain +'/yunohost/admin'

    with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
        json.dump(ssowat_conf, f, sort_keys=True, indent=4)

    os.system('chmod 644 /etc/ssowat/conf.json.persistent')

    # Create SSL CA
    service_regen_conf(['ssl'], force=True)
    ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
    command_list = [
        'echo "01" > %s/serial' % ssl_dir,
        'rm %s/index.txt'       % ssl_dir,
        'touch %s/index.txt'    % ssl_dir,
        'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir),
        'sed -i "s/yunohost.org/%s/g" %s/openssl.ca.cnf ' % (domain, ssl_dir),
        'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch' % (ssl_dir, ssl_dir, ssl_dir),
        'cp %s/ca/cacert.pem /etc/ssl/certs/ca-yunohost_crt.pem' % ssl_dir,
        'update-ca-certificates'
    ]

    for command in command_list:
        if os.system(command) != 0:
            raise MoulinetteError(errno.EPERM,
                                  m18n.n('yunohost_ca_creation_failed'))

    # New domain config
    tools_maindomain(auth, old_domain='yunohost.org', new_domain=domain, dyndns=dyndns)

    # Generate SSOwat configuration file
    app_ssowatconf(auth)

    # Change LDAP admin password
    tools_adminpw(auth, password)

    # Enable UPnP silently and reload firewall
    firewall_upnp('enable', no_refresh=True)

    os.system('touch /etc/yunohost/installed')

    # Enable and start YunoHost firewall at boot time
    os.system('update-rc.d yunohost-firewall enable')
    os.system('service yunohost-firewall start')

    service_regen_conf(force=True)

    logger.success(m18n.n('yunohost_configured'))
Exemplo n.º 18
0
def tools_postinstall(domain, password, ignore_dyndns=False):
    """
    YunoHost post-install

    Keyword argument:
        domain -- YunoHost main domain
        ignore_dyndns -- Do not subscribe domain to a DynDNS service (only
        needed for nohost.me, noho.st domains)
        password -- YunoHost admin password

    """
    dyndns = not ignore_dyndns

    # Do some checks at first
    if os.path.isfile('/etc/yunohost/installed'):
        raise MoulinetteError(errno.EPERM,
                              m18n.n('yunohost_already_installed'))
    if len(domain.split('.')) >= 3 and not ignore_dyndns:
        try:
            r = requests.get('https://dyndns.yunohost.org/domains')
        except requests.ConnectionError:
            pass
        else:
            dyndomains = json.loads(r.text)
            dyndomain = '.'.join(domain.split('.')[1:])

            if dyndomain in dyndomains:
                if requests.get('https://dyndns.yunohost.org/test/%s' %
                                domain).status_code == 200:
                    dyndns = True
                else:
                    raise MoulinetteError(errno.EEXIST,
                                          m18n.n('dyndns_unavailable'))
            else:
                dyndns = False
    else:
        dyndns = False

    logger.info(m18n.n('yunohost_installing'))

    # Initialize LDAP for YunoHost
    # TODO: Improve this part by integrate ldapinit into conf_regen hook
    auth = tools_ldapinit()

    # Create required folders
    folders_to_create = [
        '/etc/yunohost/apps', '/etc/yunohost/certs',
        '/var/cache/yunohost/repo', '/home/yunohost.backup',
        '/home/yunohost.app'
    ]

    for folder in folders_to_create:
        try:
            os.listdir(folder)
        except OSError:
            os.makedirs(folder)

    # Change folders permissions
    os.system('chmod 755 /home/yunohost.app')

    # Set hostname to avoid amavis bug
    if os.system('hostname -d') != 0:
        os.system('hostname yunohost.yunohost.org')

    # Add a temporary SSOwat rule to redirect SSO to admin page
    try:
        with open('/etc/ssowat/conf.json.persistent') as json_conf:
            ssowat_conf = json.loads(str(json_conf.read()))
    except ValueError as e:
        raise MoulinetteError(
            errno.EINVAL,
            m18n.n('ssowat_persistent_conf_read_error', error=e.strerror))
    except IOError:
        ssowat_conf = {}

    if 'redirected_urls' not in ssowat_conf:
        ssowat_conf['redirected_urls'] = {}

    ssowat_conf['redirected_urls']['/'] = domain + '/yunohost/admin'

    try:
        with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
            json.dump(ssowat_conf, f, sort_keys=True, indent=4)
    except IOError as e:
        raise MoulinetteError(
            errno.EPERM,
            m18n.n('ssowat_persistent_conf_write_error', error=e.strerror))

    os.system('chmod 644 /etc/ssowat/conf.json.persistent')

    # Create SSL CA
    service_regen_conf(['ssl'], force=True)
    ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
    command_list = [
        'echo "01" > %s/serial' % ssl_dir,
        'rm %s/index.txt' % ssl_dir,
        'touch %s/index.txt' % ssl_dir,
        'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir),
        'sed -i "s/yunohost.org/%s/g" %s/openssl.ca.cnf ' % (domain, ssl_dir),
        'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch'
        % (ssl_dir, ssl_dir, ssl_dir),
        'cp %s/ca/cacert.pem /etc/ssl/certs/ca-yunohost_crt.pem' % ssl_dir,
        'update-ca-certificates'
    ]

    for command in command_list:
        if os.system(command) != 0:
            raise MoulinetteError(errno.EPERM,
                                  m18n.n('yunohost_ca_creation_failed'))

    # New domain config
    domain_add(auth, domain, dyndns)
    tools_maindomain(auth, domain)

    # Generate SSOwat configuration file
    app_ssowatconf(auth)

    # Change LDAP admin password
    tools_adminpw(auth, password)

    # Enable UPnP silently and reload firewall
    firewall_upnp('enable', no_refresh=True)

    os.system('touch /etc/yunohost/installed')

    # Enable and start YunoHost firewall at boot time
    os.system('update-rc.d yunohost-firewall enable')
    os.system('service yunohost-firewall start')

    service_regen_conf(force=True)
    logger.success(m18n.n('yunohost_configured'))
Exemplo n.º 19
0
def domain_remove(operation_logger, domain, force=False):
    """
    Delete domains

    Keyword argument:
        domain -- Domain to delete
        force -- Force the domain removal

    """
    from yunohost.hook import hook_callback
    from yunohost.app import app_ssowatconf, app_info
    from yunohost.utils.ldap import _get_ldap_interface

    if not force and domain not in domain_list()['domains']:
        raise YunohostError('domain_name_unknown', domain=domain)

    # Check domain is not the main domain
    if domain == _get_maindomain():
        other_domains = domain_list()["domains"]
        other_domains.remove(domain)

        if other_domains:
            raise YunohostError('domain_cannot_remove_main',
                                domain=domain,
                                other_domains="\n * " +
                                ("\n * ".join(other_domains)))
        else:
            raise YunohostError('domain_cannot_remove_main_add_new_one',
                                domain=domain)

    # Check if apps are installed on the domain
    apps_on_that_domain = []

    for app in _installed_apps():
        settings = _get_app_settings(app)
        label = app_info(app)["name"]
        if settings.get("domain") == domain:
            apps_on_that_domain.append("    - %s \"%s\" on https://%s%s" %
                                       (app, label, domain, settings["path"])
                                       if "path" in settings else app)

    if apps_on_that_domain:
        raise YunohostError('domain_uninstall_app_first',
                            apps="\n".join(apps_on_that_domain))

    operation_logger.start()
    ldap = _get_ldap_interface()
    try:
        ldap.remove('virtualdomain=' + domain + ',ou=domains')
    except Exception as e:
        raise YunohostError('domain_deletion_failed', domain=domain, error=e)

    os.system('rm -rf /etc/yunohost/certs/%s' % domain)

    # Sometime we have weird issues with the regenconf where some files
    # appears as manually modified even though they weren't touched ...
    # There are a few ideas why this happens (like backup/restore nginx
    # conf ... which we shouldnt do ...). This in turns creates funky
    # situation where the regenconf may refuse to re-create the conf
    # (when re-creating a domain..)
    #
    # So here we force-clear the has out of the regenconf if it exists.
    # This is a pretty ad hoc solution and only applied to nginx
    # because it's one of the major service, but in the long term we
    # should identify the root of this bug...
    _force_clear_hashes(["/etc/nginx/conf.d/%s.conf" % domain])
    # And in addition we even force-delete the file Otherwise, if the file was
    # manually modified, it may not get removed by the regenconf which leads to
    # catastrophic consequences of nginx breaking because it can't load the
    # cert file which disappeared etc..
    if os.path.exists("/etc/nginx/conf.d/%s.conf" % domain):
        _process_regen_conf("/etc/nginx/conf.d/%s.conf" % domain,
                            new_conf=None,
                            save=True)

    regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
    app_ssowatconf()

    hook_callback('post_domain_remove', args=[domain])

    logger.success(m18n.n('domain_deleted'))
Exemplo n.º 20
0
def reset_ssowat_conf():

    # Make sure we have a ssowat
    os.system("mkdir -p /etc/ssowat/")
    app_ssowatconf()
Exemplo n.º 21
0
def tools_postinstall(domain, password, dyndns=False):
    """
    YunoHost post-install

    Keyword argument:
        domain -- YunoHost main domain
        dyndns -- Subscribe domain to a DynDNS service
        password -- YunoHost admin password

    """
    from moulinette.core import init_authenticator

    from yunohost.backup import backup_init
    from yunohost.app import app_ssowatconf
    from yunohost.firewall import firewall_upnp, firewall_reload

    try:
        with open('/etc/yunohost/installed') as f: pass
    except IOError:
        msignals.display(m18n.n('yunohost_installing'))
    else:
        raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed'))

    if len(domain.split('.')) >= 3:
        try:
            r = requests.get('http://dyndns.yunohost.org/domains')
        except ConnectionError:
            pass
        else:
            dyndomains = json.loads(r.text)
            dyndomain  = '.'.join(domain.split('.')[1:])
            if dyndomain in dyndomains:
                if requests.get('http://dyndns.yunohost.org/test/%s' % domain).status_code == 200:
                    dyndns=True
                else:
                    raise MoulinetteError(errno.EEXIST,
                                      m18n.n('dyndns_unavailable'))

    # Create required folders
    folders_to_create = [
        '/etc/yunohost/apps',
        '/etc/yunohost/certs',
        '/var/cache/yunohost/repo',
        '/home/yunohost.backup',
        '/home/yunohost.app'
    ]

    for folder in folders_to_create:
        try: os.listdir(folder)
        except OSError: os.makedirs(folder)

    # Set hostname to avoid amavis bug
    if os.system('hostname -d') != 0:
        os.system('hostname yunohost.yunohost.org')

    # Add a temporary SSOwat rule to redirect SSO to admin page
    try:
        with open('/etc/ssowat/conf.json.persistent') as json_conf:
            ssowat_conf = json.loads(str(json_conf.read()))
    except IOError:
        ssowat_conf = {}

    if 'redirected_urls' not in ssowat_conf:
        ssowat_conf['redirected_urls'] = {}

    ssowat_conf['redirected_urls']['/'] = domain +'/yunohost/admin'

    with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
        json.dump(ssowat_conf, f, sort_keys=True, indent=4)

    os.system('chmod 644 /etc/ssowat/conf.json.persistent')

    # Create SSL CA
    ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
    command_list = [
        'echo "01" > %s/serial' % ssl_dir,
        'rm %s/index.txt'       % ssl_dir,
        'touch %s/index.txt'    % ssl_dir,
        'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir),
        'sed -i "s/yunohost.org/%s/g" %s/openssl.ca.cnf ' % (domain, ssl_dir),
        'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch' % (ssl_dir, ssl_dir, ssl_dir),
        'cp %s/ca/cacert.pem /etc/ssl/certs/ca-yunohost_crt.pem' % ssl_dir,
        'update-ca-certificates'
    ]

    for command in command_list:
        if os.system(command) != 0:
            raise MoulinetteError(errno.EPERM,
                                  m18n.n('yunohost_ca_creation_failed'))

    # Instantiate LDAP Authenticator
    auth = init_authenticator(('ldap', 'default'),
                              { 'uri': "ldap://localhost:389",
                                'base_dn': "dc=yunohost,dc=org",
                                'user_rdn': "cn=admin" })
    auth.authenticate('yunohost')

    # Initialize YunoHost LDAP base
    tools_ldapinit(auth)

    # Initialize backup system
    backup_init()

    # New domain config
    tools_maindomain(auth, old_domain='yunohost.org', new_domain=domain, dyndns=dyndns)

    # Generate SSOwat configuration file
    app_ssowatconf(auth)

    # Change LDAP admin password
    tools_adminpw(old_password='******', new_password=password)

    # Enable uPnP
    firewall_upnp(action=['enable'])
    try:
        firewall_reload()
    except MoulinetteError:
        firewall_upnp(action=['disable'])

    os.system('touch /etc/yunohost/installed')

    msignals.display(m18n.n('yunohost_configured'), 'success')
Exemplo n.º 22
0
def backup_restore(auth, name, hooks=[], ignore_hooks=False,
                   apps=[], ignore_apps=False, force=False):
    """
    Restore from a local backup archive

    Keyword argument:
        name -- Name of the local backup archive
        hooks -- List of restoration hooks names to execute
        ignore_hooks -- Do not execute backup hooks
        apps -- List of application names to restore
        ignore_apps -- Do not restore apps
        force -- Force restauration on an already installed system

    """
    # Validate what to restore
    if ignore_hooks and ignore_apps:
        raise MoulinetteError(errno.EINVAL,
            m18n.n('restore_action_required'))

    # Retrieve and open the archive
    info = backup_info(name)
    archive_file = info['path']
    try:
        tar = tarfile.open(archive_file, "r:gz")
    except:
        logger.debug("cannot open backup archive '%s'",
            archive_file, exc_info=1)
        raise MoulinetteError(errno.EIO, m18n.n('backup_archive_open_failed'))

    # Check temporary directory
    tmp_dir = "%s/tmp/%s" % (backup_path, name)
    if os.path.isdir(tmp_dir):
        logger.debug("temporary directory for restoration '%s' already exists",
            tmp_dir)
        os.system('rm -rf %s' % tmp_dir)

    # Check available disk space
    statvfs = os.statvfs(backup_path)
    free_space = statvfs.f_frsize * statvfs.f_bavail
    if free_space < info['size']:
        logger.debug("%dB left but %dB is needed", free_space, info['size'])
        raise MoulinetteError(
            errno.EIO, m18n.n('not_enough_disk_space', path=backup_path))

    def _clean_tmp_dir(retcode=0):
        ret = hook_callback('post_backup_restore', args=[tmp_dir, retcode])
        if not ret['failed']:
            filesystem.rm(tmp_dir, True, True)
        else:
            logger.warning(m18n.n('restore_cleaning_failed'))

    # Extract the tarball
    logger.info(m18n.n('backup_extracting_archive'))
    tar.extractall(tmp_dir)
    tar.close()

    # Retrieve backup info
    info_file = "%s/info.json" % tmp_dir
    try:
        with open(info_file, 'r') as f:
            info = json.load(f)
    except IOError:
        logger.debug("unable to load '%s'", info_file, exc_info=1)
        raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))
    else:
        logger.debug("restoring from backup '%s' created on %s", name,
            time.ctime(info['created_at']))

    # Initialize restauration summary result
    result = {
        'apps': [],
        'hooks': {},
    }

    # Check if YunoHost is installed
    if os.path.isfile('/etc/yunohost/installed'):
        logger.warning(m18n.n('yunohost_already_installed'))
        if not force:
            try:
                # Ask confirmation for restoring
                i = msignals.prompt(m18n.n('restore_confirm_yunohost_installed',
                                           answers='y/N'))
            except NotImplemented:
                pass
            else:
                if i == 'y' or i == 'Y':
                    force = True
            if not force:
                _clean_tmp_dir()
                raise MoulinetteError(errno.EEXIST, m18n.n('restore_failed'))
    else:
        # Retrieve the domain from the backup
        try:
            with open("%s/conf/ynh/current_host" % tmp_dir, 'r') as f:
                domain = f.readline().rstrip()
        except IOError:
            logger.debug("unable to retrieve current_host from the backup",
                         exc_info=1)
            raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))

        logger.debug("executing the post-install...")
        tools_postinstall(domain, 'yunohost', True)

    # Run system hooks
    if not ignore_hooks:
        # Filter hooks to execute
        hooks_list = set(info['hooks'].keys())
        _is_hook_in_backup = lambda h: True
        if hooks:
            def _is_hook_in_backup(h):
                if h in hooks_list:
                    return True
                logger.error(m18n.n('backup_archive_hook_not_exec', hook=h))
                return False
        else:
            hooks = hooks_list

        # Check hooks availibility
        hooks_filtered = set()
        for h in hooks:
            if not _is_hook_in_backup(h):
                continue
            try:
                hook_info('restore', h)
            except:
                tmp_hooks = glob('{:s}/hooks/restore/*-{:s}'.format(tmp_dir, h))
                if not tmp_hooks:
                    logger.exception(m18n.n('restore_hook_unavailable', hook=h))
                    continue
                # Add restoration hook from the backup to the system
                # FIXME: Refactor hook_add and use it instead
                restore_hook_folder = custom_hook_folder + 'restore'
                filesystem.mkdir(restore_hook_folder, 755, True)
                for f in tmp_hooks:
                    logger.debug("adding restoration hook '%s' to the system "
                        "from the backup archive '%s'", f, archive_file)
                    shutil.copy(f, restore_hook_folder)
            hooks_filtered.add(h)

        if hooks_filtered:
            logger.info(m18n.n('restore_running_hooks'))
            ret = hook_callback('restore', hooks_filtered, args=[tmp_dir])
            result['hooks'] = ret['succeed']

    # Add apps restore hook
    if not ignore_apps:
        # Filter applications to restore
        apps_list = set(info['apps'].keys())
        apps_filtered = set()
        if apps:
            for a in apps:
                if a not in apps_list:
                    logger.error(m18n.n('backup_archive_app_not_found', app=a))
                else:
                    apps_filtered.add(a)
        else:
            apps_filtered = apps_list

        for app_instance_name in apps_filtered:
            tmp_app_dir = '{:s}/apps/{:s}'.format(tmp_dir, app_instance_name)
            tmp_app_bkp_dir = tmp_app_dir + '/backup'

            # Check if the app is not already installed
            if _is_installed(app_instance_name):
                logger.error(m18n.n('restore_already_installed_app',
                        app=app_instance_name))
                continue

            # Check if the app has a restore script
            app_script = tmp_app_dir + '/settings/scripts/restore'
            if not os.path.isfile(app_script):
                logger.warning(m18n.n('unrestore_app', app=app_instance_name))
                continue

            tmp_script = '/tmp/restore_' + app_instance_name
            app_setting_path = '/etc/yunohost/apps/' + app_instance_name
            logger.info(m18n.n('restore_running_app_script', app=app_instance_name))
            try:
                # Copy app settings and set permissions
                shutil.copytree(tmp_app_dir + '/settings', app_setting_path)
                filesystem.chmod(app_setting_path, 0555, 0444, True)
                filesystem.chmod(app_setting_path + '/settings.yml', 0400)

                # Execute app restore script
                subprocess.call(['install', '-Dm555', app_script, tmp_script])

                # Prepare env. var. to pass to script
                env_dict = {}
                app_id, app_instance_nb = _parse_app_instance_name(app_instance_name)
                env_dict["YNH_APP_ID"] = app_id
                env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name
                env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
                env_dict["YNH_APP_BACKUP_DIR"] = tmp_app_bkp_dir

                hook_exec(tmp_script, args=[tmp_app_bkp_dir, app_instance_name],
                          raise_on_error=True, chdir=tmp_app_bkp_dir, env=env_dict)
            except:
                logger.exception(m18n.n('restore_app_failed', app=app_instance_name))
                # Cleaning app directory
                shutil.rmtree(app_setting_path, ignore_errors=True)
            else:
                result['apps'].append(app_instance_name)
            finally:
                filesystem.rm(tmp_script, force=True)

    # Check if something has been restored
    if not result['hooks'] and not result['apps']:
        _clean_tmp_dir(1)
        raise MoulinetteError(errno.EINVAL, m18n.n('restore_nothings_done'))
    if result['apps']:
        app_ssowatconf(auth)

    _clean_tmp_dir()
    logger.success(m18n.n('restore_complete'))

    return result
Exemplo n.º 23
0
def user_update(operation_logger,
                username,
                firstname=None,
                lastname=None,
                mail=None,
                change_password=None,
                add_mailforward=None,
                remove_mailforward=None,
                add_mailalias=None,
                remove_mailalias=None,
                mailbox_quota=None):
    """
    Update user informations

    Keyword argument:
        lastname
        mail
        firstname
        add_mailalias -- Mail aliases to add
        remove_mailforward -- Mailforward addresses to remove
        username -- Username of user to update
        add_mailforward -- Mailforward addresses to add
        change_password -- New password to set
        remove_mailalias -- Mail aliases to remove

    """
    from yunohost.domain import domain_list, _get_maindomain
    from yunohost.app import app_ssowatconf
    from yunohost.utils.password import assert_password_is_strong_enough
    from yunohost.utils.ldap import _get_ldap_interface
    from yunohost.hook import hook_callback

    domains = domain_list()['domains']

    # Populate user informations
    ldap = _get_ldap_interface()
    attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop']
    result = ldap.search(base='ou=users,dc=yunohost,dc=org',
                         filter='uid=' + username,
                         attrs=attrs_to_fetch)
    if not result:
        raise YunohostError('user_unknown', user=username)
    user = result[0]
    env_dict = {"YNH_USER_USERNAME": username}

    # Get modifications from arguments
    new_attr_dict = {}
    if firstname:
        new_attr_dict['givenName'] = [firstname]  # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict['displayName'] = [
            firstname + ' ' + user['sn'][0]
        ]
        env_dict["YNH_USER_FIRSTNAME"] = firstname

    if lastname:
        new_attr_dict['sn'] = [lastname]  # TODO: Validate
        new_attr_dict['cn'] = new_attr_dict['displayName'] = [
            user['givenName'][0] + ' ' + lastname
        ]
        env_dict["YNH_USER_LASTNAME"] = lastname

    if lastname and firstname:
        new_attr_dict['cn'] = new_attr_dict['displayName'] = [
            firstname + ' ' + lastname
        ]

    # change_password is None if user_update is not called to change the password
    if change_password is not None:
        # when in the cli interface if the option to change the password is called
        # without a specified value, change_password will be set to the const 0.
        # In this case we prompt for the new password.
        if msettings.get('interface') == 'cli' and not change_password:
            change_password = msignals.prompt(m18n.n("ask_password"), True,
                                              True)
        # Ensure sufficiently complex password
        assert_password_is_strong_enough("user", change_password)

        new_attr_dict['userPassword'] = [_hash_user_password(change_password)]
        env_dict["YNH_USER_PASSWORD"] = change_password

    if mail:
        main_domain = _get_maindomain()
        aliases = [
            'root@' + main_domain,
            'admin@' + main_domain,
            'webmaster@' + main_domain,
            'postmaster@' + main_domain,
        ]
        try:
            ldap.validate_uniqueness({'mail': mail})
        except Exception as e:
            raise YunohostError('user_update_failed', user=username, error=e)
        if mail[mail.find('@') + 1:] not in domains:
            raise YunohostError('mail_domain_unknown',
                                domain=mail[mail.find('@') + 1:])
        if mail in aliases:
            raise YunohostError('mail_unavailable')

        del user['mail'][0]
        new_attr_dict['mail'] = [mail] + user['mail']

    if add_mailalias:
        if not isinstance(add_mailalias, list):
            add_mailalias = [add_mailalias]
        for mail in add_mailalias:
            try:
                ldap.validate_uniqueness({'mail': mail})
            except Exception as e:
                raise YunohostError('user_update_failed',
                                    user=username,
                                    error=e)
            if mail[mail.find('@') + 1:] not in domains:
                raise YunohostError('mail_domain_unknown',
                                    domain=mail[mail.find('@') + 1:])
            user['mail'].append(mail)
        new_attr_dict['mail'] = user['mail']

    if remove_mailalias:
        if not isinstance(remove_mailalias, list):
            remove_mailalias = [remove_mailalias]
        for mail in remove_mailalias:
            if len(user['mail']) > 1 and mail in user['mail'][1:]:
                user['mail'].remove(mail)
            else:
                raise YunohostError('mail_alias_remove_failed', mail=mail)
        new_attr_dict['mail'] = user['mail']

    if 'mail' in new_attr_dict:
        env_dict["YNH_USER_MAILS"] = ','.join(new_attr_dict['mail'])

    if add_mailforward:
        if not isinstance(add_mailforward, list):
            add_mailforward = [add_mailforward]
        for mail in add_mailforward:
            if mail in user['maildrop'][1:]:
                continue
            user['maildrop'].append(mail)
        new_attr_dict['maildrop'] = user['maildrop']

    if remove_mailforward:
        if not isinstance(remove_mailforward, list):
            remove_mailforward = [remove_mailforward]
        for mail in remove_mailforward:
            if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]:
                user['maildrop'].remove(mail)
            else:
                raise YunohostError('mail_forward_remove_failed', mail=mail)
        new_attr_dict['maildrop'] = user['maildrop']

    if 'maildrop' in new_attr_dict:
        env_dict["YNH_USER_MAILFORWARDS"] = ','.join(new_attr_dict['maildrop'])

    if mailbox_quota is not None:
        new_attr_dict['mailuserquota'] = [mailbox_quota]
        env_dict["YNH_USER_MAILQUOTA"] = mailbox_quota

    operation_logger.start()

    try:
        ldap.update('uid=%s,ou=users' % username, new_attr_dict)
    except Exception as e:
        raise YunohostError('user_update_failed', user=username, error=e)

    # Trigger post_user_update hooks
    hook_callback('post_user_update', env=env_dict)

    logger.success(m18n.n('user_updated'))
    app_ssowatconf()
    return user_info(username)
Exemplo n.º 24
0
def user_create(auth, username, firstname, lastname, mail, password,
        mailbox_quota=0):
    """
    Create user

    Keyword argument:
        firstname
        lastname
        username -- Must be unique
        mail -- Main mail address must be unique
        password
        mailbox_quota -- Mailbox size quota

    """
    import pwd
    from yunohost.domain import domain_list
    from yunohost.hook import hook_callback
    from yunohost.app import app_ssowatconf

    # Validate uniqueness of username and mail in LDAP
    auth.validate_uniqueness({
        'uid'       : username,
        'mail'      : mail
    })

    # Validate uniqueness of username in system users
    try:
        pwd.getpwnam(username)
    except KeyError:
        pass
    else:
        raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists'))

    # Check that the mail domain exists
    if mail[mail.find('@')+1:] not in domain_list(auth)['domains']:
        raise MoulinetteError(errno.EINVAL,
                              m18n.n('mail_domain_unknown',
                                     domain=mail[mail.find('@')+1:]))

    # Get random UID/GID
    uid_check = gid_check = 0
    while uid_check == 0 and gid_check == 0:
        uid = str(random.randint(200, 99999))
        uid_check = os.system("getent passwd %s" % uid)
        gid_check = os.system("getent group %s" % uid)

    # Adapt values for LDAP
    fullname = '%s %s' % (firstname, lastname)
    rdn = 'uid=%s,ou=users' % username
    char_set = string.ascii_uppercase + string.digits
    salt = ''.join(random.sample(char_set,8))
    salt = '$1$' + salt + '$'
    user_pwd = '{CRYPT}' + crypt.crypt(str(password), salt)
    attr_dict = {
        'objectClass'   : ['mailAccount', 'inetOrgPerson', 'posixAccount'],
        'givenName'     : firstname,
        'sn'            : lastname,
        'displayName'   : fullname,
        'cn'            : fullname,
        'uid'           : username,
        'mail'          : mail,
        'maildrop'      : username,
        'mailuserquota' : mailbox_quota,
        'userPassword'  : user_pwd,
        'gidNumber'     : uid,
        'uidNumber'     : uid,
        'homeDirectory' : '/home/' + username,
        'loginShell'    : '/bin/false'
    }

    # If it is the first user, add some aliases
    if not auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'):
        with open('/etc/yunohost/current_host') as f:
            main_domain = f.readline().rstrip()
        aliases = [
            'root@'+ main_domain,
            'admin@'+ main_domain,
            'webmaster@'+ main_domain,
            'postmaster@'+ main_domain,
        ]
        attr_dict['mail'] = [ attr_dict['mail'] ] + aliases

        # If exists, remove the redirection from the SSO
        try:
            with open('/etc/ssowat/conf.json.persistent') as json_conf:
                ssowat_conf = json.loads(str(json_conf.read()))

            if 'redirected_urls' in ssowat_conf and '/' in ssowat_conf['redirected_urls']:
                del ssowat_conf['redirected_urls']['/']

            with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
                json.dump(ssowat_conf, f, sort_keys=True, indent=4)

        except IOError: pass


    if auth.add(rdn, attr_dict):
        # Invalidate passwd to take user creation into account
        subprocess.call(['nscd', '-i', 'passwd'])

        # Update SFTP user group
        memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid']
        memberlist.append(username)
        if auth.update('cn=sftpusers,ou=groups', { 'memberUid': memberlist }):
            try:
                # Attempt to create user home folder
                subprocess.check_call(
                    ['su', '-', username, '-c', "''"])
            except subprocess.CalledProcessError:
                if not os.path.isdir('/home/{0}'.format(username)):
                    logger.warning(m18n.n('user_home_creation_failed'),
                                   exc_info=1)
            app_ssowatconf(auth)
            #TODO: Send a welcome mail to user
            logger.success(m18n.n('user_created'))
            hook_callback('post_user_create',
                          args=[username, mail, password, firstname, lastname])

            return { 'fullname' : fullname, 'username' : username, 'mail' : mail }

    raise MoulinetteError(169, m18n.n('user_creation_failed'))
Exemplo n.º 25
0
def backup_restore(auth,
                   name,
                   hooks=[],
                   ignore_hooks=False,
                   apps=[],
                   ignore_apps=False,
                   force=False):
    """
    Restore from a local backup archive

    Keyword argument:
        name -- Name of the local backup archive
        hooks -- List of restoration hooks names to execute
        ignore_hooks -- Do not execute backup hooks
        apps -- List of application names to restore
        ignore_apps -- Do not restore apps
        force -- Force restauration on an already installed system

    """
    # Validate what to restore
    if ignore_hooks and ignore_apps:
        raise MoulinetteError(errno.EINVAL, m18n.n('restore_action_required'))

    # Retrieve and open the archive
    info = backup_info(name)
    archive_file = info['path']
    try:
        tar = tarfile.open(archive_file, "r:gz")
    except:
        logger.debug("cannot open backup archive '%s'",
                     archive_file,
                     exc_info=1)
        raise MoulinetteError(errno.EIO, m18n.n('backup_archive_open_failed'))

    # Check temporary directory
    tmp_dir = "%s/tmp/%s" % (backup_path, name)
    if os.path.isdir(tmp_dir):
        logger.debug("temporary directory for restoration '%s' already exists",
                     tmp_dir)
        os.system('rm -rf %s' % tmp_dir)

    # Check available disk space
    statvfs = os.statvfs(backup_path)
    free_space = statvfs.f_frsize * statvfs.f_bavail
    if free_space < info['size']:
        logger.debug("%dB left but %dB is needed", free_space, info['size'])
        raise MoulinetteError(
            errno.EIO, m18n.n('not_enough_disk_space', path=backup_path))

    def _clean_tmp_dir(retcode=0):
        ret = hook_callback('post_backup_restore', args=[tmp_dir, retcode])
        if not ret['failed']:
            filesystem.rm(tmp_dir, True, True)
        else:
            logger.warning(m18n.n('restore_cleaning_failed'))

    # Extract the tarball
    logger.info(m18n.n('backup_extracting_archive'))
    tar.extractall(tmp_dir)
    tar.close()

    # Retrieve backup info
    info_file = "%s/info.json" % tmp_dir
    try:
        with open(info_file, 'r') as f:
            info = json.load(f)
    except IOError:
        logger.debug("unable to load '%s'", info_file, exc_info=1)
        raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))
    else:
        logger.debug("restoring from backup '%s' created on %s", name,
                     time.ctime(info['created_at']))

    # Initialize restauration summary result
    result = {
        'apps': [],
        'hooks': {},
    }

    # Check if YunoHost is installed
    if os.path.isfile('/etc/yunohost/installed'):
        logger.warning(m18n.n('yunohost_already_installed'))
        if not force:
            try:
                # Ask confirmation for restoring
                i = msignals.prompt(
                    m18n.n('restore_confirm_yunohost_installed',
                           answers='y/N'))
            except NotImplemented:
                pass
            else:
                if i == 'y' or i == 'Y':
                    force = True
            if not force:
                _clean_tmp_dir()
                raise MoulinetteError(errno.EEXIST, m18n.n('restore_failed'))
    else:
        # Retrieve the domain from the backup
        try:
            with open("%s/conf/ynh/current_host" % tmp_dir, 'r') as f:
                domain = f.readline().rstrip()
        except IOError:
            logger.debug("unable to retrieve current_host from the backup",
                         exc_info=1)
            raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))

        logger.debug("executing the post-install...")
        tools_postinstall(domain, 'yunohost', True)

    # Run system hooks
    if not ignore_hooks:
        # Filter hooks to execute
        hooks_list = set(info['hooks'].keys())
        _is_hook_in_backup = lambda h: True
        if hooks:

            def _is_hook_in_backup(h):
                if h in hooks_list:
                    return True
                logger.error(m18n.n('backup_archive_hook_not_exec', hook=h))
                return False
        else:
            hooks = hooks_list

        # Check hooks availibility
        hooks_filtered = set()
        for h in hooks:
            if not _is_hook_in_backup(h):
                continue
            try:
                hook_info('restore', h)
            except:
                tmp_hooks = glob('{:s}/hooks/restore/*-{:s}'.format(
                    tmp_dir, h))
                if not tmp_hooks:
                    logger.exception(m18n.n('restore_hook_unavailable',
                                            hook=h))
                    continue
                # Add restoration hook from the backup to the system
                # FIXME: Refactor hook_add and use it instead
                restore_hook_folder = custom_hook_folder + 'restore'
                filesystem.mkdir(restore_hook_folder, 755, True)
                for f in tmp_hooks:
                    logger.debug(
                        "adding restoration hook '%s' to the system "
                        "from the backup archive '%s'", f, archive_file)
                    shutil.copy(f, restore_hook_folder)
            hooks_filtered.add(h)

        if hooks_filtered:
            logger.info(m18n.n('restore_running_hooks'))
            ret = hook_callback('restore', hooks_filtered, args=[tmp_dir])
            result['hooks'] = ret['succeed']

    # Add apps restore hook
    if not ignore_apps:
        # Filter applications to restore
        apps_list = set(info['apps'].keys())
        apps_filtered = set()
        if apps:
            for a in apps:
                if a not in apps_list:
                    logger.error(m18n.n('backup_archive_app_not_found', app=a))
                else:
                    apps_filtered.add(a)
        else:
            apps_filtered = apps_list

        for app_instance_name in apps_filtered:
            tmp_app_dir = '{:s}/apps/{:s}'.format(tmp_dir, app_instance_name)
            tmp_app_bkp_dir = tmp_app_dir + '/backup'

            # Parse app instance name and id
            # TODO: Use app_id to check if app is installed?
            app_id, app_instance_nb = _parse_app_instance_name(
                app_instance_name)

            # Check if the app is not already installed
            if _is_installed(app_instance_name):
                logger.error(
                    m18n.n('restore_already_installed_app',
                           app=app_instance_name))
                continue

            # Check if the app has a restore script
            app_script = tmp_app_dir + '/settings/scripts/restore'
            if not os.path.isfile(app_script):
                logger.warning(m18n.n('unrestore_app', app=app_instance_name))
                continue

            tmp_script = '/tmp/restore_' + app_instance_name
            app_setting_path = '/etc/yunohost/apps/' + app_instance_name
            logger.info(
                m18n.n('restore_running_app_script', app=app_instance_name))
            try:
                # Copy app settings and set permissions
                # TODO: Copy app hooks too
                shutil.copytree(tmp_app_dir + '/settings', app_setting_path)
                filesystem.chmod(app_setting_path, 0555, 0444, True)
                filesystem.chmod(app_setting_path + '/settings.yml', 0400)

                # Copy restore script in a tmp file
                subprocess.call(['install', '-Dm555', app_script, tmp_script])

                # Prepare env. var. to pass to script
                env_dict = {}
                env_dict["YNH_APP_ID"] = app_id
                env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name
                env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
                env_dict["YNH_APP_BACKUP_DIR"] = tmp_app_bkp_dir

                # Execute app restore script
                hook_exec(tmp_script,
                          args=[tmp_app_bkp_dir, app_instance_name],
                          raise_on_error=True,
                          chdir=tmp_app_bkp_dir,
                          env=env_dict)
            except:
                logger.exception(
                    m18n.n('restore_app_failed', app=app_instance_name))

                # Copy remove script in a tmp file
                filesystem.rm(tmp_script, force=True)
                app_script = tmp_app_dir + '/settings/scripts/remove'
                tmp_script = '/tmp/remove_' + app_instance_name
                subprocess.call(['install', '-Dm555', app_script, tmp_script])

                # Setup environment for remove script
                env_dict_remove = {}
                env_dict_remove["YNH_APP_ID"] = app_id
                env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name
                env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(
                    app_instance_nb)

                # Execute remove script
                # TODO: call app_remove instead
                if hook_exec(tmp_script,
                             args=[app_instance_name],
                             env=env_dict_remove) != 0:
                    logger.warning(
                        m18n.n('app_not_properly_removed',
                               app=app_instance_name))

                # Cleaning app directory
                shutil.rmtree(app_setting_path, ignore_errors=True)
            else:
                result['apps'].append(app_instance_name)
            finally:
                filesystem.rm(tmp_script, force=True)

    # Check if something has been restored
    if not result['hooks'] and not result['apps']:
        _clean_tmp_dir(1)
        raise MoulinetteError(errno.EINVAL, m18n.n('restore_nothings_done'))
    if result['apps']:
        app_ssowatconf(auth)

    _clean_tmp_dir()
    logger.success(m18n.n('restore_complete'))

    return result
Exemplo n.º 26
0
def tools_postinstall(domain, password, ignore_dyndns=False):
    """
    YunoHost post-install

    Keyword argument:
        domain -- YunoHost main domain
        ignore_dyndns -- Do not subscribe domain to a DynDNS service
        password -- YunoHost admin password

    """
    from moulinette.core import init_authenticator

    from yunohost.app import app_ssowatconf
    from yunohost.firewall import firewall_upnp, firewall_reload

    dyndns = not ignore_dyndns

    try:
        with open('/etc/yunohost/installed') as f: pass
    except IOError:
        msignals.display(m18n.n('yunohost_installing'))
    else:
        raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed'))

    if len(domain.split('.')) >= 3 and not ignore_dyndns:
        try:
            r = requests.get('https://dyndns.yunohost.org/domains')
        except ConnectionError:
            pass
        else:
            dyndomains = json.loads(r.text)
            dyndomain  = '.'.join(domain.split('.')[1:])
            if dyndomain in dyndomains:
                if requests.get('https://dyndns.yunohost.org/test/%s' % domain).status_code == 200:
                    dyndns=True
                else:
                    raise MoulinetteError(errno.EEXIST,
                                      m18n.n('dyndns_unavailable'))

    # Create required folders
    folders_to_create = [
        '/etc/yunohost/apps',
        '/etc/yunohost/certs',
        '/var/cache/yunohost/repo',
        '/home/yunohost.backup',
        '/home/yunohost.app'
    ]

    for folder in folders_to_create:
        try: os.listdir(folder)
        except OSError: os.makedirs(folder)

    # Change folders permissions
    os.system('chmod 755 /home/yunohost.app')

    # Set hostname to avoid amavis bug
    if os.system('hostname -d') != 0:
        os.system('hostname yunohost.yunohost.org')

    # Add a temporary SSOwat rule to redirect SSO to admin page
    try:
        with open('/etc/ssowat/conf.json.persistent') as json_conf:
            ssowat_conf = json.loads(str(json_conf.read()))
    except IOError:
        ssowat_conf = {}

    if 'redirected_urls' not in ssowat_conf:
        ssowat_conf['redirected_urls'] = {}

    ssowat_conf['redirected_urls']['/'] = domain +'/yunohost/admin'

    with open('/etc/ssowat/conf.json.persistent', 'w+') as f:
        json.dump(ssowat_conf, f, sort_keys=True, indent=4)

    os.system('chmod 644 /etc/ssowat/conf.json.persistent')

    # Create SSL CA
    ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
    command_list = [
        'echo "01" > %s/serial' % ssl_dir,
        'rm %s/index.txt'       % ssl_dir,
        'touch %s/index.txt'    % ssl_dir,
        'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir),
        'sed -i "s/yunohost.org/%s/g" %s/openssl.ca.cnf ' % (domain, ssl_dir),
        'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch' % (ssl_dir, ssl_dir, ssl_dir),
        'cp %s/ca/cacert.pem /etc/ssl/certs/ca-yunohost_crt.pem' % ssl_dir,
        'update-ca-certificates'
    ]

    for command in command_list:
        if os.system(command) != 0:
            raise MoulinetteError(errno.EPERM,
                                  m18n.n('yunohost_ca_creation_failed'))

    # Instantiate LDAP Authenticator
    auth = init_authenticator(('ldap', 'default'),
                              { 'uri': "ldap://localhost:389",
                                'base_dn': "dc=yunohost,dc=org",
                                'user_rdn': "cn=admin" })
    auth.authenticate('yunohost')

    # Initialize YunoHost LDAP base
    tools_ldapinit(auth)

    # New domain config
    tools_maindomain(auth, old_domain='yunohost.org', new_domain=domain, dyndns=dyndns)

    # Generate SSOwat configuration file
    app_ssowatconf(auth)

    # Change LDAP admin password
    tools_adminpw(old_password='******', new_password=password)

    # Enable uPnP
    firewall_upnp(action=['enable'])
    try:
        firewall_reload()
    except MoulinetteError:
        firewall_upnp(action=['disable'])

    # Enable iptables at boot time
    os.system('update-rc.d yunohost-firewall defaults')

    os.system('touch /etc/yunohost/installed')

    msignals.display(m18n.n('yunohost_configured'), 'success')