Commit 1f5c3f26 authored by jan.koester's avatar jan.koester
Browse files

test

parent 5b8d4e7f
Loading
Loading
Loading
Loading
+23 −9
Changes for modules/users/sources.cpp: 23 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -297,6 +297,11 @@ const Field Fields[] = {
    {"insecure", Type::Bool, 0, 0},
    {"ca_file", Type::Str, 0, 0},
    {"bind_dn", Type::Str, 0, 0},
    {"auth", Type::Str, 0, 0},
    {"kerberos_principal", Type::Str, 0, 0},
    {"kerberos_realm", Type::Str, 0, 0},
    {"keytab", Type::Str, 0, 0},
    {"kdcs", Type::List, 0, 0},
    {"id_mapping", Type::Str, 0, 0},
    {"id_base", Type::Int, 1000, 4294967294L},
    {"id_range", Type::Int, 1, 4294967294L},
@@ -395,6 +400,8 @@ json_object *buildAd(json_object *config, const std::string &passwordFile, const
                    return error(tr("tls: ldaps, starttls or none", "tls: ldaps, starttls oder none"));
                tlsNone = s == "none";
            }
            if (!strcmp(f.key, "auth") && s != "simple" && s != "kerberos")
                return error(tr("auth: simple or kerberos", "auth: simple oder kerberos"));
            if (!strcmp(f.key, "id_mapping") && s != "rid" && s != "rfc2307" && s != "guid")
                return error(tr("id_mapping: rid, rfc2307 or guid", "id_mapping: rid, rfc2307 oder guid"));
            if (!strcmp(f.key, "bind_dn")) bindDn = s;
@@ -409,7 +416,7 @@ json_object *buildAd(json_object *config, const std::string &passwordFile, const
                std::string item = s ? s : "";
                if (item.empty()) continue;
                if (!validText(item) || item.find(' ') != std::string::npos)
                    return error(tr("invalid server %1", "ungültiger Server %1", {item}));
                    return error(tr("invalid host %1 in %2", "ungültiger Host %1 in %2", {item, f.key}));
                items += (items.empty() ? "" : ", ") + quoted(item);
            }
            if (!items.empty()) yaml += std::string("  ") + f.key + ": [" + items + "]\n";
@@ -437,14 +444,21 @@ json_object *buildAd(json_object *config, const std::string &passwordFile, const
    }
    std::string domain = webctl::str(config, "domain");
    if (domain.empty()) return error(tr("the domain is missing", "die Domäne fehlt"));
    if (tlsNone && !insecure)
        return error(tr("without TLS the password goes over the network in the clear: confirm it with \"insecure\"",
                        "ohne TLS geht das Passwort im Klartext übers Netz: mit \"insecure\" bestätigen"));
    if (!bindDn.empty()) {
        if (!passwordFile.empty()) yaml += "  bind_password_file: " + quoted(passwordFile) + "\n";
        else if (!inlinePassword.empty()) yaml += "  bind_password: " + quoted(inlinePassword) + "\n";
        else return error(tr("the password of the bind account is missing", "das Passwort des Bind-Kontos fehlt"));
    }
    // Kerberos seals the connection itself; a simple bind needs TLS
    bool kerberos = webctl::str(config, "auth") == "kerberos";
    if (tlsNone && !insecure && !kerberos)
        return error(tr("without TLS the password goes over the network in the clear: use Kerberos, or confirm it with \"insecure\"",
                        "ohne TLS geht das Passwort im Klartext übers Netz: Kerberos verwenden oder mit \"insecure\" bestätigen"));
    if (kerberos && webctl::str(config, "kerberos_principal").empty() && bindDn.find('@') == std::string::npos)
        return error(tr("Kerberos needs the account: a principal, or the bind account as user@domain",
                        "Kerberos braucht das Konto: einen Principal oder das Bind-Konto als benutzer@domäne"));
    bool needPassword = kerberos ? webctl::str(config, "keytab").empty() : !bindDn.empty();
    if (!passwordFile.empty() && (needPassword || kerberos || !bindDn.empty()))
        yaml += "  bind_password_file: " + quoted(passwordFile) + "\n";
    else if (!inlinePassword.empty() && (needPassword || kerberos || !bindDn.empty()))
        yaml += "  bind_password: " + quoted(inlinePassword) + "\n";
    else if (needPassword)
        return error(tr("the password of the account is missing", "das Passwort des Kontos fehlt"));
    return nullptr;
}

