diff options
-rw-r--r-- | src/mcd-account-manager.c | 2 | ||||
-rw-r--r-- | src/mcd-account-priv.h | 2 | ||||
-rw-r--r-- | src/mcd-account.c | 127 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 1 | ||||
-rw-r--r-- | tests/twisted/account-manager/backend-makes-changes.py | 3 | ||||
-rw-r--r-- | tests/twisted/account-manager/restricted-storage.py | 165 | ||||
-rw-r--r-- | tests/twisted/constants.py | 6 | ||||
-rw-r--r-- | tests/twisted/dbus-account-plugin.c | 30 | ||||
-rw-r--r-- | tests/twisted/fakeaccountsservice.py | 7 |
9 files changed, 257 insertions, 86 deletions
diff --git a/src/mcd-account-manager.c b/src/mcd-account-manager.c index cd104611..c8af4164 100644 --- a/src/mcd-account-manager.c +++ b/src/mcd-account-manager.c @@ -333,7 +333,7 @@ toggled_cb (GObject *plugin, const gchar *name, gboolean on, gpointer data) } _mcd_account_set_enabled (account, on, FALSE, - MCD_DBUS_PROP_SET_FLAG_NONE, &error); + MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE, &error); if (error != NULL) { diff --git a/src/mcd-account-priv.h b/src/mcd-account-priv.h index 649efa63..cf682f25 100644 --- a/src/mcd-account-priv.h +++ b/src/mcd-account-priv.h @@ -152,8 +152,6 @@ G_GNUC_INTERNAL gboolean _mcd_account_check_request_real (McdAccount *account, GHashTable *request, GError **error); -G_GNUC_INTERNAL gboolean _mcd_account_get_always_on (McdAccount *self); - G_GNUC_INTERNAL void _mcd_account_set_changing_presence (McdAccount *self, gboolean value); G_GNUC_INTERNAL gboolean _mcd_account_set_enabled (McdAccount *account, diff --git a/src/mcd-account.c b/src/mcd-account.c index 98300b83..de4abd16 100644 --- a/src/mcd-account.c +++ b/src/mcd-account.c @@ -158,7 +158,6 @@ struct _McdAccountPrivate gboolean loaded; gboolean has_been_online; gboolean removed; - gboolean always_on; gboolean changing_presence; gboolean setting_avatar; gboolean waiting_for_initial_avatar; @@ -183,7 +182,6 @@ enum PROP_CONNECTIVITY_MONITOR, PROP_STORAGE, PROP_NAME, - PROP_ALWAYS_ON, }; enum @@ -1135,6 +1133,9 @@ get_has_been_online (TpSvcDBusProperties *self, const gchar *name, g_value_set_boolean (value, priv->has_been_online); } +static TpStorageRestrictionFlags mcd_account_get_storage_restrictions ( + McdAccount *account); + /** * mcd_account_set_enabled: * @account: the #McdAccount @@ -1153,19 +1154,21 @@ _mcd_account_set_enabled (McdAccount *account, { McdAccountPrivate *priv = account->priv; - if (priv->always_on && !enabled) - { - g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s cannot be disabled", - priv->unique_name); - return FALSE; - } - if (priv->enabled != enabled) { GValue value = G_VALUE_INIT; const gchar *name = mcd_account_get_unique_name (account); + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow changing " + "its Enabled property", name); + return FALSE; + } + if (!enabled && priv->connection != NULL) _mcd_connection_request_presence (priv->connection, TP_CONNECTION_PRESENCE_TYPE_OFFLINE, @@ -1693,6 +1696,16 @@ set_automatic_presence (TpSvcDBusProperties *self, return FALSE; } + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow changing " + "its presence", priv->unique_name); + return FALSE; + } + DEBUG ("setting automatic presence: %d, %s, %s", type, status, message); if (priv->auto_presence_type != type) @@ -1774,18 +1787,24 @@ set_connect_automatically (TpSvcDBusProperties *self, connect_automatically = g_value_get_boolean (value); - if (priv->always_on && !connect_automatically) - { - g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s always connects automatically", - priv->unique_name); - return FALSE; - } - if (priv->connect_automatically != connect_automatically) { const gchar *account_name = mcd_account_get_unique_name (account); + /* We use CANNOT_SET_PRESENCE to control access to + * ConnectAutomatically, because RequestedPresence is not stored, + * but it can be derived from ConnectAutomatically and + * AutomaticPresence */ + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE) != 0) + { + g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, + "Storage plugin for %s does not allow changing " + "its ConnectAutomatically property", account_name); + return FALSE; + } + if (!(flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE)) { mcd_storage_set_attribute (priv->storage, account_name, @@ -1927,10 +1946,13 @@ set_requested_presence (TpSvcDBusProperties *self, status = g_value_get_string (va->values + 1); message = g_value_get_string (va->values + 2); - if (priv->always_on && !_presence_type_is_online (type)) + if ((flags & MCD_DBUS_PROP_SET_FLAG_ALREADY_IN_STORAGE) == 0 && + (mcd_account_get_storage_restrictions (account) & + TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PRESENCE) != 0) { g_set_error (error, TP_ERROR, TP_ERROR_PERMISSION_DENIED, - "Account %s cannot be taken offline", priv->unique_name); + "Storage plugin for %s does not allow changing " + "its presence", priv->unique_name); return FALSE; } @@ -2137,22 +2159,24 @@ get_storage_specific_info (TpSvcDBusProperties *self, g_value_take_boxed (value, storage_specific_info); } +static TpStorageRestrictionFlags +mcd_account_get_storage_restrictions (McdAccount *self) +{ + McpAccountStorage *storage_plugin = get_storage_plugin (self); + + g_return_val_if_fail (storage_plugin != NULL, 0); + + return mcp_account_storage_get_restrictions (storage_plugin, + self->priv->unique_name); +} + static void get_storage_restrictions (TpSvcDBusProperties *self, const gchar *name, GValue *value) { - TpStorageRestrictionFlags flags; - McdAccount *account = MCD_ACCOUNT (self); - McpAccountStorage *storage_plugin = get_storage_plugin (account); - g_value_init (value, G_TYPE_UINT); - - g_return_if_fail (storage_plugin != NULL); - - flags = mcp_account_storage_get_restrictions (storage_plugin, - account->priv->unique_name); - - g_value_set_uint (value, flags); + g_value_set_uint (value, + mcd_account_get_storage_restrictions (MCD_ACCOUNT (self))); } static const McdDBusProp account_properties[] = { @@ -3235,15 +3259,11 @@ mcd_account_setup (McdAccount *account) priv->object_path = g_strconcat (TP_ACCOUNT_OBJECT_PATH_BASE, name, NULL); - if (!priv->always_on) - { - priv->enabled = - mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_ENABLED); + priv->enabled = mcd_storage_get_boolean (storage, name, + MC_ACCOUNTS_KEY_ENABLED); - priv->connect_automatically = - mcd_storage_get_boolean (storage, name, - MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY); - } + priv->connect_automatically = mcd_storage_get_boolean (storage, name, + MC_ACCOUNTS_KEY_CONNECT_AUTOMATICALLY); priv->has_been_online = mcd_storage_get_boolean (storage, name, MC_ACCOUNTS_KEY_HAS_BEEN_ONLINE); @@ -3393,19 +3413,6 @@ set_property (GObject *obj, guint prop_id, priv->unique_name = g_value_dup_string (val); break; - case PROP_ALWAYS_ON: - priv->always_on = g_value_get_boolean (val); - - if (priv->always_on) - { - priv->enabled = TRUE; - priv->connect_automatically = TRUE; - priv->req_presence_type = priv->auto_presence_type; - priv->req_presence_status = g_strdup (priv->auto_presence_status); - priv->req_presence_message = g_strdup (priv->auto_presence_message); - } - - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; @@ -3657,13 +3664,6 @@ mcd_account_class_init (McdAccountClass * klass) NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property - (object_class, PROP_ALWAYS_ON, - g_param_spec_boolean ("always-on", "Always on?", "Always on?", - FALSE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - /* Signals */ _mcd_account_signals[VALIDITY_CHANGED] = g_signal_new ("validity-changed", @@ -3705,7 +3705,6 @@ mcd_account_init (McdAccount *account) priv->curr_presence_status = g_strdup ("offline"); priv->curr_presence_message = g_strdup (""); - priv->always_on = FALSE; priv->always_dispatch = FALSE; priv->enabled = FALSE; priv->connect_automatically = FALSE; @@ -5131,14 +5130,6 @@ _mcd_account_set_connection_context (McdAccount *self, } gboolean -_mcd_account_get_always_on (McdAccount *self) -{ - g_return_val_if_fail (MCD_IS_ACCOUNT (self), FALSE); - - return self->priv->always_on; -} - -gboolean _mcd_account_needs_dispatch (McdAccount *self) { g_return_val_if_fail (MCD_IS_ACCOUNT (self), FALSE); diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index d62c521d..7aef6493 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -19,6 +19,7 @@ TWISTED_BASIC_TESTS = \ account-manager/recover-from-disconnect.py \ account-manager/req-conn-fails.py \ account-manager/request-online.py \ + account-manager/restricted-storage.py \ account-manager/service.py \ account-manager/update-parameters.py \ account-requests/cancel.py \ diff --git a/tests/twisted/account-manager/backend-makes-changes.py b/tests/twisted/account-manager/backend-makes-changes.py index 30bb0ef8..a4caeac0 100644 --- a/tests/twisted/account-manager/backend-makes-changes.py +++ b/tests/twisted/account-manager/backend-makes-changes.py @@ -47,7 +47,8 @@ def test(q, bus, mc, fake_accounts_service=None, **kwargs): {'account': 'ezio@firenze.fic', 'password': 'nothing is true'}, {}, - {'account': 0, 'password': cs.PARAM_SECRET}]), + {'account': 0, 'password': cs.PARAM_SECRET}, + 0]), EventPattern('dbus-signal', path=cs.AM_PATH, signal='AccountValidityChanged', diff --git a/tests/twisted/account-manager/restricted-storage.py b/tests/twisted/account-manager/restricted-storage.py new file mode 100644 index 00000000..bf01a2cc --- /dev/null +++ b/tests/twisted/account-manager/restricted-storage.py @@ -0,0 +1,165 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009-2012 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus.service + +from servicetest import EventPattern, call_async, assertEquals +from mctest import (exec_test, SimulatedConnection, + SimulatedConnectionManager, Account) +import constants as cs + +def test(q, bus, mc, fake_accounts_service=None, **kwargs): + simulated_cm = SimulatedConnectionManager(q, bus) + + for enabled in (True, False): + for online in (True, False): + account_tail = ('fakecm/fakeprotocol/ezio_2efirenze_40fic' + + (enabled and '_enabled' or '_disabled') + + (online and '_online' or '_offline')) + account_path = cs.ACCOUNT_PATH_PREFIX + account_tail + + try_to_connect = [ + EventPattern('dbus-method-call', method='RequestConnection', + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + predicate=lambda e: + e.args[1]['account'] == account_tail, + handled=False) + ] + + if enabled and online: + also_expected = try_to_connect[:] + else: + also_expected = [] + q.forbid_events(try_to_connect) + + args = ( + { + 'Enabled': enabled, + 'ConnectAutomatically': online, + 'manager': 'fakecm', + 'protocol': 'fakeprotocol', + 'AutomaticPresence': + dbus.Struct((dbus.UInt32(cs.PRESENCE_HIDDEN), + 'hidden', 'press X to blend'), + signature='uss'), + }, + { + 'Enabled': 0, + 'ConnectAutomatically': 0, + 'manager': 0, + 'protocol': 0, + 'AutomaticPresence': 0, + }, + { + 'account': account_tail, + 'password': 'nothing is true' + }, + {}, # untyped parameters + { + 'account': 0, + 'password': cs.PARAM_SECRET + }, + cs.StorageRestrictionFlags.CANNOT_SET_PRESENCE | + cs.StorageRestrictionFlags.CANNOT_SET_ENABLED, + ) + + fake_accounts_service.create_account(account_tail, *args) + + events = q.expect_many( + EventPattern('dbus-signal', + path=cs.TEST_DBUS_ACCOUNT_SERVICE_PATH, + signal='AccountCreated', + args=[account_tail] + list(args)), + EventPattern('dbus-signal', + path=cs.AM_PATH, + signal='AccountValidityChanged', + args=[account_path, True]), + *also_expected) + account = Account(bus, account_path) + + if enabled and online: + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', + account_tail.replace('/', '_'), 'ezio', + has_presence=True) + q.dbus_return(events[-1].message, conn.bus_name, + conn.object_path, signature='so') + q.expect('dbus-method-call', method='SetPresence', + # the fake CM doesn't support 'hidden' by default + args=['busy', 'press X to blend']) + + requested_presence = (dbus.UInt32(cs.PRESENCE_HIDDEN), 'hidden', + 'press X to blend') + else: + requested_presence = (dbus.UInt32(cs.PRESENCE_OFFLINE), + 'offline', '') + + call_async(q, account.Properties, 'Get', cs.ACCOUNT, + 'RequestedPresence') + q.expect('dbus-return', method='Get', value=(requested_presence,)) + + # changes that are not really changes are fine + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'Enabled', enabled) + q.expect('dbus-return', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'ConnectAutomatically', online) + q.expect('dbus-return', method='Set') + + # changes that actually change the restrictive properties + # are not allowed + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'RequestedPresence', + ((dbus.UInt32(cs.PRESENCE_AVAILABLE), 'available', + 'highly conspicuous'))) + q.expect('dbus-error', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'AutomaticPresence', + ((dbus.UInt32(cs.PRESENCE_AVAILABLE), 'available', + 'highly conspicuous'))) + q.expect('dbus-error', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'Enabled', not enabled) + q.expect('dbus-error', method='Set') + call_async(q, account.Properties, 'Set', cs.ACCOUNT, + 'ConnectAutomatically', not online) + q.expect('dbus-error', method='Set') + + # ... but the backend can still change them + if enabled and online: + q.forbid_events(try_to_connect) + fake_accounts_service.update_attributes(account_tail, + { + 'Enabled': False, + 'ConnectAutomatically': False, + }) + q.expect('dbus-method-call', method='Disconnect') + q.unforbid_events(try_to_connect) + else: + q.unforbid_events(try_to_connect) + fake_accounts_service.update_attributes(account_tail, + { + 'Enabled': True, + 'ConnectAutomatically': True, + }) + q.expect_many(*try_to_connect) + +if __name__ == '__main__': + exec_test(test, {}, pass_kwargs=True) diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py index f06b4968..2a4b69b8 100644 --- a/tests/twisted/constants.py +++ b/tests/twisted/constants.py @@ -620,3 +620,9 @@ TEST_DBUS_ACCOUNT_SERVICE_IFACE = TEST_DBUS_ACCOUNT_SERVICE TEST_DBUS_ACCOUNT_PLUGIN_PATH = TESTSLASH + "DBusAccountPlugin" TEST_DBUS_ACCOUNT_PLUGIN_IFACE = TESTDOT + "DBusAccountPlugin" + +class StorageRestrictionFlags(object): + CANNOT_SET_PARAMETERS = 1 + CANNOT_SET_ENABLED = 2 + CANNOT_SET_PRESENCE = 4 + CANNOT_SET_SERVICE = 8 diff --git a/tests/twisted/dbus-account-plugin.c b/tests/twisted/dbus-account-plugin.c index eb727a3d..3cf29ca1 100644 --- a/tests/twisted/dbus-account-plugin.c +++ b/tests/twisted/dbus-account-plugin.c @@ -55,6 +55,7 @@ typedef struct { /* set of strings */ GHashTable *uncommitted_parameters; enum { UNCOMMITTED_CREATION, UNCOMMITTED_DELETION } flags; + TpStorageRestrictionFlags restrictions; } Account; static void @@ -267,7 +268,8 @@ test_dbus_account_plugin_add_account (TestDBusAccountPlugin *self, GVariant *attribute_flags, GVariant *parameters, GVariant *untyped_parameters, - GVariant *param_flags) + GVariant *param_flags, + TpStorageRestrictionFlags restrictions) { GVariantIter iter; const gchar *k; @@ -306,6 +308,8 @@ test_dbus_account_plugin_add_account (TestDBusAccountPlugin *self, g_hash_table_insert (account->parameter_flags, g_strdup (k), GUINT_TO_POINTER (u)); + account->restrictions = restrictions; + return account; } @@ -320,10 +324,12 @@ test_dbus_account_plugin_process_account_creation (TestDBusAccountPlugin *self, GVariant *untyped_params; GVariant *attr_flags; GVariant *param_flags; + guint32 restrictions; - g_variant_get (args, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su})", + g_variant_get (args, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su}u)", &account_name, &attrs, &attr_flags, - ¶ms, &untyped_params, ¶m_flags); + ¶ms, &untyped_params, ¶m_flags, + &restrictions); DEBUG ("%s", account_name); account = lookup_account (self, account_name); @@ -339,7 +345,7 @@ test_dbus_account_plugin_process_account_creation (TestDBusAccountPlugin *self, * a lot of rubbish */ account = test_dbus_account_plugin_add_account (self, account_name, attrs, attr_flags, - params, untyped_params, param_flags); + params, untyped_params, param_flags, restrictions); mcp_account_storage_emit_created ( MCP_ACCOUNT_STORAGE (self), account_name); @@ -686,8 +692,8 @@ account_created_cb (GDBusConnection *bus, TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); const gchar *account_name; - g_variant_get (tuple, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su})", - &account_name, NULL, NULL, NULL, NULL, NULL); + g_variant_get (tuple, "(&s@a{sv}@a{su}@a{sv}@a{ss}@a{su}u)", + &account_name, NULL, NULL, NULL, NULL, NULL, NULL); DEBUG ("%s", account_name); g_queue_push_tail (&self->events, event_new (EVENT_CREATION, tuple)); @@ -765,6 +771,7 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, GVariantIter account_iter; const gchar *account_name; GList *ret = NULL; + guint32 restrictions; DEBUG ("called"); @@ -823,7 +830,7 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, TEST_DBUS_ACCOUNT_SERVICE_IFACE, "GetAccounts", NULL, /* no parameters */ - G_VARIANT_TYPE ("(a{s(a{sv}a{su}a{sv}a{ss}a{su})})"), + G_VARIANT_TYPE ("(a{s(a{sv}a{su}a{sv}a{ss}a{su}u)})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, /* no cancellable */ @@ -846,13 +853,13 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, g_variant_iter_init (&account_iter, accounts); while (g_variant_iter_loop (&account_iter, - "{s(@a{sv}@a{su}@a{sv}@a{ss}@a{su})}", &account_name, + "{s(@a{sv}@a{su}@a{sv}@a{ss}@a{su}u)}", &account_name, &attributes, &attribute_flags, - ¶meters, &untyped_parameters, ¶m_flags)) + ¶meters, &untyped_parameters, ¶m_flags, &restrictions)) { test_dbus_account_plugin_add_account (self, account_name, attributes, attribute_flags, parameters, untyped_parameters, - param_flags); + param_flags, restrictions); ret = g_list_prepend (ret, g_strdup (account_name)); } @@ -1570,8 +1577,7 @@ test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) return 0; - /* FIXME: actually enforce this restriction */ - return TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_SERVICE; + return account->restrictions; } static gboolean diff --git a/tests/twisted/fakeaccountsservice.py b/tests/twisted/fakeaccountsservice.py index 8459f60f..d8c87a0b 100644 --- a/tests/twisted/fakeaccountsservice.py +++ b/tests/twisted/fakeaccountsservice.py @@ -33,8 +33,9 @@ class FakeAccount(object): self.params = dbus.Dictionary({}, signature='sv') self.untyped_params = dbus.Dictionary({}, signature='ss') self.param_flags = dbus.Dictionary({}, signature='su') + self.restrictions = 0 - SIGNATURE = 'a{sv}a{su}a{sv}a{ss}a{su}' + SIGNATURE = 'a{sv}a{su}a{sv}a{ss}a{su}u' def to_dbus(self): return ( @@ -43,6 +44,7 @@ class FakeAccount(object): self.params, self.untyped_params, self.param_flags, + dbus.UInt32(self.restrictions), ) class FakeAccountsService(object): @@ -81,12 +83,13 @@ class FakeAccountsService(object): method='UpdateParameters') def create_account(self, account, attrs={}, attr_flags={}, params={}, - untyped_params={}, param_flags={}): + untyped_params={}, param_flags={}, restrictions=0): if account in self.accounts: raise KeyError('Account %s already exists' % account) self.accounts[account] = FakeAccount() + self.accounts[account].restrictions = restrictions self.accounts[account].attrs.update(attrs) for attr in attrs: self.accounts[account].attr_flags[attr] = dbus.UInt32(0) |