Compare commits

...

7 Commits

Author SHA1 Message Date
57f4ff5187 flake: update inputs 2023-06-01 10:48:13 +10:00
7d59e09968 flake: fix missing setuptools imports 2022-12-07 13:06:25 +11:00
4537511dab flake: update inputs 2022-12-07 12:50:20 +11:00
74051f3afc flake: add nixosModule 2022-11-02 11:02:41 +11:00
c3831e9efe routes/home: handle missing groups 2022-09-03 15:29:54 +10:00
860dbe6032 docs: add readme 2022-09-03 15:29:54 +10:00
aa86f5de48 routes/home: configure brand name 2022-09-03 15:29:51 +10:00
7 changed files with 205 additions and 29 deletions

37
README.md Normal file
View File

@ -0,0 +1,37 @@
# frontpage (name pending)
The front page of your self-hosted server.
This app fits the use case of having multiple applications with access gated by
an OIDC provider, and showing a user what applications they have access to.
## Usage
```sh
frontpage -c CONFIG.TOML
```
where a minimal config file looks like:
```toml
[oidc]
client_id = "some_id"
client_secret = "some_secret"
issuer = "https://auth.example.com/oauth"
scopes = [ "groups" ]
```
Applications are defined using the `apps` keys:
```toml
[apps.login]
name = "Login portal"
url = "https://auth.example.com"
description = "Update your user details"
groups = [ "users" ]
```
In this example, only users whose OIDC groups claim includes `users` will be
allowed to see a link to the login portal. Protection of the link, should a user
gain access to it otherwise, is expected to be done externall (e.g., via an
ingress controller).

67
flake.lock generated
View File

@ -1,12 +1,15 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"lastModified": 1685518550,
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github"
},
"original": {
@ -16,12 +19,15 @@
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"lastModified": 1685518550,
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github"
},
"original": {
@ -32,26 +38,27 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1662166109,
"narHash": "sha256-cmNWUeVDfSJC9y8nmX2O/7kuOXJU1ZVFJMYP87qrm/Y=",
"lastModified": 1685533922,
"narHash": "sha256-y4FCQpYafMQ42l1V+NUrMel9RtFtZo59PzdzflKR/lo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cb5a1a003dde9c16a1ae4b28cbe7bf0fab15da32",
"rev": "3a70dd92993182f8e514700ccf5b1ae9fc8a3b8d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1662166109,
"narHash": "sha256-cmNWUeVDfSJC9y8nmX2O/7kuOXJU1ZVFJMYP87qrm/Y=",
"lastModified": 1685580370,
"narHash": "sha256-zTPVdZwLVQl/y0QTZEtYs9iNvZW6H9h+/MZsKdUinu8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "cb5a1a003dde9c16a1ae4b28cbe7bf0fab15da32",
"rev": "fabe2064486b607c2516296ce6108549de0649c4",
"type": "github"
},
"original": {
@ -66,11 +73,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1662044036,
"narHash": "sha256-+5YZPznhy1gEKPdWiZj7UcLoRaLbfvUDr8OzOY+75jM=",
"lastModified": 1685418143,
"narHash": "sha256-q2ORekI8au0pGMtOLQI8WMCJBxjzWgYRHpiEOVSBq3w=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "efe5b281b51c22495c488480d23d7bb1426bf3ba",
"rev": "f11cc14e28078c701072f2d1fb34a6495c9376b1",
"type": "github"
},
"original": {
@ -85,6 +92,36 @@
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@ -2,32 +2,53 @@
description = "Application packaged using poetry2nix";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
inputs.poetry2nix.url = "github:nix-community/poetry2nix";
outputs = { self, nixpkgs, flake-utils, poetry2nix }: {
overlay = nixpkgs.lib.composeManyExtensions [
poetry2nix.overlay
(final: prev: {
frontpage = prev.poetry2nix.mkPoetryApplication {
python = prev.python39;
projectDir = ./.;
};
frontpage = prev.poetry2nix.mkPoetryApplication
{
python = prev.python39;
projectDir = ./.;
overrides =
prev.poetry2nix.overrides.withDefaults (final_: prev_:
nixpkgs.lib.listToAttrs
(builtins.map
(name: {
inherit name;
value = prev_."${name}".overridePythonAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ final_.setuptools ];
});
})
[ "beaker" "flask-pyoidc-oda" "oic" ]));
};
})
];
nixosModules = rec {
default = frontpage;
frontpage = import ./nixos;
};
} // (flake-utils.lib.eachDefaultSystem (system:
let pkgs = import nixpkgs { inherit system; overlays = [ self.overlay ]; }; in
{
apps = {
default = pkgs.frontpage;
frontpage = pkgs.frontpage;
};
defaultApp = pkgs.frontpage;
devShell = (pkgs.poetry2nix.mkPoetryEnv {
python = pkgs.python39;
projectDir = ./.;
extraPackages = ps: [ ps.python-lsp-server ];
}).env;
packages = {
default = pkgs.frontpage;
frontpage = pkgs.frontpage;
};
}));
}

