Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37853765
en ru br
Репозитории ALT

Группа :: Система/Основа
Пакет: cockpit

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: cockpit-280.1-alt.patch
Скачать


 pkg/apps/manifest.json                  |  3 +-
 pkg/base1/test-permissions.js           |  2 +-
 pkg/lib/cockpit-components-password.jsx | 63 ++++++++++++++++++++++++++++++---
 pkg/storaged/manifest.json              |  1 +
 pkg/users/account-create-dialog.js      |  7 ++--
 pkg/users/account-details.js            |  2 +-
 pkg/users/account-logs-panel.jsx        | 18 ++++++----
 pkg/users/accounts-list.js              |  2 +-
 pkg/users/expiration-dialogs.js         |  2 +-
 pkg/users/index.html                    |  1 +
 pkg/users/manifest.json                 |  6 ++++
 pkg/users/password-dialogs.js           | 17 +++++----
 src/bridge/test-connect.c               | 10 +++---
 src/bridge/test-stream.c                | 10 +++---
 src/common/cockpitloopback.c            | 23 ++++++++++--
 15 files changed, 128 insertions(+), 39 deletions(-)
diff --git a/pkg/apps/manifest.json b/pkg/apps/manifest.json
index 0679c1b3b..e09a2d587 100644
--- a/pkg/apps/manifest.json
+++ b/pkg/apps/manifest.json
@@ -17,7 +17,8 @@
             "debian": ["appstream"], "ubuntu": ["appstream"]
         },
         "appstream_data_packages": {
-            "fedora": ["appstream-data"], "rhel": ["appstream-data"]
+            "fedora": ["appstream-data"], "rhel": ["appstream-data"],
+            "altlinux": ["appstream-data"]
         }
     }
 }
