Release v0.1.1 #1

Merged
kqjy merged 20 commits from next into main 2025-11-22 12:31:27 +00:00
3 changed files with 19 additions and 10 deletions
Showing only changes of commit f2daa8a8a3 - Show all commits

View File

@@ -77,10 +77,20 @@ class IamService:
self._users: Dict[str, Dict[str, Any]] = {}
self._raw_config: Dict[str, Any] = {}
self._failed_attempts: Dict[str, Deque[datetime]] = {}
self._last_load_time = 0.0
self._load()
def _maybe_reload(self) -> None:
"""Reload configuration if the file has changed on disk."""
try:
if self.config_path.stat().st_mtime > self._last_load_time:
self._load()
except OSError:
pass
# ---------------------- authz helpers ----------------------
def authenticate(self, access_key: str, secret_key: str) -> Principal:
self._maybe_reload()
access_key = (access_key or "").strip()
secret_key = (secret_key or "").strip()
if not access_key or not secret_key:
@@ -135,12 +145,14 @@ class IamService:
return int(max(0, self.auth_lockout_window.total_seconds() - elapsed))
def principal_for_key(self, access_key: str) -> Principal:
self._maybe_reload()
record = self._users.get(access_key)
if not record:
raise IamError("Unknown access key")
return self._build_principal(access_key, record)
def secret_for_key(self, access_key: str) -> str:
self._maybe_reload()
record = self._users.get(access_key)
if not record:
raise IamError("Unknown access key")
@@ -245,6 +257,7 @@ class IamService:
# ---------------------- config helpers ----------------------
def _load(self) -> None:
try:
self._last_load_time = self.config_path.stat().st_mtime
content = self.config_path.read_text(encoding='utf-8')
raw = json.loads(content)
except FileNotFoundError:

View File

@@ -926,6 +926,12 @@ def rotate_iam_secret(access_key: str):
return redirect(url_for("ui.iam_dashboard"))
try:
new_secret = _iam().rotate_secret(access_key)
# If rotating own key, update session immediately so subsequent API calls (like presign) work
if principal and principal.access_key == access_key:
creds = session.get("credentials", {})
creds["secret_key"] = new_secret
session["credentials"] = creds
session.modified = True
except IamError as exc:
if request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html:
return jsonify({"error": str(exc)}), 400

View File

@@ -286,9 +286,6 @@
</div>
<div class="modal-body" id="rotateSecretConfirm">
<p>Are you sure you want to rotate the secret key for <strong id="rotateUserLabel"></strong>?</p>
<div id="rotateSelfWarning" class="alert alert-warning d-none">
<strong>Warning:</strong> You are rotating your own secret key. You will need to sign in again with the new key.
</div>
<div class="alert alert-warning mb-0">
The old secret key will stop working immediately. Any applications using it must be updated.
</div>
@@ -474,7 +471,6 @@
const rotateSecretResult = document.getElementById('rotateSecretResult');
const newSecretKeyInput = document.getElementById('newSecretKey');
const copyNewSecretBtn = document.getElementById('copyNewSecret');
const rotateSelfWarning = document.getElementById('rotateSelfWarning');
let currentRotateKey = null;
document.querySelectorAll('[data-rotate-user]').forEach(btn => {
@@ -482,12 +478,6 @@
currentRotateKey = btn.dataset.rotateUser;
rotateUserLabel.textContent = currentRotateKey;
if (currentRotateKey === currentUserKey) {
rotateSelfWarning.classList.remove('d-none');
} else {
rotateSelfWarning.classList.add('d-none');
}
// Reset Modal State
rotateSecretConfirm.classList.remove('d-none');
rotateSecretResult.classList.add('d-none');