+25 −3
Changes for www/modules/users.js: 25 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -94,6 +94,15 @@
    'Sources saved.': 'Quellen gespeichert.', 'Remove source {0}': 'Quelle {0} entfernen',
    'The libc no longer asks {0} for users and groups.': 'Die libc fragt {0} nicht mehr nach Benutzern und Gruppen.',
    'invalid option line: {0}': 'ungültige Optionszeile: {0}',
    'Login': 'Anmeldung', 'Simple bind (password over LDAPS/StartTLS)': 'Einfacher Bind (Passwort über LDAPS/StartTLS)',
    'Kerberos (SASL GSSAPI, sealed by Kerberos)': 'Kerberos (SASL GSSAPI, von Kerberos verschlüsselt)',
    'Kerberos principal (empty: the user part of the account)': 'Kerberos-Principal (leer: Benutzerteil des Kontos)',
    'Keytab (instead of the password, optional)': 'Keytab (statt Passwort, optional)',
    'KDCs (space separated, empty: the domain controllers)': 'KDCs (durch Leerzeichen getrennt, leer: die Domänencontroller)',
    'With Kerberos the connection is protected without TLS (encryption "none" is fine) and needs no DC certificate; the clock must be within 5 minutes of the DCs\' and the domain controllers must be given by DNS name.':
      'Mit Kerberos ist die Verbindung ohne TLS geschützt (Verschlüsselung „keine“ genügt) und braucht kein DC-Zertifikat; die Uhr darf höchstens 5 Minuten von der der DCs abweichen, und die Domänencontroller müssen als DNS-Namen angegeben sein.',
    'Kerberos': 'Kerberos', 'simple bind': 'einfacher Bind',
    'none (sealed by Kerberos)': 'keine (von Kerberos verschlüsselt)', 'none (port 389; for Kerberos)': 'keine (Port 389; für Kerberos)',
    'Add': 'Hinzufügen', 'error': 'Fehler',
  }});

@@ -393,7 +402,8 @@
    const c = a.config, row = (label, value) => h('tr', {}, h('th', {}, label), h('td', {}, value));
    const svc = a.service || {};
    const inUse = a.listed && svc.enabled === 'enabled';
    const tls = {ldaps: 'LDAPS', starttls: 'StartTLS', none: t('none (password in the clear!)')}[c.tls || 'ldaps'];
    const tls = {ldaps: 'LDAPS', starttls: 'StartTLS',
                 none: c.auth === 'kerberos' ? t('none (sealed by Kerberos)') : t('none (password in the clear!)')}[c.tls || 'ldaps'];
    const mapping = {rid: 'RID', rfc2307: 'RFC 2307', guid: 'GUID'}[c.id_mapping || 'rid'];
    const offline = c.offline_ttl === undefined ? 259200 : Number(c.offline_ttl);
    return h('div', {class: 'table-wrap'}, h('table', {class: 'facts'}, h('tbody', {},
@@ -404,6 +414,7 @@
      row(t('Domain'), h('strong', {}, c.domain || '')),
      row(t('Domain controllers'), h('span', {class: 'mono'}, (c.servers && c.servers.length ? c.servers : [c.domain]).join(' '))),
      row(t('Encryption'), tls),
      row(t('Login'), c.auth === 'kerberos' ? t('Kerberos') + (c.kerberos_principal ? ' (' + c.kerberos_principal + ')' : '') : t('simple bind')),
      row(t('IDs'), mapping + (!c.id_mapping || c.id_mapping === 'rid' ? ' (' + (c.id_base || 200000) + ' + RID)' : '')),
      row(t('Offline cache'), offline > 0 ? t('up to {0} h old', Math.round(offline / 3600)) : t('off')))));
  }
