feat: set-token.ps1 + JF LANdata import script
- get-token.ps1: reads token from ~/.ka-note/token.txt, validates JWT expiry - set-token.ps1: stores token (from arg or clipboard) to ~/.ka-note/token.txt with expiry display - import-jf-landata.ps1: idempotent import for JF LANdata context (12 topics, 14 history entries) - MSAL auto-login blocked by missing redirect URI in app registration (documented in get-token.ps1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3b6ae8c92b
commit
a67dc06b30
|
|
@ -1,56 +1,70 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Gets a Bearer token for the Ka-Note production API via MSAL.PS.
|
||||
Installs MSAL.PS automatically if missing.
|
||||
Uses cached tokens / refresh tokens — browser login only needed on first run
|
||||
or after token cache is cleared.
|
||||
Gets a valid Bearer token for the Ka-Note production API.
|
||||
|
||||
PREFERRED (fully automatic): Add the redirect URI once in Azure Portal:
|
||||
App registrations → Ka-Note (1aba7af7-...) → Authentication
|
||||
→ Add platform "Mobile and desktop" → URI: https://login.microsoftonline.com/common/oauth2/nativeclient
|
||||
Then uncomment the MSAL section below.
|
||||
|
||||
CURRENT FALLBACK: reads cached token from %USERPROFILE%\.ka-note\token.txt
|
||||
Store a fresh token there with: .\set-token.ps1 "eyJ..."
|
||||
|
||||
.OUTPUTS
|
||||
Writes the access token string to stdout.
|
||||
|
||||
.EXAMPLE
|
||||
$token = & "$PSScriptRoot\get-token.ps1"
|
||||
Writes the access token string to stdout (no trailing newline).
|
||||
#>
|
||||
param()
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ClientId = '1aba7af7-eec1-4e49-b87e-9f941c0e8630'
|
||||
$TenantId = '94cf90d7-e9ff-49a1-bc3b-a5b94d3cc8ca'
|
||||
$Scopes = "api://$ClientId/access"
|
||||
$tokenFile = Join-Path $env:USERPROFILE '.ka-note\token.txt'
|
||||
|
||||
# --- Ensure MSAL.PS is available ---------------------------------------------
|
||||
if (-not (Get-Module -ListAvailable -Name 'MSAL.PS')) {
|
||||
Write-Host " [INFO] Installing MSAL.PS module..." -ForegroundColor DarkGray
|
||||
Install-Module -Name 'MSAL.PS' -Scope CurrentUser -Force -AllowClobber
|
||||
}
|
||||
Import-Module MSAL.PS -ErrorAction Stop
|
||||
# ── Option A: MSAL.PS (uncomment after adding redirect URI in Azure Portal) ───
|
||||
# Requires one-time Azure Portal step:
|
||||
# App registrations → 1aba7af7-eec1-4e49-b87e-9f941c0e8630 → Authentication
|
||||
# → Add platform "Mobile and desktop applications"
|
||||
# → Redirect URI: https://login.microsoftonline.com/common/oauth2/nativeclient
|
||||
# → Save
|
||||
#
|
||||
# if (-not (Get-Module -ListAvailable MSAL.PS)) {
|
||||
# Install-Module -Name MSAL.PS -Scope CurrentUser -Force -AllowClobber
|
||||
# }
|
||||
# Import-Module MSAL.PS
|
||||
# $p = @{
|
||||
# ClientId = '1aba7af7-eec1-4e49-b87e-9f941c0e8630'
|
||||
# TenantId = '94cf90d7-e9ff-49a1-bc3b-a5b94d3cc8ca'
|
||||
# Scopes = 'api://1aba7af7-eec1-4e49-b87e-9f941c0e8630/access'
|
||||
# }
|
||||
# try { $r = Get-MsalToken @p -Silent 2>$null }
|
||||
# catch { $r = Get-MsalToken @p -Interactive }
|
||||
# Write-Output $r.AccessToken
|
||||
# return
|
||||
|
||||
# --- Acquire token -----------------------------------------------------------
|
||||
$params = @{
|
||||
ClientId = $ClientId
|
||||
TenantId = $TenantId
|
||||
Scopes = $Scopes
|
||||
}
|
||||
|
||||
$result = $null
|
||||
|
||||
# 1. Try silent first (uses cached access token or refresh token)
|
||||
try {
|
||||
$result = Get-MsalToken @params -Silent 2>$null
|
||||
} catch {
|
||||
# No cached token or refresh failed — fall back to interactive
|
||||
}
|
||||
|
||||
# 2. Interactive browser login
|
||||
if (-not $result) {
|
||||
Write-Host " [AUTH] Opening browser for login..." -ForegroundColor Yellow
|
||||
$result = Get-MsalToken @params -Interactive
|
||||
}
|
||||
|
||||
if (-not $result -or -not $result.AccessToken) {
|
||||
Write-Error "Failed to acquire token."
|
||||
# ── Option B: cached token file (current default) ────────────────────────────
|
||||
if (-not (Test-Path $tokenFile)) {
|
||||
Write-Error "No cached token found. Run: .\set-token.ps1 `"eyJ...`""
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Output only the token (callers capture via $token = & .\get-token.ps1)
|
||||
Write-Output $result.AccessToken
|
||||
$token = (Get-Content $tokenFile -Raw -Encoding UTF8).Trim()
|
||||
|
||||
# Check JWT expiry (payload is base64url, second segment)
|
||||
try {
|
||||
$parts = $token.Split('.')
|
||||
$payload = $parts[1]
|
||||
# Pad base64url to multiple of 4
|
||||
$pad = 4 - ($payload.Length % 4); if ($pad -ne 4) { $payload += '=' * $pad }
|
||||
$payload = $payload.Replace('-', '+').Replace('_', '/')
|
||||
$claims = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload)) | ConvertFrom-Json
|
||||
$expUtc = [System.DateTimeOffset]::FromUnixTimeSeconds($claims.exp).UtcDateTime
|
||||
$remaining = ($expUtc - [System.DateTime]::UtcNow).TotalMinutes
|
||||
|
||||
if ($remaining -lt 2) {
|
||||
Write-Warning "Token expired $(if ($remaining -lt 0) { [math]::Round(-$remaining) + ' min ago' } else { 'in < 2 min' }). Run: .\set-token.ps1 `"eyJ...`""
|
||||
exit 1
|
||||
}
|
||||
Write-Host " Token valid for $([math]::Round($remaining)) min." -ForegroundColor DarkGray
|
||||
} catch {
|
||||
Write-Warning "Could not parse token expiry - using as-is."
|
||||
}
|
||||
|
||||
Write-Output $token
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Imports / re-imports "JF: LANdata" into Ka-Note. Idempotent.
|
||||
|
||||
.EXAMPLE
|
||||
.\import-jf-landata.ps1 -Prod
|
||||
#>
|
||||
param([switch]$Prod, [switch]$Force)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
. "$PSScriptRoot\import-helpers.ps1"
|
||||
|
||||
$scripts = $PSScriptRoot
|
||||
$RepoRoot = Resolve-Path "$PSScriptRoot\..\.."
|
||||
$workDir = Join-Path $RepoRoot "work"
|
||||
|
||||
# ── 1. Download ───────────────────────────────────────────────────────────────
|
||||
Write-Host "`n[1] Downloading bundle..." -ForegroundColor Cyan
|
||||
$dlArgs = @('-File', "$scripts\download.ps1", '-Force')
|
||||
if ($Prod) { $dlArgs += '-Prod' }
|
||||
& powershell @dlArgs
|
||||
if ($LASTEXITCODE -ne 0) { throw "Download failed" }
|
||||
|
||||
Initialize-BundleSession -WorkDir $workDir
|
||||
$ctxPath = "$workDir\contexts.json"
|
||||
$topsPath = "$workDir\topics.json"
|
||||
$ctxId = 'jf-landata'
|
||||
|
||||
# ── 2. Context ────────────────────────────────────────────────────────────────
|
||||
Write-Host "`n[2] Context..." -ForegroundColor Cyan
|
||||
Upsert-Context -CtxPath $ctxPath -Id $ctxId -Name 'JF: LANdata' -SortOrder 30
|
||||
|
||||
# ── 3. Cleanup + Topics ───────────────────────────────────────────────────────
|
||||
Write-Host "`n[3] Cleanup + Topics..." -ForegroundColor Cyan
|
||||
Remove-ContextTopics -TopicsPath $topsPath -ContextId $ctxId
|
||||
|
||||
$journalId = Add-Topic $topsPath $ctxId 'Sitzungsprotokoll' `
|
||||
'Meeting-Notizen chronologisch.' 5 $true
|
||||
|
||||
Add-Topic $topsPath $ctxId 'Access-Manager' `
|
||||
"- VM vorbereitet, warten auf Stefan`n- SQL-Server benoetigt`n- Banf kann freigegeben werden`n- Angebot mit AM (nur Drolshagen)`n- Jo triggert Thema an" 10 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'Teams-Probleme HKR / BZ-WLAN' `
|
||||
"- LANdata startet Aufzeichnung, Log-Probleme`n- Ball bei Steffen (Kabel neu ziehen, Kabel Telekom-Sophos tauschen)`n- Zaehler resettet, Fehler weniger geworden`n- Steffen: zweiten Rechner mit PfSense Gateway`n- PRTG Leitung HKR ueberwachen`n- Ticket BZ-WLAN: #481057 DNS Verschluesselung" 20 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'PRTG und Sophos / STAS' `
|
||||
"- Fromm / Abdul an PRTG-Sophos Protokollierung WAN-Port`n- Jo braucht PRTG-Daten fuer Analyse`n- STAS-Problem: Sophos Heartbeat`n- Sophos Ticket bei Marvin, Citrix-Endpoint-Problem`n- Auth-Service geaendert (geloest)" 30 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId '5G Router / Netzwerk HKR' `
|
||||
"- Problem Standard-Gateway → asynchrone Routen`n- Reverse-Problem: Default-GW ist Switch → Routing-Problem`n- Test Fr./Sa. Abend: max. 2h Internet-Downtime`n- Philip: Test nach Karneval`n- diverse Netzwerkfehler HKR, hohe Verluste am Switch (Zaehler)" 40 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'Citrix / NetScaler / MFA' `
|
||||
"- Passwortproblem: Zeichensatz auf NS anders als MPS`n- Stefan: Termin SVA-Citrix + LANdata-MPS`n- MFA fuer Citrix: Gruppen mit IP-Adressen kombinieren (erledigt)`n- Citrix Bleed Sicherheitsluecke`n- Sophos Endpoint auf Citrix Terminalserver (Marvin)" 50 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'Microsoft Lizenzen' `
|
||||
"- Open Value Verlaengerung: 60:40 Jahres-/Monatslizenzen`n- E3 ohne Teams (EU-Gesetz): 2 EUR guenstiger, mit Teams 6 EUR teurer`n- Win11 OEM: open key ca. 69 EUR Upgrade`n- KYC fuer Teams Phone Porting (Jo)`n- Lizenzen WITEC: MS Server, Sophos FW, Hornet offboarding automatisch" 60 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'Azure Berechtigungskonzept' `
|
||||
"- Matrix: Welche Rollen fuer welche Aufgaben`n- Azubi war Voll-Admin der Azure-Cloud (Ticket #13969)`n- Mitglieder durch Gruppen (Willy liefert noch)`n- Zuordnung Org-Einheiten in Azure`n- Rollenbeschreibungen`n- unzulaessige Azure-Lizenzierung wg. MFA-Pflicht: P1 erforderlich" 70 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'SAN / Backup / File-Server' `
|
||||
"- SAN Latenzen (nicht freier Speicher, Willy sucht Support)`n- Diskrepanz: SAN 7 TB frei, VM 2 TB frei`n- Notfallhandbuch SAN erweitern`n- SAP-Ausfall: HA-Cluster hat nicht funktioniert (WAN-Port kein Trigger)`n- File-Server Ausfall: Ticket #257534" 80 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'Firewall / Heartbeat / Luedensch.' `
|
||||
"- Heartbeat Authentifizierung (CSOC Thema)`n- Slave als neuen Master neu konfigurieren`n- Luedenscheid: Betriebsmodus RED, Routen passen nicht zur Sophos-Konfig`n- HKR-Regel MS wegen Updates: Natting-Problem? NAT-Regeln fehlen?`n- Backup-FW update PhiLos" 90 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'China / Internationale Vernetzung' `
|
||||
"- AFT/staatliche Regulierungen`n- SD-WAN Empfehlung: lokale Anbieter, keinen Europaeischen`n- Artikel 9 Data Security Policy China`n- Logfiles 6 Monate vorhalten`n- MS 365: Popup-Funktion nicht verfuegbar (chinesische Regulierung)`n- FW China: Jo hat noch keine Antwort" 100 | Out-Null
|
||||
|
||||
Add-Topic $topsPath $ctxId 'DNS Verschluesselung' `
|
||||
"- DNS Traffic aktuell nicht verschluesselt`n- DNS Protection Sophos verschluesselt auch nicht`n- Ticket #481057`n- nicht kritisch aber auf der Liste" 110 | Out-Null
|
||||
|
||||
# ── 4. History entries ────────────────────────────────────────────────────────
|
||||
Write-Host "`n[4] History entries..." -ForegroundColor Cyan
|
||||
|
||||
Add-HistoryEntry $journalId '2026-02-18' "## Next`n`n- Access Manager`n- Sophos Ticket zum Thema STAS: bitte neues Ticket bei Sophos" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-02-18' "## 2026-02-18`n`n- 5G Router: erst WLAN-Test`n- diverse Replikationsfehler DC bei RES geloest`n- PRTG und Sophos: Jo macht Wissensuebergabe (Problem geloest)`n- HKR-Regel MS wegen Updates: LANdata im Austausch mit Anbieter, Kuriosite festgestellt, hakt nach`n- Idee Philip/ChrKl: Jo prueft Natting-Problem (NAT-Regeln ev. fehlend)" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-02-11' "## 2026-02-11`n`n- Backup und Firewall: Stefan beschreibt Grund fuer neuen ESX-Server`n- Termin Willi + Christopher wg. Domaenen-Anmeldung: Fehler behoben (falsch konfig. DCs), weitere Tests`n- Angebot DL fuer AM: ChrKl banft`n- TISAX: Status Sophos Heartbeat`n- Teams HKR: Zaehler resettet, Fehler weniger, auch mit neuen Kabeln. Steffen: zweiten Rechner mit PfSense. Warten auf Steffen`n- PRTG und Sophos: Jo braucht PRTG-Daten, dann tiefere Analyse`n- HKR-Regel MS: LANdata hakt nach" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-02-03' "## 2026-02-03`n`n- Neues Format LANdata: kostenloser IT-Strategie-Termin 2h, Ziel Roadmap`n- Info Server VLAN verteilt`n- PRTG und Sophos: Abdul/Fromm in Arbeit, finale Rueckmeldung ausstehend`n- Teams HKR: Ball bei Steffen (Kabel, PfSense)`n- 5G Router: Standard-Gateway Problem → asynchrone Routen, Philip verschiebt Test nach Karneval" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-01-28' "## 2026-01-28`n`n- PRTG und Sophos: Protokollierung WAN-Port, Philip prueft nochmal`n- Citrix NetScaler Passwortproblem: Zeichensatz NS != MPS, Stefan organisiert Termin`n- Austausch NHV KW23: Koordination (wer faehrt, wer schaltet, was wird getestet)`n- Teams HKR: LANdata startet Aufzeichnung" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-01-21' "## 2026-01-21`n`n- Windows Hello tut nicht → Eray uebernimmt`n- File-Server Problem: klingt nach DC-Problem`n- China: staatliche Regulierungen, SD-WAN Empfehlung lokale Anbieter, Artikel 9 Data Security Policy, Logfiles 6 Monate, MS 365 einschraenkungen`n- Empfehlung: Vernetzung China aus China beauftragen" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-01-14' "## 2026-01-14`n`n- HKR: Jo mit Telekom gesprochen (Leitung nicht stark ausgelastet), hohe Verluste am Switch (Zaehler), Termin Fr. 8:15`n- MacMon Angebot: genauer Plan Switche benoetigt, Philip legt Projekt in Plane an`n- FW HKR-Regel MS: Christian F. auf TODO`n- 5G Router: Problem Standard-Gateway, Test nach Karneval`n- PRTG und Sophos: Fromm in Arbeit`n- Edge-Problem: seit Sa. behoben" | Out-Null
|
||||
Add-HistoryEntry $journalId '2026-01-07' "## 2026-01-07`n`n- 5G Router: Standard-Gateway Problem (asynchrone Routen), reverse-Problem (Default-GW ist Switch), Sa./Fr. Test, max. 2h Downtime`n- PRTG mit Jo: Fromm bei Sophos dran, Ziel Internetbandbreite HKR ueberwachen`n- Citrix NetScaler: Zeichensatz-Problem, Stefan organisiert Termin`n- FW HKR-Regel MS: Teams bricht ab wenn Regel aktiv" | Out-Null
|
||||
Add-HistoryEntry $journalId '2025-12-10' "## 2025-12-10`n`n- SAN Probleme: nicht freier Speicher, Latenz unterirdisch, Willy sucht Unterstuetzung" | Out-Null
|
||||
Add-HistoryEntry $journalId '2025-12-03' "## 2025-12-03`n`n- KYC fuer Teams Phone Porting: Jo klaert`n- Teilen mit Malaysia: LANdata prueft, ob zusaetzliche Richtlinie gebaut werden kann`n- 5G Router: grundsaetzlich funktioniert er" | Out-Null
|
||||
Add-HistoryEntry $journalId '2025-11-26' "## 2025-11-26`n`n- Status Access-Manager: Jo triggert an`n- BitSight HKR untergliedern?`n- Citrix: Sonderzeichen-Problem in komplexen Passwoertern → NetScaler (nicht Radius)`n- Sophos AP Support kuendigen?`n- HKR sperren Sky: Auffaelligkeiten zusammenstellen, Steffen: XP + lokale Admin-Rechte entziehen" | Out-Null
|
||||
Add-HistoryEntry $journalId '2025-11-19' "## 2025-11-19`n`n- AM Termin passt`n- Sophos und Citrix: Marvin war krank, Ticket wieder eroeffnet`n- FW China: Jo hat noch keine Antwort`n- Teilen mit Malaysia: LANdata prueft`n- Makros Hornet durch SMTP-Regel durchlassen`n- Jo: Telekom HKR: 100% ausgelastet 6-18 Uhr, Downloads koennen Teams-Problem verursachen, Jo + Philip schauen sich das an" | Out-Null
|
||||
Add-HistoryEntry $journalId '2025-11-12' "## 2025-11-12`n`n- Entscheidung Lizenz Visio fuer viflow: open vs. e3`n- FW WITEC nach China: internationale Leitung auf China-Seite benoetigt`n- AM: Feedback von Willi benoetigt`n- Bestellung MS Open erledigt`n- SAN: Diskrepanz 7TB / 2TB, hohe Latenzen, Jo meldet sich" | Out-Null
|
||||
Add-HistoryEntry $journalId '2025-11-05' "## 2025-11-05`n`n- Lizenz AM: Jo schickt Lizenz`n- Sophos auf Citrix: Marvin krank, Ticket bei Sophos aufgemacht, laesst sich nicht installieren" | Out-Null
|
||||
|
||||
# ── 5. Upload ─────────────────────────────────────────────────────────────────
|
||||
Write-Host "`n[5] Uploading..." -ForegroundColor Cyan
|
||||
$ulArgs = @('-File', "$scripts\upload.ps1")
|
||||
if ($Prod) { $ulArgs += '-Prod' }
|
||||
if ($Force) { $ulArgs += '-Force' }
|
||||
& powershell @ulArgs
|
||||
if ($LASTEXITCODE -ne 0) { throw "Upload failed" }
|
||||
|
||||
Write-Host "`n[DONE] JF LANdata import complete." -ForegroundColor Green
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Stores a Bearer token for Ka-Note prod in %USERPROFILE%\.ka-note\token.txt.
|
||||
Run this after copying a fresh token from the Ka-Note browser app.
|
||||
|
||||
.PARAMETER Token
|
||||
The JWT token string. If omitted, reads from clipboard.
|
||||
|
||||
.EXAMPLE
|
||||
.\set-token.ps1 "eyJ..."
|
||||
.\set-token.ps1 # reads from clipboard
|
||||
#>
|
||||
param([string]$Token)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if (-not $Token) {
|
||||
$Token = Get-Clipboard
|
||||
if (-not $Token) { Write-Error "No token provided and clipboard is empty."; exit 1 }
|
||||
Write-Host " Read token from clipboard." -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
$Token = $Token.Trim()
|
||||
if (-not $Token.StartsWith('eyJ')) {
|
||||
Write-Error "Does not look like a JWT token (expected 'eyJ...')."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse expiry
|
||||
try {
|
||||
$parts = $Token.Split('.')
|
||||
$payload = $parts[1]
|
||||
$pad = 4 - ($payload.Length % 4); if ($pad -ne 4) { $payload += '=' * $pad }
|
||||
$payload = $payload.Replace('-', '+').Replace('_', '/')
|
||||
$claims = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload)) | ConvertFrom-Json
|
||||
$expUtc = [System.DateTimeOffset]::FromUnixTimeSeconds($claims.exp).UtcDateTime
|
||||
$local = $expUtc.ToLocalTime()
|
||||
$remaining = [math]::Round(($expUtc - [System.DateTime]::UtcNow).TotalMinutes)
|
||||
Write-Host " Token expires: $($local.ToString('HH:mm')) ($remaining min remaining)" -ForegroundColor $(if ($remaining -lt 10) { 'Yellow' } else { 'Green' })
|
||||
} catch {
|
||||
Write-Host " Token stored (could not parse expiry)." -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# Save
|
||||
$dir = Join-Path $env:USERPROFILE '.ka-note'
|
||||
if (-not (Test-Path $dir)) { New-Item -ItemType Directory $dir | Out-Null }
|
||||
$file = Join-Path $dir 'token.txt'
|
||||
$Token | Set-Content $file -Encoding UTF8 -NoNewline
|
||||
Write-Host " Saved to: $file" -ForegroundColor Green
|
||||
Loading…
Reference in New Issue