View File

@ -15,6 +15,7 @@ class CoreConfig:
debug: bool = False
port: int = 5000
name: str = "Front page"
@dataclass
@ -43,8 +44,8 @@ class AppConfig:
class Config:
"""Top-level configuration."""
core: CoreConfig
oidc: OidcConfig
core: CoreConfig = field(default_factory=CoreConfig)
oidc: OidcConfig = field(default_factory=OidcConfig)
apps: Dict[str, AppConfig] = field(default_factory=dict)

View File

@ -7,7 +7,7 @@ from flask import Blueprint, current_app, render_template
from flask_pyoidc import OIDCAuthentication
from flask_pyoidc.user_session import UserSession
from frontpage.config import AppConfig, current_config
from frontpage.config import AppConfig, Config, current_config
def _allowed(items_from: Iterable[Any], items_in: Iterable[Any]) -> bool:
@ -29,12 +29,16 @@ def register(auth: OIDCAuthentication, auth_provider: str) -> Blueprint:
Renders the home route.
"""
user_session = UserSession(flask.session)
groups: List[str] = user_session.userinfo["groups"]
groups: List[str] = user_session.userinfo.get("groups") or []
apps: AppConfig = current_config().apps
config: Config = current_config()
name = config.core.name
apps = config.apps
allowed_apps = {
ident: a for ident, a in apps.items() if _allowed(a.groups, groups)
}
return render_template("home.html", apps=allowed_apps, groups=groups)
return render_template(
"home.html", brand_name=name, apps=allowed_apps, groups=groups
)
return routes

View File

@ -8,7 +8,7 @@
<body>
<nav class="nav">
<div class="nav-left">
<a class="brand" href="/">Front page</a>
<a class="brand" href="/">{{ brand_name }}</a>
</div>
</nav>
<main class="container">

76
nixos/default.nix Normal file
View File

@ -0,0 +1,76 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.frontpage;
toml = pkgs.formats.toml { };
fullSettings = recursiveUpdate cfg.settings {
core.port = cfg.port;
oidc.client_secret = "@SECRET@";
};
settingsFile = toml.generate "config.toml" fullSettings;
in
{
options.services.frontpage = {
enable = mkEnableOption "frontpage";
package = mkPackageOption pkgs "frontpage" { };
user = mkOption {
type = types.str;
default = "frontpage";
};
group = mkOption {
type = types.str;
default = "frontpage";
};
port = mkOption {
type = types.port;
default = 32195;
};
oidcSecretFile = mkOption {
type = types.path;
description = ''
Path to a file containing the OIDC secret for the application.
'';
};
settings = mkOption {
type = with types; attrsOf anything;
default = { };
description = ''
Settings attribute set as described by the documentation.
'';
};
};
config = mkIf cfg.enable {
systemd.services.frontpage = {
description = "Web front page";
wantedBy = [ "multi-user.target" ];
preStart = ''
sed \
"s=@SECRET@=$(<${cfg.oidcSecretFile})=" \
${settingsFile} \
> /run/frontpage/config.toml
'';
serviceConfig = {
Restart = "on-failure";
RestartSec = "2s";
ExecStart = "${cfg.package}/bin/frontpage -c /run/frontpage/config.toml";
RuntimeDirectory = [ "frontpage" ];
User = cfg.user;
};
};
users.users."${cfg.user}" = {
isSystemUser = true;
group = cfg.group;
};
users.groups."${cfg.group}" = { };
};
}