Centralize evaluation of console accessibility.

This commit is contained in:
Michael Lipp 2025-03-05 13:02:00 +01:00
parent 7437a17c9f
commit 2524172c12
5 changed files with 85 additions and 66 deletions

View file

@ -524,6 +524,13 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
.assignedTo(user)).get().stream().findFirst();
}
/**
* Returns the permissions from the VM definition.
*
* @param vmDef the VM definition
* @param session the session
* @return the sets the
*/
private Set<Permission> permissions(VmDefinition vmDef, Session session) {
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
@ -532,6 +539,13 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
return vmDef.permissionsFor(user, roles);
}
/**
* Returns the permissions from the pool.
*
* @param pool the pool
* @param session the session
* @return the sets the
*/
private Set<Permission> permissions(VmPool pool, Session session) {
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
@ -540,23 +554,33 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
return pool.permissionsFor(user, roles);
}
private Set<Permission> permissions(ResourceModel model, Session session,
VmPool pool, VmDefinition vmDef) throws InterruptedException {
/**
* Returns the permissions from the VM definition or the pool depending
* on the state of the model.
*
* @param session the session
* @param model the model
* @param vmDef the vm def
* @return the sets the
* @throws InterruptedException the interrupted exception
*/
private Set<Permission> permissions(Session session, ResourceModel model,
VmDefinition vmDef) throws InterruptedException {
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
var roles = WebConsoleUtils.rolesFromSession(session)
.stream().map(ConsoleRole::getName).toList();
if (model.mode() == ResourceModel.Mode.POOL) {
if (pool == null) {
pool = appPipeline.fire(new GetPools()
.withName(model.name())).get().stream().findFirst()
.orElse(null);
}
// Use permissions from pool
var pool = appPipeline.fire(new GetPools().withName(model.name()))
.get().stream().findFirst().orElse(null);
if (pool == null) {
return Collections.emptySet();
}
return pool.permissionsFor(user, roles);
}
// Use permissions from VM
if (vmDef == null) {
vmDef = appPipeline.fire(new GetVms().assignedFrom(model.name())
.assignedTo(user)).get().stream().map(VmData::definition)
@ -578,7 +602,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
VmDefinition vmDef) throws InterruptedException {
channel.respond(new NotifyConletView(type(),
model.getConletId(), "updateConfig", model.mode(), model.name(),
permissions(model, channel.session(), null, vmDef).stream()
permissions(channel.session(), model, vmDef).stream()
.map(VmDefinition.Permission::toString).toList()));
}
@ -589,12 +613,17 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
model.setAssignedVm(null);
} else {
model.setAssignedVm(vmDef.name());
var session = channel.session();
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
var perms = permissions(session, model, vmDef);
try {
data = Map.of("metadata",
Map.of("namespace", vmDef.namespace(),
data = Map.of(
"metadata", Map.of("namespace", vmDef.namespace(),
"name", vmDef.name()),
"spec", vmDef.spec(),
"status", vmDef.status());
"status", vmDef.status(),
"consoleAccessible", vmDef.consoleAccessible(user, perms));
} catch (JsonSyntaxException e) {
logger.log(Level.SEVERE, e,
() -> "Failed to serialize VM definition");
@ -635,6 +664,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
// Update known conlets
for (var entry : conletIdsByConsoleConnection().entrySet()) {
var connection = entry.getKey();
var user = WebConsoleUtils.userFromSession(connection.session())
.map(ConsoleUser::getName).orElse(null);
for (var conletId : entry.getValue()) {
var model = stateFromSession(connection.session(), conletId);
if (model.isEmpty()
@ -655,9 +686,6 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
} else {
// Check if VM is used by pool conlet or to be assigned to
// it
var user
= WebConsoleUtils.userFromSession(connection.session())
.map(ConsoleUser::getName).orElse(null);
var toBeUsedByConlet = vmDef.assignment()
.map(Assignment::pool)
.map(p -> p.equals(model.get().name())).orElse(false)
@ -752,7 +780,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
var vmChannel = vmData.get().channel();
var vmDef = vmData.get().definition();
var vmName = vmDef.metadata().getName();
var perms = permissions(model, channel.session(), null, vmDef);
var perms = permissions(channel.session(), model, vmDef);
var resourceBundle = resourceBundle(channel.locale());
switch (event.method()) {
case "start":
@ -776,9 +804,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
}
break;
case "openConsole":
if (perms.contains(VmDefinition.Permission.ACCESS_CONSOLE)) {
openConsole(channel, model, vmChannel, vmDef, perms);
}
openConsole(channel, model, vmChannel, vmDef, perms);
break;
default:// ignore
break;

View file

@ -1,6 +1,6 @@
/*
* VM-Operator
* Copyright (C) 2024 Michael N. Lipp
* Copyright (C) 2024,2025 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@ -71,12 +71,10 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
const poolName = computed(() => previewApi.poolName);
const vmName = computed(() => previewApi.vmDefinition.name);
const configured = computed(() => previewApi.vmDefinition.spec);
const accessible = computed(() => previewApi.vmDefinition.consoleAccessible);
const busy = computed(() => previewApi.vmDefinition.spec
&& (previewApi.vmDefinition.spec.vm.state === 'Running'
&& ((previewApi.poolName
&& previewApi.vmDefinition.userLoginRequested)
? !previewApi.vmDefinition.userLoggedIn
: !previewApi.vmDefinition.running)
&& (!previewApi.vmDefinition.consoleAccessible)
|| previewApi.vmDefinition.spec.vm.state === 'Stopped'
&& previewApi.vmDefinition.running));
const startable = computed(() => previewApi.vmDefinition.spec
@ -88,8 +86,6 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
previewApi.vmDefinition.spec.vm.state !== 'Stopped'
&& previewApi.vmDefinition.running);
const running = computed(() => previewApi.vmDefinition.running);
const userLoginRequested = computed(() => previewApi.vmDefinition.userLoginRequested);
const userLoggedIn = computed(() => previewApi.vmDefinition.userLoggedIn);
const inUse = computed(() => previewApi.vmDefinition.usedBy != '');
const permissions = computed(() => previewApi.permissions);
const osicon = computed(() => {
@ -125,8 +121,8 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
};
return { localize, resourceBase, vmAction, poolName, vmName,
configured, busy, startable, stoppable, running, userLoggedIn,
userLoginRequested, inUse, permissions, osicon };
configured, accessible, busy, startable, stoppable, running,
inUse, permissions, osicon };
},
template: `
<table>
@ -134,10 +130,7 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
<tr>
<td rowspan="2" style="position: relative"><span
style="position: absolute;" :class="{ busy: busy }"
><img role=button :aria-disabled="
((poolName && userLoginRequested)
? !userLoggedIn : !running)
|| !permissions.includes('accessConsole')"
><img role=button :aria-disabled="!accessible"
v-on:click="vmAction('openConsole')"
:src="resourceBase + (running
? (inUse ? 'computer-in-use.svg' : 'computer.svg')
@ -214,16 +207,11 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
vmDefinition.currentRam = Number(vmDefinition.status.ram);
vmDefinition.usedBy = vmDefinition.status.consoleClient || "";
// safety fallbacks
vmDefinition.userLoginRequested = true;
vmDefinition.userLoggedIn = false;
vmDefinition.status.conditions.forEach((condition: any) => {
if (condition.type === "Running") {
vmDefinition.running = condition.status === "True";
vmDefinition.runningConditionSince
= new Date(condition.lastTransitionTime);
} else if (condition.type === "UserLoggedIn") {
vmDefinition.userLoggedIn = condition.status === "True";
vmDefinition.userLoginRequested = condition.reason !== "NotRequested";
}
})
} else {