From e9700a04c2985a001949a8802ec9c0e2f8919df5 Mon Sep 17 00:00:00 2001 From: Semyon Klintsov Date: Tue, 14 Jul 2026 09:27:47 +0300 Subject: [PATCH 1/4] Allow interaction autorization --- .../dbus/RemoteInvocationHandler.java | 4 ++++ .../MethodAllowInteractiveAutorization.java | 22 +++++++++++++++++++ .../dbus/messages/constants/Flags.java | 7 +++--- 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 dbus-java-core/src/main/java/org/freedesktop/dbus/annotations/MethodAllowInteractiveAutorization.java 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() { From 9d0d53624023d7017e431818a6435e269ffdea2a Mon Sep 17 00:00:00 2001 From: Semyon Klintsov Date: Wed, 15 Jul 2026 20:23:57 +0300 Subject: [PATCH 2/4] Allow interaction autorization: documentation added --- .../systemd/SystemdUnitsManagment.java | 70 +++++++++++++++++++ src/site/markdown/remote-objects.md | 33 ++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/systemd/SystemdUnitsManagment.java 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..425136ba4 --- /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("httpd.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/src/site/markdown/remote-objects.md b/src/site/markdown/remote-objects.md index 4dc1d9c28..a50211ff9 100644 --- a/src/site/markdown/remote-objects.md +++ b/src/site/markdown/remote-objects.md @@ -40,4 +40,35 @@ data is handled completely transparently. *Note:* The one issue that this does have is that the method calls are all blocking. If the remote application has an issue, your application will stall -until the method call times out. \ No newline at end of file +until the method call times out. + +## Controlling Remote Method Call Behavior + +When calling remote D-Bus methods, you can influence how the call is handled by using annotations on the methods declared in your interface. These annotations allow you to set specific flags in the D-Bus message header. + +### Available Annotations + +* **`@MethodNoReply`** + Method does not return replies or error replies, even if it is of a type that can have a reply. + ```java + @MethodNoReply + int add(int _a, int _b); + ``` + Returns null even for the arguments from the example above. + +* **`@MethodAllowInteractiveAutorization`** + This annotation signals the D-Bus daemon that the caller is ready to wait for interactive authorization (e.g., Polkit password prompts). It is useful when unprivileged code calls a privileged method, and an authorization framework that supports user interaction is in place. + + As an example, let's take a call to a remote Systemd manager object method that requires elevated privileges to execute. Use [the code from the example](https://github.com/hypfvieh/dbus-java/tree/master/dbus-java-examples/src/main/java/com/github/hypfvieh/dbus/examples/systemd/SystemdUnitsManagment.java): + ```java + @DBusMemberName(value = "StartUnit") + @MethodAllowInteractiveAutorization + DBusPath startUnit(String name, String mode); + + @DBusMemberName(value = "StopUnit") + @MethodAllowInteractiveAutorization + DBusPath stopUnit(String name, String mode); + ``` + After calling the method, the user authorization window is guaranteed to be displayed if it needed. + +*Note:* More info about this you can find [here](https://dbus.freedesktop.org/doc/dbus-specification.html). \ No newline at end of file From 48f4f24eb4349663e66723080413755e1c53e1a0 Mon Sep 17 00:00:00 2001 From: Semyon Klintsov Date: Wed, 15 Jul 2026 20:45:44 +0300 Subject: [PATCH 3/4] Allow interaction autorization: tests added --- .../org/freedesktop/dbus/test/MethodTest.java | 28 +++++++++++++++++++ .../dbus/test/helper/SampleClass.java | 8 +++++- .../RemoteInteractiveInterface.java | 13 +++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/interfaces/RemoteInteractiveInterface.java 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, Boolean> show(A _in) { logger.debug("Showing Stuff: {} ({})", _in.getClass(), _in); diff --git a/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/interfaces/RemoteInteractiveInterface.java b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/interfaces/RemoteInteractiveInterface.java new file mode 100644 index 000000000..86b3cd4db --- /dev/null +++ b/dbus-java-tests/src/test/java/org/freedesktop/dbus/test/helper/interfaces/RemoteInteractiveInterface.java @@ -0,0 +1,13 @@ +package org.freedesktop.dbus.test.helper.interfaces; + +import org.freedesktop.dbus.annotations.MethodAllowInteractiveAutorization; +import org.freedesktop.dbus.interfaces.DBusInterface; +import org.freedesktop.dbus.messages.constants.Flags; + +/** + * Interface for testing {@link Flags#ALLOW_INTERACTIVE_AUTHORIZATION} flag. + */ +public interface RemoteInteractiveInterface extends DBusInterface { + @MethodAllowInteractiveAutorization + void interactiveMethod(); +} From 7fcddba3ae4fe350bf4824e95857e46ab243287f Mon Sep 17 00:00:00 2001 From: Semyon <158846161+unfamiliarS@users.noreply.github.com> Date: Wed, 15 Jul 2026 20:51:18 +0300 Subject: [PATCH 4/4] In SystemdUnitsManagment.java example code replace real unit name on 'your' --- .../hypfvieh/dbus/examples/systemd/SystemdUnitsManagment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 425136ba4..09c7a88fd 100644 --- 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 @@ -51,7 +51,7 @@ public static void main(String[] _args) throws DBusException { SimpleSystemdManagerInterface.class ); - System.out.println(sysdManager.stopUnit("httpd.service", "replace")); + System.out.println(sysdManager.stopUnit("your.service", "replace")); } }