@@ -413,6 +424,8 @@
    const num = v => v === '' || v === undefined ? '' : Number(v);
    return {
      domain: d.domain.trim(), base: d.base.trim(), servers: d.servers.split(/[\s,]+/).filter(Boolean),
      auth: d.auth, kerberos_principal: d.kerberos_principal.trim(), keytab: d.keytab.trim(),
      kdcs: d.kdcs.split(/[\s,]+/).filter(Boolean),
      tls: d.tls, port: num(d.port), insecure: !!d.insecure, ca_file: d.ca_file.trim(), bind_dn: d.bind_dn.trim(),
      id_mapping: d.id_mapping, id_base: num(d.id_base), id_range: num(d.id_range),
      home: d.home.trim(), shell: d.shell.trim(), lowercase_names: !!d.lowercase_names, enumerate: !!d.enumerate,
@@ -467,17 +480,26 @@
          h('input', {name: 'servers', value: (c.servers || []).join(' '), placeholder: 'dc1.example.com dc2.example.com'})),
        h('div', {class: 'row'},
          h('label', {}, t('Encryption'), h('select', {name: 'tls'},
            [['ldaps', t('LDAPS (port 636)')], ['starttls', t('StartTLS (port 389)')], ['none', t('none (password in the clear!)')]]
            [['ldaps', t('LDAPS (port 636)')], ['starttls', t('StartTLS (port 389)')], ['none', t('none (port 389; for Kerberos)')]]
              .map(([k, l]) => h('option', {value: k, selected: v('tls', 'ldaps') === k}, l)))),
          number('port', t('Port (empty: default)'), '', 1, 65535)),
        check('insecure', t('Send the password unencrypted anyway'), false),
        input('ca_file', t('CA certificate (PEM, empty: system bundle)'), {placeholder: '/etc/adconnect/ca.pem'}),
        h('h3', {}, t('Bind account')),
        h('h3', {}, t('Login')),
        h('label', {}, t('Login'), h('select', {name: 'auth'},
          [['simple', t('Simple bind (password over LDAPS/StartTLS)')], ['kerberos', t('Kerberos (SASL GSSAPI, sealed by Kerberos)')]]
            .map(([k, l]) => h('option', {value: k, selected: v('auth', 'simple') === k}, l)))),
        h('p', {class: 'hint'}, t('With Kerberos the connection is protected without TLS (encryption "none" is fine) and needs no DC certificate; the clock must be within 5 minutes of the DCs\' and the domain controllers must be given by DNS name.')),
        h('div', {class: 'row'},
          input('bind_dn', t('Account (user@domain or DN)'), {placeholder: 'svc-fenriros@example.com', autocomplete: 'off'}),
          h('label', {}, t('Password'), h('input', {name: 'password', type: 'password', autocomplete: 'new-password',
                                                     placeholder: a.passwordSet ? t('unchanged') : ''}))),
        h('p', {class: 'hint'}, t('Stored root-only in /etc/adconnect/bind.secret.')),
        h('div', {class: 'row'},
          input('kerberos_principal', t('Kerberos principal (empty: the user part of the account)'), {placeholder: 'svc-fenriros', autocomplete: 'off'}),
          input('keytab', t('Keytab (instead of the password, optional)'), {placeholder: '/etc/adconnect/svc.keytab'})),
        h('label', {}, t('KDCs (space separated, empty: the domain controllers)'),
          h('input', {name: 'kdcs', value: (c.kdcs || []).join(' '), placeholder: 'dc1.example.com'})),
        h('h3', {}, t('ID mapping')),
        h('label', {}, t('ID mapping'), h('select', {name: 'id_mapping'},
          [['rid', t('id_base + RID of the SID (classic ids)')], ['rfc2307', t('uidNumber / gidNumber (RFC 2307)')],