diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java
index 40d2137c2..af7e5b106 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java
@@ -1,6 +1,7 @@
package org.freedesktop.dbus;
import org.freedesktop.dbus.annotations.DBusBoundProperty;
+import org.freedesktop.dbus.annotations.MethodAllowInteractiveAutorization;
import org.freedesktop.dbus.annotations.MethodNoReply;
import org.freedesktop.dbus.connections.AbstractConnection;
import org.freedesktop.dbus.errors.NoReply;
@@ -196,6 +197,9 @@ public static Object executeRemoteMethod(boolean _methodCall, final RemoteObject
if (_m.isAnnotationPresent(MethodNoReply.class)) {
flags |= Flags.NO_REPLY_EXPECTED;
}
+ if (_m.isAnnotationPresent(MethodAllowInteractiveAutorization.class)) {
+ flags |= Flags.ALLOW_INTERACTIVE_AUTHORIZATION;
+ }
try {
String name = DBusNamingUtil.getMethodName(_m);
if (null == _ro.getInterface()) {
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/annotations/MethodAllowInteractiveAutorization.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/annotations/MethodAllowInteractiveAutorization.java
new file mode 100644
index 000000000..d6c78e701
--- /dev/null
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/annotations/MethodAllowInteractiveAutorization.java
@@ -0,0 +1,22 @@
+package org.freedesktop.dbus.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Methods allow interactive autorization
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@DBusInterfaceName("org.freedesktop.DBus.Method.AllowInteractiveAutorization")
+public @interface MethodAllowInteractiveAutorization {
+
+ /**
+ * Annotation value, true by default
+ *
+ * @return true when a method allow interactive autorization, false otherwise
+ */
+ boolean value() default true;
+}
diff --git a/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/constants/Flags.java b/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/constants/Flags.java
index 1b5a38e5e..56a570ec6 100644
--- a/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/constants/Flags.java
+++ b/dbus-java-core/src/main/java/org/freedesktop/dbus/messages/constants/Flags.java
@@ -5,9 +5,10 @@
* @since 5.0.0 - 2023-10-23
*/
public final class Flags {
- public static final byte NO_REPLY_EXPECTED = 0x01;
- public static final byte NO_AUTO_START = 0x02;
- public static final byte ASYNC = 0x40;
+ public static final byte NO_REPLY_EXPECTED = 0x01;
+ public static final byte NO_AUTO_START = 0x02;
+ public static final byte ALLOW_INTERACTIVE_AUTHORIZATION = 0x04;
+ public static final byte ASYNC = 0x40;
private Flags() {
diff --git a/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/systemd/SystemdUnitsManagment.java b/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/systemd/SystemdUnitsManagment.java
new file mode 100644
index 000000000..09c7a88fd
--- /dev/null
+++ b/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/systemd/SystemdUnitsManagment.java
@@ -0,0 +1,70 @@
+package com.github.hypfvieh.dbus.examples.systemd;
+
+import org.freedesktop.dbus.DBusPath;
+import org.freedesktop.dbus.annotations.DBusInterfaceName;
+import org.freedesktop.dbus.annotations.DBusMemberName;
+import org.freedesktop.dbus.annotations.MethodAllowInteractiveAutorization;
+import org.freedesktop.dbus.connections.impl.DBusConnection;
+import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder;
+import org.freedesktop.dbus.exceptions.DBusException;
+import org.freedesktop.dbus.interfaces.DBusInterface;
+
+/**
+ * Demonstrates how to use {@link MethodAllowInteractiveAutorization} to call
+ * systemd methods that may require Polkit authentication.
+ *
+ * In this example, we connect to the systemd Manager interface and call + * {@code StartUnit} or {@code StopUnit}. These methods typically require + * administrative privileges and may trigger an interactive Polkit dialog + * when called by an unprivileged user. + *
+ *+ * The {@link MethodAllowInteractiveAutorization} annotation tells the D-Bus + * daemon that the caller is prepared to wait for interactive authorization + * (e.g., password prompt). Without this annotation, such calls may fail + * with a {@code DBusExecutionException: Interactive authentication required}. + *
+ *+ * The interface {@link SimpleSystemdManagerInterface} uses the + * {@link DBusInterfaceName} annotation to specify the D-Bus interface name, + * and {@link DBusMemberName} to map Java method names to D-Bus method names. + *
+ * + * @author unfamiliarS + * + * @see MethodAllowInteractiveAutorization + * @see DBusInterfaceName + * @see DBusMemberName + * + * @since 2026-07-15 + */ +public final class SystemdUnitsManagment { + private SystemdUnitsManagment() {} + + public static void main(String[] _args) throws DBusException { + + try (DBusConnection sessionConnection = DBusConnectionBuilder.forSystemBus().build()) { + + SimpleSystemdManagerInterface sysdManager = sessionConnection.getRemoteObject( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + SimpleSystemdManagerInterface.class + ); + + System.out.println(sysdManager.stopUnit("your.service", "replace")); + + } + } + + @DBusInterfaceName("org.freedesktop.systemd1.Manager") + interface SimpleSystemdManagerInterface extends DBusInterface { + + @DBusMemberName(value = "StartUnit") + @MethodAllowInteractiveAutorization + DBusPath startUnit(String _name, String _mode); + + @DBusMemberName(value = "StopUnit") + @MethodAllowInteractiveAutorization + DBusPath stopUnit(String _name, String _mode); + } +} diff --git a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MethodTest.java b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MethodTest.java index e72c1de41..83c0d2026 100644 --- a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MethodTest.java +++ b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/MethodTest.java @@ -2,12 +2,15 @@ import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.Marshalling; +import org.freedesktop.dbus.annotations.MethodAllowInteractiveAutorization; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.test.helper.SampleException; import org.freedesktop.dbus.test.helper.callbacks.handler.CallbackHandlerImpl; +import org.freedesktop.dbus.test.helper.interfaces.RemoteInteractiveInterface; import org.freedesktop.dbus.test.helper.interfaces.SampleRemoteInterface; import org.junit.jupiter.api.Test; +import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; @@ -95,4 +98,29 @@ public void testCallWithCallback() throws DBusException, InterruptedException { assertEquals(0, cbWhichWorks.getTestErrorCalls()); assertEquals(1, cbWhichThrows.getTestErrorCalls()); } + + @Test + public void testInteractiveAutorizationFlag() throws DBusException, NoSuchMethodException { + logger.debug("Testing @MethodAllowInteractiveAutorization"); + + // Test if annotation is present + Method method = RemoteInteractiveInterface.class.getMethod("interactiveMethod"); + assertTrue(method.isAnnotationPresent(MethodAllowInteractiveAutorization.class), + "Method should have @MethodAllowInteractiveAutorization annotation"); + + // Test if annotation value is true + MethodAllowInteractiveAutorization annotation = + method.getAnnotation(MethodAllowInteractiveAutorization.class); + assertTrue(annotation.value(), "Default value should be true"); + + RemoteInteractiveInterface tri = (RemoteInteractiveInterface) clientconn.getPeerRemoteObject(getTestBusName(), getTestObjectPath()); + + // Try to invoke annotated method + try { + tri.interactiveMethod(); + logger.debug("Method with @MethodAllowInteractiveAutorization called successfully"); + } catch (Exception _ex) { + fail("Method with @MethodAllowInteractiveAutorization should not throw: " + _ex.getMessage()); + } + } } diff --git a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/SampleClass.java b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/SampleClass.java index e5c079219..83adcb495 100644 --- a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/SampleClass.java +++ b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/SampleClass.java @@ -10,6 +10,7 @@ import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.interfaces.DBusInterface; import org.freedesktop.dbus.interfaces.Properties; +import org.freedesktop.dbus.test.helper.interfaces.RemoteInteractiveInterface; import org.freedesktop.dbus.test.helper.interfaces.SampleNewInterface; import org.freedesktop.dbus.test.helper.interfaces.SampleRemoteInterface; import org.freedesktop.dbus.test.helper.interfaces.SampleRemoteInterface2; @@ -26,7 +27,7 @@ import java.util.*; @SuppressWarnings({"checkstyle:methodname"}) -public class SampleClass implements SampleRemoteInterface, SampleRemoteInterface2, SampleRemoteInterfaceEnum, Properties { +public class SampleClass implements SampleRemoteInterface, SampleRemoteInterface2, SampleRemoteInterfaceEnum, Properties, RemoteInteractiveInterface { private final transient Logger logger = LoggerFactory.getLogger(getClass()); @@ -92,6 +93,11 @@ public void waitawhile() { logger.debug("Done sleeping."); } + @Override + public void interactiveMethod() { + logger.debug("testInteractiveMethod called"); + } + @Override public SampleTuple