implement profiles

This commit is contained in:
Valentin Boettcher 2024-04-05 18:47:33 -04:00
parent c9bdbb305c
commit f85a1900b7
No known key found for this signature in database
GPG key ID: E034E12B7AF56ACE
6 changed files with 119 additions and 132 deletions

View file

@ -1,16 +1,24 @@
from pathlib import Path
import tomllib
from types import SimpleNamespace
import sys
with open(Path.home() / ".o365-oauth-config.toml", "rb") as f:
config_data = tomllib.load(f)
cache_path = Path.home() / ".chache/o365-oauth"
cache_path.mkdir(parents=True, exist_ok=True)
def get_config(profile):
with open(Path.home() / ".o365-auth-config.toml", "rb") as f:
toplevel_data = tomllib.load(f)
ClientId = config_data["ClientId"]
ClientSecret = config_data["ClientSecret"]
Scopes = config_data["Scopes"]
RefreshTokenFileName = cache_path / "imap_smtp_refresh_token"
AccessTokenFileName = cache_path / "imap_smtp_access_token"
if profile not in toplevel_data:
sys.exit("Invalid profile specified.")
Authority = config_data["Authority"] or None
config_data = toplevel_data[profile]
cache_path = Path.home() / ".cache/o365-oauth" / profile
cache_path.mkdir(parents=True, exist_ok=True)
return SimpleNamespace(
ClientId = config_data["ClientId"],
ClientSecret = config_data["ClientSecret"],
Scopes = config_data["Scopes"],
RefreshTokenFileName = cache_path / "imap_smtp_refresh_token",
AccessTokenFileName = cache_path / "imap_smtp_access_token",
Authority = config_data["Authority"] or None)

87
flake.lock generated
View file