diff --git a/pkg/base1/test-permissions.js b/pkg/base1/test-permissions.js
index 956395858..123943fad 100644
--- a/pkg/base1/test-permissions.js
+++ b/pkg/base1/test-permissions.js
@@ -9,7 +9,7 @@ const root_user = {
 
 const priv_user = {
     name: "user",
-    id: 1000,
+    id: 500,
     groups: ["user", "agroup"]
 };
 
diff --git a/pkg/lib/cockpit-components-password.jsx b/pkg/lib/cockpit-components-password.jsx
index 08cfc51b6..3e7f8f152 100644
--- a/pkg/lib/cockpit-components-password.jsx
+++ b/pkg/lib/cockpit-components-password.jsx
@@ -17,6 +17,7 @@
  * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
  */
 import cockpit from 'cockpit';
+import { read_os_release } from "os-release.js";
 import React, { useState } from 'react';
 import { FormGroup, Popover, Progress, ProgressSize, ProgressMeasureLocation, TextInput } from '@patternfly/react-core';
 import { HelpIcon } from '@patternfly/react-icons';
@@ -25,10 +26,34 @@ import './cockpit-components-password.scss';
 
 const _ = cockpit.gettext;
 
-export function password_quality(password, force) {
+export function password_quality_proxy(new_password, old_password, user, force) {
+    function get_config(name, distro_id, def) {
+        if (cockpit.manifests.users && cockpit.manifests.users.config) {
+            let val = cockpit.manifests.users.config[name];
+            if (typeof val === 'object' && val !== null)
+                val = val[distro_id];
+            return val !== undefined ? val : def;
+        } else {
+            return def;
+        }
+    }
+
+    const password_quality_libs = {
+        libpasswdqc: pw_libpasswdqc,
+        libpwquality: pw_libpwquality,
+    };
+
+    return read_os_release().then(os_release => {
+        const quality_lib = get_config('password_quality_lib', os_release.ID, 'libpwquality');
+        const quality_function = password_quality_libs[quality_lib];
+        return quality_function(new_password, old_password, user, force);
+    });
+}
+
+function pw_libpwquality(new_password, old_password, user, force) {
     return new Promise((resolve, reject) => {
         cockpit.spawn('/usr/bin/pwscore', { err: "message" })
-                .input(password)
+                .input(new_password)
                 .done(function(content) {
                     const quality = parseInt(content, 10);
                     if (quality === 0)
@@ -45,12 +70,42 @@ export function password_quality(password, force) {
     });
 }
 
+function pw_libpasswdqc(new_password, old_password, user, force) {
+    return new Promise((resolve, reject) => {
+        if (!user || !new_password)
+            reject(new Error(_("Username or password is empty")));
+
+        /* pwqcheck doesn't accept nonexistent users */
+        cockpit.spawn('/usr/bin/pwqcheck', { err: "message" })
+                .input(
+                    [
+                        new_password,
+                        old_password,
+                        `${user}:::::/dev/null:/sbin/nologin`,
+                    ].join('\n')
+                )
+                .done(function(content) {
+                    if (content === 'OK\n')
+                        resolve({ value: '', message: undefined });
+                    else
+                        reject(new Error(_("Password is too weak")));
+                })
+                .fail(function(ex) {
+                    if (!force)
+                        reject(new Error(ex.message || _("Password is not acceptable")));
+                    else
+                        resolve({ value: 0 });
+                });
+    });
+}
+
 export const PasswordFormFields = ({
     password_label, password_confirm_label,
     password_label_info,
     initial_password,
     error_password, error_password_confirm,
-    idPrefix, change
+    idPrefix, change,
+    user_name
 }) => {
     const [password, setPassword] = useState(initial_password);
     const [passwordConfirm, setConfirmPassword] = useState(undefined);
@@ -62,7 +117,7 @@ export const PasswordFormFields = ({
         change("password", value);
 
         if (value) {
-            password_quality(value)
+            password_quality_proxy(value, '', user_name)
                     .catch(() => {
                         return { value: 0 };
                     })
diff --git a/pkg/storaged/manifest.json b/pkg/storaged/manifest.json
index 1e686ab11..3774b405d 100644
--- a/pkg/storaged/manifest.json
+++ b/pkg/storaged/manifest.json
@@ -53,6 +53,7 @@
     "config": {
         "nfs_client_package": {
             "rhel": "nfs-utils", "fedora": "nfs-utils",
+            "altlinux": "nfs-utils",
             "opensuse": "nfs-client", "opensuse-leap": "nfs-client",
             "debian": "nfs-common", "ubuntu": "nfs-common",
             "arch": "nfs-utils"
diff --git a/pkg/users/account-create-dialog.js b/pkg/users/account-create-dialog.js
index 2f66c2775..0ef784aa2 100644
--- a/pkg/users/account-create-dialog.js
+++ b/pkg/users/account-create-dialog.js
@@ -23,7 +23,7 @@ import React from 'react';
 import { Checkbox, Form, FormGroup, TextInput, Popover, Flex, FlexItem, Radio } from '@patternfly/react-core';
 import { has_errors } from "./dialog-utils.js";
 import { passwd_change } from "./password-dialogs.js";
-import { password_quality, PasswordFormFields } from "cockpit-components-password.jsx";
+import { password_quality_proxy, PasswordFormFields } from "cockpit-components-password.jsx";
 import { show_modal_dialog, apply_modal_dialog } from "cockpit-components-dialog.jsx";
 import { HelpIcon } from '@patternfly/react-icons';
 
@@ -60,7 +60,8 @@ function AccountCreateBody({ state, errors, change }) {
                                 error_password={errors && errors.password}
                                 error_password_confirm={errors && errors.password_confirm}
                                 idPrefix="accounts-create-password"
-                                change={change} />
+                                change={change}
+                                user_name={user_name} />
 
             <FormGroup label={_("Authentication")} fieldId="accounts-create-locked" hasNoPaddingTop>
                 <Radio id="account-use-password"
@@ -214,7 +215,7 @@ export function account_create_dialog(accounts) {
 
         errs.user_name = validate_username(user_name, accounts);
 
-        return password_quality(password, force)
+        return password_quality_proxy(password, '', user_name, force)
                 .catch(ex => {
                     errs.password = (ex.message || ex.toString()).replaceAll("\n", " ");
                 })
diff --git a/pkg/users/account-details.js b/pkg/users/account-details.js
index f8d4e1079..5ebbcc9ef 100644
--- a/pkg/users/account-details.js
+++ b/pkg/users/account-details.js
@@ -49,7 +49,7 @@ function log_unexpected_error(error) {
 }
 
 function get_locked(name) {
-    return cockpit.spawn(["/usr/bin/passwd", "-S", name], { environ: ["LC_ALL=C"], superuser: "require" })
+    return cockpit.spawn(["/usr/bin/getent", "shadow", name], { environ: ["LC_ALL=C"], superuser: "require" })
             .catch(() => "")
             .then(content => {
                 const status = content.split(" ")[1];
diff --git a/pkg/users/account-logs-panel.jsx b/pkg/users/account-logs-panel.jsx
index 6e3cbac47..8b0210381 100644
--- a/pkg/users/account-logs-panel.jsx
+++ b/pkg/users/account-logs-panel.jsx
@@ -23,15 +23,13 @@ import React, { useState, useEffect } from 'react';
 import { Card, CardTitle, CardBody, Text } from '@patternfly/react-core';
 import { ListingTable } from 'cockpit-components-table.jsx';
 
-import * as timeformat from "timeformat.js";
-
 const _ = cockpit.gettext;
 
 export function AccountLogs({ name }) {
     const [logins, setLogins] = useState([]);
     useEffect(() => {
         if (logins.length === 0) {
-            cockpit.spawn(["/usr/bin/last", "--time-format", "iso", "-n", 25, name], { environ: ["LC_ALL=C"] })
+            cockpit.spawn(["/usr/bin/last", "-F", "-w", "-n", 25, name], { environ: ["LC_ALL=C"] })
                     .then(data => {
                         let logins = [];
                         data.split('\n').forEach(line => {
@@ -46,6 +44,7 @@ export function AccountLogs({ name }) {
 
                             // format:
                             // admin    web console  ::ffff:172.27.0. 2021-09-24T09:02:13+00:00 - 2021-09-24T09:04:20+00:00  (00:02)
+                            /*
                             const lines = line.split(/ +/);
                             const ended = new Date(lines[lines.length - 2]);
                             const started = new Date(lines[lines.length - 4]);
@@ -59,6 +58,13 @@ export function AccountLogs({ name }) {
                                 ended,
                                 from
                             });
+                            */
+                            /*
+                            one of last's format is
+                            "%-8.*s %-12.12s %-16.*s %-24.24s %-26.26s %-12.12s\n",
+                            which in unparsable(?), thereby, print it as is
+                            */
+                            logins.push(line);
                         });
 
                         // Only show 15 login lines
@@ -79,13 +85,11 @@ export function AccountLogs({ name }) {
             <CardBody className="contains-list">
                 <ListingTable variant="compact" aria-label={ _("Login history list") }
                     columns={ [
-                        { title: _("Started") },
-                        { title: _("Ended") },
-                        { title: _("From") },
+                        { title: _("Login history") },
                     ] }
                     rows={ logins.map((line, index) => ({
                         props: { key: index },
-                        columns: [timeformat.dateTime(line.started), timeformat.dateTime(line.ended), line.from]
+                        columns: [line]
                     }))} />
             </CardBody>
         </Card>
diff --git a/pkg/users/accounts-list.js b/pkg/users/accounts-list.js
index 222cb644a..67bc72fa1 100644
--- a/pkg/users/accounts-list.js
+++ b/pkg/users/accounts-list.js
@@ -57,7 +57,7 @@ function AccountItem({ account, current }) {
 
 export function AccountsList({ accounts, current_user }) {
     const filtered_accounts = accounts.filter(function(account) {
-        return !((account.uid < 1000 && account.uid !== 0) ||
+        return !((account.uid < 500 && account.uid !== 0) ||
                  account.shell.match(/^(\/usr)?\/sbin\/nologin/) ||
                  account.shell === '/bin/false');
     });
diff --git a/pkg/users/expiration-dialogs.js b/pkg/users/expiration-dialogs.js
index 4ae408596..b2a287f94 100644
--- a/pkg/users/expiration-dialogs.js
+++ b/pkg/users/expiration-dialogs.js
@@ -218,7 +218,7 @@ export function password_expiration_dialog(account, expire_days) {
                     clicked: () => {
                         if (validate()) {
                             const days = state.mode == "expires" ? parseInt(state.days) : 99999;
-                            return cockpit.spawn(["/usr/bin/passwd", "-x", String(days), account.name],
+                            return cockpit.spawn(["/usr/bin/chage", "-M", String(days), account.name],
                                                  { superuser: true, err: "message" });
                         } else {
                             update();
diff --git a/pkg/users/index.html b/pkg/users/index.html
index 28766bc76..84f65f089 100644
--- a/pkg/users/index.html
+++ b/pkg/users/index.html
@@ -25,6 +25,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <link href="users.css" type="text/css" rel="stylesheet">
     <script src="../base1/cockpit.js"></script>
+    <script src="../manifests.js"></script>
     <script src="../base1/po.js"></script>
     <script src="po.js"></script>
     <script src="users.js"></script>
diff --git a/pkg/users/manifest.json b/pkg/users/manifest.json
index 4ee89f263..52c42ed0a 100644
--- a/pkg/users/manifest.json
+++ b/pkg/users/manifest.json
@@ -15,5 +15,11 @@
                 }
             ]
         }
+    },
+
+    "config": {
+        "password_quality_lib": {
+            "altlinux": "libpasswdqc"
+        }
     }
 }
diff --git a/pkg/users/password-dialogs.js b/pkg/users/password-dialogs.js
index ade6b2079..1bd3a7016 100644
--- a/pkg/users/password-dialogs.js
+++ b/pkg/users/password-dialogs.js
@@ -24,7 +24,7 @@ import { Form, FormGroup, TextInput } from '@patternfly/react-core';
 
 import { has_errors } from "./dialog-utils.js";
 import { show_modal_dialog, apply_modal_dialog } from "cockpit-components-dialog.jsx";
-import { password_quality, PasswordFormFields } from "cockpit-components-password.jsx";
+import { password_quality_proxy, PasswordFormFields } from "cockpit-components-password.jsx";
 
 const _ = cockpit.gettext;
 
@@ -38,7 +38,8 @@ function passwd_self(old_pass, new_pass) {
         /.*New password: $/,
         /.*Retype new password: $/,
         /.*Enter new \w*\s?password: $/,
-        /.*Retype new \w*\s?password: $/
+        /.*Retype new \w*\s?password: $/,
+        /.*Re-type new password: $/
     ];
     const bad_exps = [
         /.*BAD PASSWORD:.*/
@@ -128,7 +129,7 @@ export function passwd_change(user, new_pass) {
 }
 
 function SetPasswordDialogBody({ state, errors, change }) {
-    const { need_old, password_old, current_user } = state;
+    const { need_old, password_old, current_user, user_name } = state;
 
     return (
         <Form isHorizontal onSubmit={apply_modal_dialog}>
@@ -148,7 +149,8 @@ function SetPasswordDialogBody({ state, errors, change }) {
                                 error_password={errors && errors.password}
                                 error_password_confirm={errors && errors.password_confirm}
                                 idPrefix="account-set-password"
-                                change={change} />
+                                change={change}
+                                user_name={user_name} />
         </Form>
     );
 }
@@ -165,6 +167,7 @@ export function set_password_dialog(account, current_user) {
         password: "",
         password_confirm: "",
         confirm_weak: false,
+        user_name: account.name,
     };
 
     let errors = { };
@@ -183,7 +186,7 @@ export function set_password_dialog(account, current_user) {
         update();
     }
 
-    function validate(force, password, password_confirm) {
+    function validate(force, password, password_confirm, password_old) {
         const errs = { };
 
         if (password != password_confirm)
@@ -192,7 +195,7 @@ export function set_password_dialog(account, current_user) {
         if (password.length > 256)
             errs.password = _("Password is longer than 256 characters");
 
-        return password_quality(password, force)
+        return password_quality_proxy(password, password_old, account.name, force)
                 .catch(ex => {
                     errs.password = (ex.message || ex.toString()).replaceAll("\n", " ");
                 })
@@ -277,7 +280,7 @@ export function reset_password_dialog(account) {
                 caption: _("Reset password"),
                 style: "primary",
                 clicked: () => {
-                    return cockpit.spawn(["/usr/bin/passwd", "-e", account.name],
+                    return cockpit.spawn(["/usr/bin/chage", "-d", "0", account.name],
                                          { superuser: true, err: "message" });
                 }
             }
diff --git a/src/bridge/test-connect.c b/src/bridge/test-connect.c
index 25d147948..3e8ca6d20 100644
--- a/src/bridge/test-connect.c
+++ b/src/bridge/test-connect.c
@@ -181,11 +181,6 @@ setup_connect (TestConnect *tc,
 
   tc->listen_sock = g_socket_new (family, G_SOCKET_TYPE_STREAM,
                                   G_SOCKET_PROTOCOL_DEFAULT, &error);
-  g_assert_no_error (error);
-
-  g_socket_bind (tc->listen_sock, address, TRUE, &error);
-  g_object_unref (address);
-
   if (error != NULL && family == G_SOCKET_FAMILY_IPV6)
     {
       /* Some test runners don't have IPv6 loopback, strangely enough */
@@ -196,6 +191,11 @@ setup_connect (TestConnect *tc,
 
   g_assert_no_error (error);
 
+  g_socket_bind (tc->listen_sock, address, TRUE, &error);
+  g_object_unref (address);
+
+  g_assert_no_error (error);
+
   tc->address = g_socket_get_local_address (tc->listen_sock, &error);
   g_assert_no_error (error);
 
diff --git a/src/bridge/test-stream.c b/src/bridge/test-stream.c
index 227d72911..4af12372f 100644
--- a/src/bridge/test-stream.c
+++ b/src/bridge/test-stream.c
@@ -731,11 +731,6 @@ setup_connect (TestConnect *tc,
 
   tc->listen_sock = g_socket_new (family, G_SOCKET_TYPE_STREAM,
                                   G_SOCKET_PROTOCOL_DEFAULT, &error);
-  g_assert_no_error (error);
-
-  g_socket_bind (tc->listen_sock, address, TRUE, &error);
-  g_object_unref (address);
-
   if (error != NULL && family == G_SOCKET_FAMILY_IPV6)
     {
       /* Some test runners don't have IPv6 loopback, strangely enough */
@@ -746,6 +741,11 @@ setup_connect (TestConnect *tc,
 
   g_assert_no_error (error);
 
+  g_socket_bind (tc->listen_sock, address, TRUE, &error);
+  g_object_unref (address);
+
+  g_assert_no_error (error);
+
   tc->address = g_socket_get_local_address (tc->listen_sock, &error);
   g_assert_no_error (error);
 
diff --git a/src/common/cockpitloopback.c b/src/common/cockpitloopback.c
index 2a2cb6f75..b70fab6c9 100644
--- a/src/common/cockpitloopback.c
+++ b/src/common/cockpitloopback.c
@@ -127,6 +127,20 @@ cockpit_loopback_connectable_iface (GSocketConnectableIface *iface)
   iface->proxy_enumerate = cockpit_loopback_enumerate;
 }
 
+static gboolean
+ipv6_enabled (void)
+{
+  GSocket *socket;
+  socket = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM,
+                G_SOCKET_PROTOCOL_DEFAULT, NULL);
+  if (socket)
+    {
+      g_object_unref (socket);
+      return TRUE;
+    }
+  return FALSE;
+}
+
 GSocketConnectable *
 cockpit_loopback_new (guint16 port)
 {
@@ -135,9 +149,12 @@ cockpit_loopback_new (guint16 port)
 
   self = g_object_new (COCKPIT_TYPE_LOOPBACK, NULL);
 
-  addr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6);
-  g_queue_push_tail (&self->addresses, g_inet_socket_address_new (addr, port));
-  g_object_unref (addr);
+  if (ipv6_enabled())
+    {
+      addr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6);
+      g_queue_push_tail (&self->addresses, g_inet_socket_address_new (addr, port));
+      g_object_unref (addr);
+    }
 
   addr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
   g_queue_push_tail (&self->addresses, g_inet_socket_address_new (addr, port));
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin