NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
17.29k stars 13.54k forks source link

Declarative user profile pictures #163080

Open alyaeanyx opened 2 years ago

alyaeanyx commented 2 years ago

Currently, user profile pictures as they are used in Gnome cannot be reasonably changed. The interface used for changing user icons in the Gnome system settings is unreliable (seems to only work for the preset icons). The only way to change them at all is to manually put them into /var/lib/AccountsService/icons.

My ideas to make declarative user icons possible are:

davidak commented 2 years ago

This would also be great to work with Pantheon since we don't have the users settings. (cc @NixOS/pantheon)

We could use something like users.users.<name>.avatar. Is there a standard how this is called?

We can specify an image there that we have in our configuration directory and every DE can use it, which might need DE specific code.

Related issues:

aanderse commented 2 years ago

Doesn't ~/.face also work? Seems reasonably declarative...

Instead of patching can you please open a feature request upstream?

alyaeanyx commented 2 years ago

Doesn't ~/.face also work? Seems reasonably declarative...

Yes, that seems like something that could be integrated easily into home-manager. However, according to my understanding, the accountsservice user files (/var/lib/AccountsService/users/<username>) need to have an Icon entry specifying /home/<username>/.face as the user profile picture, which is not the case by default on my GNOME install. And even apart from that, there seem to be permission issues with it (https://github.com/NixOS/nixpkgs/issues/73976)

aanderse commented 2 years ago

https://wiki.archlinux.org/title/SDDM#User_icon_(avatar) suggests using ACLs in the case of sddm. Presumably that would work with other software as well.

jakehamilton commented 2 years ago

I'd love to see this supported. I had tried a handful of things but couldn't get my icon to display in GDM. Currently making use of home-manager to add ~/.face which works in a GNOME session at least.

https://github.com/jakehamilton/config/blob/52cdc8151a9d8f1a9d5d182a8d480e9225312a77/modules/user/user.nix#L42

litchipi commented 2 years ago

Something like:

      boot.postBootCommands = let
        gdm_user_conf = ''
          [User]
          Session=
          XSession=
          Icon=${./path/to/your/icon}
          SystemAccount=false
        '';
      in ''
        echo '${gdm_user_conf}' > /var/lib/AccountsService/users/USERNAME_HERE
      '';

works for me, even if it may be a bit hacky. Maybe it could be cleaned into a NixOS option like

services.xserver.displayManager.gdm.user_icons = {
   name0 = ./path/to/icon0;
   name1 = ./path/to/icon1;
};
SeineEloquenz commented 1 year ago

I used @litchipi's code to make a module suitable for multi-user installs. I thought I'd share it so it might help others. It adds an option users.users.<name>.icon where we can provide a path to an image file that is to be used:

{ lib, config, ...}:

let
  userOptions = with lib; {
    options.icon = mkOption { type = types.nullOr types.path; default = null; };
  };

  mkGdmUserConf = icon: ''
    [User]
    Session=
    XSession=
    Icon=${icon}
    SystemAccount=false
  '';

  userList = with lib; filter (entry: entry.icon != null) (mapAttrsToList (name: value: { inherit name; icon = value.icon; }) config.users.users);

  mkBootCommand = entry: "echo -e '${mkGdmUserConf entry.icon}' > /var/lib/AccountsService/users/${entry.name}\n";

  bootCommands = map mkBootCommand userList;
in

{
  options = {
    users.users = with lib; with types; mkOption {
      type = attrsOf (submodule userOptions);
    };
  };

  config = lib.mkIf config.services.xserver.displayManager.gdm.enable {
    boot.postBootCommands = with lib; strings.concatStrings bootCommands;
  };
}
musjj commented 1 year ago

It's interesting that the Arch Wiki recommends this as one of the options:

Alternatively, create the image file as /home/username/.face and skip the next step if the defaults already point to the user home directory path

Of course, this won't work since the home directory won't be readable. Does it expect the user to chmod 755 their home directory, or is there something I'm missing here?

jonathan-conder commented 11 months ago

Of course, this won't work since the home directory won't be readable. Does it expect the user to chmod 755 their home directory, or is there something I'm missing here?

AccountsService runs as root so it can see the icon just fine. But GDM can't. In theory AccountsService could copy .face into /var/lib/AccountsService/icons but it doesn't seem to do that currently.

Here is another approach which avoids writing anything to /var/lib:

{config, lib, pkgs, ...}: let
  inherit (lib) types;

  iconOptions.options.icon = lib.mkOption {
    type = types.nullOr types.path;
    default = null;
  };

  users = lib.filterAttrs (_: value: value.icon != null) config.users.users;
  iconLinks = lib.mapAttrsToList (name: value: "ln -s ${value.icon} ${name}") users;
  icons = pkgs.runCommand "user-icons" {} ''
    mkdir $out
    cd $out
    ${builtins.concatStringsSep "\n" iconLinks}
  '';

  templateText = lib.generators.toINI {} {
    User.Icon = "${icons}/\${USER}";
  };
  templateFile = pkgs.writeText "user-template" templateText;
  templateDir = "share/accountsservice/user-templates";

  templates = pkgs.runCommand "user-templates" { meta.priority = 0; } ''
    mkdir -p $out/${templateDir}
    cd $out/${templateDir}

    ln -s ${templateFile} administrator
    ln -s ${templateFile} standard
  '';
in {
  options.users.users = lib.mkOption {
    type = types.attrsOf (types.submodule iconOptions);
  };

  config.environment.systemPackages = [templates];
}

It creates an icons package with links name -> users.users.${name}.icon, then uses this feature to set the default icon to ${icons}/$USER. That's in the nix store so it should be world-readable.

nixos-discourse commented 9 months ago

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/setting-the-user-profile-image-under-gnome/36233/1

D3vil0p3r commented 7 months ago

It's interesting that the Arch Wiki recommends this as one of the options:

Alternatively, create the image file as /home/username/.face and skip the next step if the defaults already point to the user home directory path

Of course, this won't work since the home directory won't be readable. Does it expect the user to chmod 755 their home directory, or is there something I'm missing here?

You don't need chmod 755. More precisely, it does not need write permission in home folder. It needs only exec permission, so chmod 711.

PS: @jonathan-conder your solution seems nice. Why don't you implement it as module in Nixpkgs?