@ -13,61 +13,11 @@
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1642700792,
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"mach-nix": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs",
"pypi-deps-db": "pypi-deps-db"
},
"locked": {
"lastModified": 1705470643,
"narHash": "sha256-CqgkRcvOonJ2fL6542/ykZR3yL6qVLWy0pdyY5YKRlE=",
"owner": "DavHau",
"repo": "mach-nix",
"rev": "28f563aeb8c9e679a3f2b531c728573fce7a4594",
"type": "github"
},
"original": {
"id": "mach-nix",
"id": "flake-utils",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1643805626,
"narHash": "sha256-AXLDVMG+UaAGsGSpOtQHPIKB+IZ0KSd9WS77aanGzgc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "554d2d8aa25b6e583575459c297ec23750adb6cb",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1712192574,
"narHash": "sha256-LbbVOliJKTF4Zl2b9salumvdMXuQBr2kuKP5+ZwbYq4=",
@ -83,27 +33,11 @@
"type": "github"
}
},
"pypi-deps-db": {
"flake": false,
"locked": {
"lastModified": 1685526402,
"narHash": "sha256-V0SXx0dWlUBL3E/wHWTszrkK2dOnuYYnBc7n6e0+NQU=",
"owner": "DavHau",
"repo": "pypi-deps-db",
"rev": "ba35683c35218acb5258b69a9916994979dc73a9",
"type": "github"
},
"original": {
"owner": "DavHau",
"repo": "pypi-deps-db",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"mach-nix": "mach-nix",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs",
"systems": "systems_2"
}
},
"systems": {
@ -120,6 +54,21 @@
"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

@ -3,17 +3,23 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
systems.url = "github:nix-systems/default";
};
outputs = { nixpkgs, flake-utils, mach-nix, ... }:
outputs = { self, nixpkgs, flake-utils, systems, ... }:
let
eachSystem = nixpkgs.lib.genAttrs (import systems);
in
{
homeManagerModules.default = import ./nix/hm-module.nix self;
flake-utils.lib.eachDefaultSystem (system:
packages = eachSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
with pkgs.python3Packages; {
packages.default = pkgs.stdenv.mkDerivation {
name = "myscript";
default = pkgs.stdenv.mkDerivation {
name = "o365-auth";
propagatedBuildInputs = [
(pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
msal
@ -26,6 +32,6 @@
install -Dm755 ${./config.py} $out/bin/config.py
'';
};
homeManagerModules.default = import ./nix/hm-module.nix self;
});
};
}

View file

@ -8,14 +8,22 @@ import threading
import urllib.parse
import webbrowser
if len(sys.argv) > 1:
profile = sys.argv[1]
else:
sys.exit("Please provide a profile name as the first argument.")
profile_config = config.get_config(profile)
redirect_uri = "http://localhost:8745/"
# We use the cache to extract the refresh token
cache = SerializableTokenCache()
app = ConfidentialClientApplication(config.ClientId, client_credential=config.ClientSecret, token_cache=cache, authority=config.Authority)
app = ConfidentialClientApplication(profile_config.ClientId, client_credential=profile_config.ClientSecret, token_cache=cache, authority=profile_config.Authority)
url = app.get_authorization_request_url(config.Scopes, redirect_uri=redirect_uri)
url = app.get_authorization_request_url(profile_config.Scopes, redirect_uri=redirect_uri)
# webbrowser.open may fail silently
print("Navigate to the following url in a web browser, if doesn't open automatically:")
@ -62,7 +70,7 @@ if code == '':
i = resp.find('code') + 5
code = resp[i : resp.find('&', i)] if i > 4 else resp
token = app.acquire_token_by_authorization_code(code, config.Scopes, redirect_uri=redirect_uri)
token = app.acquire_token_by_authorization_code(code, profile_config.Scopes, redirect_uri=redirect_uri)
print()
@ -70,10 +78,10 @@ if 'error' in token:
print(token)
sys.exit("Failed to get access token")
with open(config.RefreshTokenFileName, 'w') as f:
print(f'Refresh token acquired, writing to file {config.RefreshTokenFileName}')
with open(profile_config.RefreshTokenFileName, 'w') as f:
print(f'Refresh token acquired, writing to file {profile_config.RefreshTokenFileName}')
f.write(token['refresh_token'])
with open(config.AccessTokenFileName, 'w') as f:
print(f'Access token acquired, writing to file {config.AccessTokenFileName}')
with open(profile_config.AccessTokenFileName, 'w') as f:
print(f'Access token acquired, writing to file {profile_config.AccessTokenFileName}')
f.write(token['access_token'])

View file

@ -1,33 +1,35 @@
self: {
config,
lib,
pkgs,
...
}:
self: { config
, lib
, pkgs
, ...
}:
with lib;
let
inherit (pkgs.stdenv.hostPlatform) system;
package = self.packages.${system}.default;
cfg = config.programs.o365-auth;
in {
in
{
options.programs.o365-auth = {
enable = mkEnableOption "o365 token refresh script";
package = mkOption {
type = types.package;
default = package;
};
config = mkOption {
type = types.str;
default = ''
ClientId = "08162f7c-0fd2-4200-a84a-f25a4db0b584"
ClientSecret = "TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82"
Scopes = ['https://outlook.office.com/IMAP.AccessAsUser.All','https://outlook.office.com/SMTP.Send']
RefreshTokenFileName = "imap_smtp_refresh_token"
AccessTokenFileName = "imap_smtp_access_token"
Authority = false
'';
[mcgill]
ClientId = "08162f7c-0fd2-4200-a84a-f25a4db0b584"
ClientSecret = "TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82"
Scopes = ['https://outlook.office.com/IMAP.AccessAsUser.All','https://outlook.office.com/SMTP.Send']
Authority = false
'';
};
};
config = mkIf cfg.enable {
home.packages = [ package ];
home.file.".o365-auth-config".source = cfg.config;
home.packages = [ cfg.package ];
home.file.".o365-auth-config".text = cfg.config;
};
}

View file

@ -3,27 +3,41 @@ from msal import ConfidentialClientApplication, SerializableTokenCache
import config
import sys
print_access_token = True
print_access_token = True
# get first command line argument
if len(sys.argv) > 1:
profile = sys.argv[1]
else:
sys.exit("Please provide a profile name as the first argument.")
profile_config = config.get_config(profile)
# We use the cache to extract the refresh token
cache = SerializableTokenCache()
app = ConfidentialClientApplication(config.ClientId, client_credential=config.ClientSecret, token_cache=cache, authority=config.Authority)
app = ConfidentialClientApplication(profile_config.ClientId, client_credential=profile_config.ClientSecret, token_cache=cache, authority=profile_config.Authority)
old_refresh_token = open(config.RefreshTokenFileName,'r').read()
# check if file exists and error out if it doesn't
try:
old_refresh_token = open(profile_config.RefreshTokenFileName,'r').read()
except FileNotFoundError:
sys.exit("Please get the initial token by running `o365-get-token` first.")
token = app.acquire_token_by_refresh_token(old_refresh_token,config.Scopes)
token = app.acquire_token_by_refresh_token(old_refresh_token,profile_config.Scopes)
if 'error' in token:
print(token)
sys.exit("Failed to get access token")
# you're supposed to save the old refresh token each time
with open(config.RefreshTokenFileName, 'w') as f:
with open(profile_config.RefreshTokenFileName, 'w') as f:
#f.write(cache.find('RefreshToken')[0]['secret'])
f.write(token['refresh_token'])
with open(config.AccessTokenFileName, 'w') as f:
with open(profile_config.AccessTokenFileName, 'w') as f:
f.write(token['access_token'])
if print_access_token:
print(token['access_token'])