The AgenDAV project is dead and doesn't even work with e.g. php-8.x.
Here we remove all support and documentation for it.
* Implement moving of domains.
-* The AgenDAV "user exists" test is wonky, because there's no real
- users in AgenDAV. Right now we check the "username" column in the
- "prefs" table, but all of the shares (and principals?) have URLs
- instead of usernames. We don't parse the URLs, and instead rely
- on doing find/replace of substrings in e.g. AgendavMv.
-
- In particular, this means that AgenDAV pruning does not work! If
- a user with default preferences is deleted, we don't notice.
-
* mailshears --help crashes before doing what it should just do:
$ mailshears --help
# mailshears mv "test1@example.com" "test2@example.com"
mailshears, 2020-01-30 14:40:09 -0500 (Plugin: MvPlugin)
--------------------------------------------------------
- AgendavMv - Would move user test1@example.com (User not found) to .
DavicalMv - Would move user test1@example.com (User not found) to .
DovecotMv - Would move user test1@example.com (/var/spool/mail/vhosts/example.com/test1) to .
PostfixadminMv - Would move user test1@example.com to .
i_mean_business: false
-plugins: [agendav, davical, dovecot, postfixadmin, roundcube]
-
-agendav_dbhost: localhost
-agendav_dbport: 5432
-agendav_dbopts:
-agendav_dbuser: postgres
-agendav_dbpass:
-agendav_dbname: agendav
+plugins: [davical, dovecot, postfixadmin, roundcube]
davical_dbhost: localhost
davical_dbport: 5432
functionality is provided by plugins. The following are supported at
the moment:
\#
-.IP \(bu 2
-Agendav (database, v2.1.x)
.IP \(bu
DAViCal (database, v1.1.x)
.IP \(bu
.I $ mailshears
mailshears, 2015-11-08 17:01:47 -0500 (Plugin: PrunePlugin)
-----------------------------------------------------------
-AgendavPrune - Removed user booger@example.com.
DavicalPrune - Removed user booger@example.com (Principal ID: 2).
DovecotPrune - Removed user booger@example.com (/tmp/mailshears-test/example.com/booger).
DovecotPrune - Removed user jeremy@example.com (/tmp/mailshears-test/example.com/jeremy).
.I $ mailshears rm adam@example.net
mailshears, 2015-11-08 17:04:42 -0500 (Plugin: RmPlugin)
--------------------------------------------------------
-AgendavRm - Removed user adam@example.net.
DavicalRm - User adam@example.net not found.
DovecotRm - Removed user adam@example.net (/tmp/mailshears-test/example.net/adam).
PostfixadminRm - Removed user adam@example.net.
.I $ mailshears rm example.net
mailshears, 2015-11-08 17:05:42 -0500 (Plugin: RmPlugin)
--------------------------------------------------------
-AgendavRm - Removed domain example.net.
DavicalRm - Domain example.net not found.
DovecotRm - Removed domain example.net (/tmp/mailshears-test/example.net).
PostfixadminRm - Removed domain example.net.
.I $ mailshears mv alice@example.com alice@example.net
mailshears, 2015-11-08 17:06:29 -0500 (Plugin: MvPlugin)
--------------------------------------------------------
-AgendavMv - Source user alice@example.com not found.
DavicalMv - Moved user alice@example.com (Principal ID: 1) to alice@example.net (Principal ID: 1).
DovecotMv - Moved user alice@example.com (/tmp/mailshears-test/example.com/alice) to alice@example.net (/tmp/mailshears-test/example.net/alice).
PostfixadminMv - Moved user alice@example.com to alice@example.net.
\#
.IP \(bu
\fIplugins\fR (default: ['postfixadmin'])
-A list of enabled plugins. Valid values are \(dqagendav\(dq,
-\(dqdavical\(dq, \(dqdovecot\(dq, \(dqpostfixadmin\(dq, and
-\(dqroundcube\(dq.
+A list of enabled plugins. Valid values are \(dqdavical\(dq,
+\(dqdovecot\(dq, \(dqpostfixadmin\(dq, and \(dqroundcube\(dq.
.P
The \(dqdovecot\(dq plugin supports the following:
\#
.P
The database plugins all have the same configutation options:
connections settings preceded by the plugin name. So in what follows,
-<plugin> would be replaced by \(dqagendav\(dq, \(dqdavical\(dq, or so
-on. Their meanings should be self-explanatory.
+<plugin> would be replaced by \(dqdavical\(dq, for example. Their
+meanings should be self-explanatory.
\#
.IP \(bu 2
\fI<plugin>_dbhost\fR (default: localhost)
+++ /dev/null
-require 'common/plugin'
-require 'common/user'
-
-# Code that all Agendav plugins ({AgendavPrune}, {AgendavRm},
-# {AgendavMv}) share.
-module AgendavPlugin
-
- # We implement the Plugin "interface."
- include Plugin
-
-
- # Initialize this Agendav {Plugin} with values in *cfg*.
- #
- # @param cfg [Configuration] the configuration for this plugin.
- #
- def initialize(cfg)
- @db_hash = {
- :host => cfg.agendav_dbhost,
- :port => cfg.agendav_dbport,
- :options => cfg.agendav_dbopts,
- :dbname => cfg.agendav_dbname,
- :user => cfg.agendav_dbuser,
- :password => cfg.agendav_dbpass }
- end
-
-
- # Return a list of Agendav users.
- #
- # @return [Array<User>] a list of users contained in the
- # Agendav database.
- #
- def list_users()
- users = []
-
- connection = PG::Connection.new(@db_hash)
-
- # There are also "owner" and "with" fields in the "shares" table,
- # but they contains principal URLs and not a bare username. Thus
- # their format depends on the CalDAV server configuration, and
- # isn't predictable.
- sql_query = 'SELECT username FROM prefs;'
-
- begin
- connection.sync_exec(sql_query) do |result|
- users = result.field_values('username')
- end
- ensure
- # Make sure the connection gets closed even if the query explodes.
- connection.close()
- end
-
- return users.map{ |u| User.new(u) }
- end
-
-
- # Count the number of rows in the "shares" table. Used only for
- # testing.
- #
- # @return [Fixnum] the number of rows in the "shares" table.
- #
- def count_shares()
- count = nil
- connection = PG::Connection.new(@db_hash)
-
- sql_query = 'SELECT count(*) FROM shares;'
- begin
- connection.sync_exec(sql_query) do |result|
- count = result.getvalue(0,0).to_i()
- end
- ensure
- # Make sure the connection gets closed even if the query explodes.
- connection.close()
- end
-
- return count
- end
-
-end
d['i_mean_business'] = false
d['plugins'] = ['postfixadmin']
- d['agendav_dbhost'] = 'localhost'
- d['agendav_dbport'] = 5432
- d['agendav_dbopts'] = ''
- d['agendav_dbuser'] = 'postgres'
- d['agendav_dbpass'] = ''
- d['agendav_dbname'] = 'agendav'
-
d['davical_dbhost'] = 'localhost'
d['davical_dbport'] = 5432
d['davical_dbopts'] = ''
+++ /dev/null
-require 'pg'
-
-require 'common/agendav_plugin'
-require 'mv/mv_plugin'
-
-
-# Handle moving (renaming) Agendav users in its database. Agendav has
-# no concept of domains.
-#
-class AgendavMv
-
- include AgendavPlugin
- include MvPlugin
-
- # Move the user *src* to *dst* within the Agendav database. This
- # should "rename" him in _every_ table where he is referenced.
- #
- # This can fail if *dst* already exists before the move. It should
- # also be an error if the destination domain doesn't exist. But
- # Agendav doesn't know about domains, so we let that slide.
- #
- # If the source user doesn't exist, we do our best. AgenDAV has a
- # "shares" table that isn't keyed on the username, but rather the
- # principal URL. And its "prefs" table doesn't contain entries for
- # users who have default preferences. As a result, we may need to
- # perform some find/replaces in the "shares" table even if no
- # corresponding user exists in the "prefs" table (which is how we
- # tell if a user exists in AgenDAV). Thus it's not a fatal error if
- # the *src* user doesn't exist.
- #
- # @param src [User] the source user to be moved.
- #
- # @param dst [User] the destination user being moved to.
- #
- def mv_user(src, dst)
- raise UserAlreadyExistsError.new(dst.to_s()) if user_exists(dst)
-
- connection = PG::Connection.new(@db_hash)
- begin
- # The "prefs" table uses the normal username as a key...
- # This should be harmless if the source user does not exist.
- sql_query0 = 'UPDATE prefs SET username = $1 WHERE username = $2;'
- connection.sync_exec_params(sql_query0, [dst.to_s(), src.to_s()])
-
- # But the "shares" table uses encoded principal URLs. For the
- # "shares" table, we need to do a find/replace on the username
- # with its "@" symbol translated to a "%40".
- encoded_src = src.to_s()['@'] = '%40'
- encoded_dst = dst.to_s()['@'] = '%40'
-
- # Unlike in the "rm" plugin, we do modify the "calendar" field
- # here. That's because in the usual legitimate use case, the
- # calendar URL will change when a user moves. This will ALSO
- # affect people who name their calendars something like
- # "user%40example.com", but screw those people.
- sql_queries = ['UPDATE shares SET owner=REPLACE(owner, $2, $1);']
- sql_queries << 'UPDATE shares SET calendar=REPLACE(calendar, $2, $1);'
- sql_queries << 'UPDATE shares SET "with"=REPLACE("with", $2, $1);'
-
- sql_queries.each do |sql_query|
- connection.sync_exec_params(sql_query, [encoded_dst, encoded_src])
- end
- ensure
- # Make sure the connection gets closed even if a query explodes.
- connection.close()
- end
- end
-
-end
+++ /dev/null
-require 'pg'
-
-require 'prune/prune_plugin'
-require 'rm/plugins/agendav'
-
-# Handle the pruning of Agendav users from its database. This class
-# doesn't need to do anything; by inheriting from {AgendavRm}, we get
-# its {AgendavRm#remove_user} method and that's all we need to prune.
-#
-class AgendavPrune < AgendavRm
- # Needed for the magic includers <tt>run()</tt> method.
- include PrunePlugin
-end
+++ /dev/null
-require 'pg'
-
-require 'common/agendav_plugin'
-require 'rm/rm_plugin'
-
-
-# Handle the removal of Agendav users from its database. Agendav has
-# no concept of domains.
-#
-class AgendavRm
-
- include AgendavPlugin
- include RmPlugin
-
-
- # Remove *user* from the Agendav database. This should remove him
- # from _every_ table in which he is referenced.
- #
- # We do not raise an error if the user doesn't exist. This is due to
- # an unfortunate problem with the "user exists" check in AgenDAV.
- # The AgenDAV "shares" table is not tied directly to a username, so
- # we are forced to use a regexp match to decide what rows to delete
- # from that table. We do so regardless of whether or not the username
- # exists in the "prefs" table, because that table stores only non-
- # default preferences -- not all users' preferences.
- #
- # @param user [User] the user to remove.
- #
- def remove_user(user)
- sql_queries = ['DELETE FROM prefs WHERE username = $1;']
-
- # The "shares" table contains principal URLs, and the "@" symbol
- # is usually encoded to "%40". These queries do a regex match on
- # the username after replacing the "%40" with a "@".
- #
- # As a precaution, I haven chosen not to delete based on the
- # "calendar" field here. Nobody should have a calendar named
- # "user%40example.com", but it's not impossible -- and we don't
- # want to delete that calendar when the not-necessarily-related
- # "user@example.com" account is removed. And the usual appearance
- # of the user's email address in the "calendar" field happens when
- # he is also the owner, so the calendar does get deleted in the
- # normal situation.
- sql_queries << "DELETE FROM shares WHERE REPLACE(owner,'%40','@') ~ $1;"
- sql_queries << "DELETE FROM shares WHERE REPLACE(\"with\",'%40','@') ~ $1;"
-
- connection = PG::Connection.new(@db_hash)
- begin
- sql_queries.each do |sql_query|
- connection.sync_exec_params(sql_query, [user.to_s()])
- end
- ensure
- # Make sure the connection gets closed even if a query explodes.
- connection.close()
- end
- end
-
-end
mkdir -p /tmp/mailshears-test/example.com/jeremy
mkdir -p /tmp/mailshears-test/example.net/adam
-dropdb --if-exists -U postgres agendav
-createdb -U postgres agendav
-psql -U postgres -d agendav < test/sql/agendav.sql
-psql -U postgres -d agendav < test/sql/agendav-fixtures.sql
-
dropdb --if-exists -U postgres davical
createdb -U postgres davical
psql -U postgres -d davical < test/sql/davical.sql
i_mean_business: true
-plugins: [agendav, davical, dovecot, postfixadmin, roundcube]
-
-agendav_dbhost: localhost
-agendav_dbport: 5432
-agendav_dbopts:
-agendav_dbuser: postgres
-agendav_dbpass:
-agendav_dbname: agendav_test
+plugins: [davical, dovecot, postfixadmin, roundcube]
davical_dbhost: localhost
davical_dbport: 5432
davical_dbpass:
davical_dbname: davical_test
+# Warning: unsafe! Fixed paths under /tmp are asking to be exploited
+# by other users on the system.
dovecot_mail_root: /tmp/mailshears-test
postfixadmin_dbhost: localhost
#
# == Databases ==
#
- # 1. agendav_test
- #
- # +------------------------------+
- # | prefs |
- # +--------------------+---------+
- # | username | options |
- # +--------------------+---------+
- # | adam@example.net | herp |
- # +--------------------+---------+
- # | booger@example.com | herp |
- # +------------------ +---------+
- #
- #
- # +---------------------------------------------------------------------------------------------------------------------+
- # | shares |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- # | sid | owner | calendar | with |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- # | 1 | /caldav.php/adam%40example.net/ | /caldav.php/adam%40example.net/calendar-default | /beth%40example.net/ |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- # | 2 | /caldav.php/booger%40example.com/ | /caldav.php/booger%40example.com/calendar-default | /carol%40example.net/ |
- # +-----+-----------------------------------+---------------------------------------------------+-----------------------+
- #
- #
- # 2. davical_test
+ # 1. davical_test
#
# +--------------------------------------------------------+
# | usr |
# +---------+--------------+----------------+
#
#
- # 3. postfixadmin_test
+ # 2. postfixadmin_test
#
# +-------------+
# | domain |
# | admin@example.com | example.net |
# +-------------------+-------------+
#
- # 4. roundcube_test
+ # 3. roundcube_test
#
#
# +---------+--------------------+
+++ /dev/null
-/* Add an AgenDAV record for one user only. */
-INSERT INTO prefs (username, options) VALUES ('adam@example.net', 'herp');
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/adam%40example.net/',
- '/caldav.php/adam%40example.net/calendar-default',
- '/beth%40example.net/',
- 'a:0:{}',
- false);
-
-/* Just kidding, here's another one! */
-INSERT INTO prefs (username, options) VALUES ('booger@example.com', 'herp');
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/booger%40example.com/',
- '/caldav.php/booger%40example.com/calendar-default',
- '/carol%40example.net/',
- 'a:0:{}',
- true);
-
-/* These two are missing a prefs entry to test the removal of "shares"
- * entries in that situation.
-*/
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/stinky%40example.com/',
- '/caldav.php/stinky%40example.com/calendar-default',
- '/herp%40example.net/',
- 'a:0:{}',
- true);
-
-INSERT INTO shares (owner, calendar, "with", options, rw)
- VALUES ('/caldav.php/goober%40example.com/',
- '/caldav.php/goober%40example.com/calendar-default',
- '/derp%40example.net/',
- 'a:0:{}',
- true);
+++ /dev/null
---
--- PostgreSQL database dump
---
-
-SET statement_timeout = 0;
-SET lock_timeout = 0;
-SET client_encoding = 'UTF8';
-SET standard_conforming_strings = on;
-SET check_function_bodies = false;
-SET client_min_messages = warning;
-SET row_security = off;
-
---
--- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
---
-
-CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
-
-
---
--- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
---
-
-COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
-
-
-SET search_path = public, pg_catalog;
-
-SET default_tablespace = '';
-
-SET default_with_oids = false;
-
---
--- Name: prefs; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE prefs (
- username character varying(255) NOT NULL,
- options text NOT NULL
-);
-
-
-ALTER TABLE prefs OWNER to postgres;
-
---
--- Name: principals; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE principals (
- url character varying(255) NOT NULL,
- displayname character varying(255) NOT NULL,
- email character varying(255) NOT NULL
-);
-
-
-ALTER TABLE principals OWNER to postgres;
-
---
--- Name: schema_versions; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE schema_versions (
- version character varying(255) NOT NULL
-);
-
-
-ALTER TABLE schema_versions OWNER to postgres;
-
---
--- Name: sessions; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE sessions (
- sess_id character varying(128) NOT NULL,
- sess_data bytea NOT NULL,
- sess_lifetime integer NOT NULL,
- sess_time integer NOT NULL
-);
-
-
-ALTER TABLE sessions OWNER to postgres;
-
---
--- Name: shares; Type: TABLE; Schema: public; Owner: agendav
---
-
-CREATE TABLE shares (
- sid integer NOT NULL,
- owner character varying(255) NOT NULL,
- calendar character varying(255) NOT NULL,
- "with" character varying(255) NOT NULL,
- options text NOT NULL,
- rw boolean NOT NULL
-);
-
-
-ALTER TABLE shares OWNER to postgres;
-
---
--- Name: shares_sid_seq; Type: SEQUENCE; Schema: public; Owner: agendav
---
-
-CREATE SEQUENCE shares_sid_seq
- START WITH 1
- INCREMENT BY 1
- NO MINVALUE
- NO MAXVALUE
- CACHE 1;
-
-
-ALTER TABLE shares_sid_seq OWNER to postgres;
-
---
--- Name: shares_sid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: agendav
---
-
-ALTER SEQUENCE shares_sid_seq OWNED BY shares.sid;
-
-
---
--- Name: sid; Type: DEFAULT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY shares ALTER COLUMN sid SET DEFAULT nextval('shares_sid_seq'::regclass);
-
-
---
--- Name: prefs_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY prefs
- ADD CONSTRAINT prefs_pkey PRIMARY KEY (username);
-
-
---
--- Name: principals_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY principals
- ADD CONSTRAINT principals_pkey PRIMARY KEY (url);
-
-
---
--- Name: schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY schema_versions
- ADD CONSTRAINT schema_versions_pkey PRIMARY KEY (version);
-
-
---
--- Name: sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY sessions
- ADD CONSTRAINT sessions_pkey PRIMARY KEY (sess_id);
-
-
---
--- Name: shares_pkey; Type: CONSTRAINT; Schema: public; Owner: agendav
---
-
-ALTER TABLE ONLY shares
- ADD CONSTRAINT shares_pkey PRIMARY KEY (sid);
-
-
---
--- Name: idx_905f717c9890e20e; Type: INDEX; Schema: public; Owner: agendav
---
-
-CREATE INDEX idx_905f717c9890e20e ON shares USING btree ("with");
-
-
---
--- Name: idx_905f717ccf60e67c6ea9a146; Type: INDEX; Schema: public; Owner: agendav
---
-
-CREATE INDEX idx_905f717ccf60e67c6ea9a146 ON shares USING btree (owner, calendar);
-
-
---
--- Name: public; Type: ACL; Schema: -; Owner: postgres
---
-
-REVOKE ALL ON SCHEMA public FROM PUBLIC;
-REVOKE ALL ON SCHEMA public FROM postgres;
-GRANT ALL ON SCHEMA public TO postgres;
-GRANT ALL ON SCHEMA public TO PUBLIC;
-
-
---
--- PostgreSQL database dump complete
---
-
require 'common/domain'
require 'common/user'
require 'mailshears_test'
-require 'mv/plugins/agendav'
require 'mv/plugins/davical'
require 'mv/plugins/dovecot'
require 'mv/plugins/postfixadmin'
actual = output_buffer.string()
expected =
- "AgendavMv - Moved user alice@example.com (User not found) " +
- "to alice@example.net (User not found).\n" +
"DavicalMv - Moved user alice@example.com (Principal ID: 1) " +
"to alice@example.net (Principal ID: 1).\n" +
"DovecotMv - Moved user alice@example.com " +
# Now check the database.
- amv = AgendavMv.new(cfg)
- actual = amv.list_users()
- expected = [User.new('adam@example.net'),User.new('booger@example.com')]
- assert_equal(expected.sort(), actual.sort())
-
dmv = DavicalMv.new(cfg)
actual = dmv.list_users()
expected = [User.new('alice@example.net'), User.new('booger@example.com')]
# Skip output verification, it's ugly. But make sure the database
# has what we expect.
- amv = AgendavMv.new(cfg)
- actual = amv.list_users()
- expected = [User.new('adam@example.net'),User.new('booger@example.com')]
- assert_equal(expected.sort(), actual.sort())
-
dmv = DavicalMv.new(cfg)
actual = dmv.list_users()
expected = [User.new('alice@example.com'), User.new('booger@example.com')]
require 'common/domain'
require 'common/user'
require 'mailshears_test'
-require 'prune/plugins/agendav'
require 'prune/plugins/davical'
require 'prune/plugins/dovecot'
require 'prune/plugins/postfixadmin'
# Both of our tests have the same expected output / results, so
# check them both using the same function.
expected =
- "AgendavPrune - Removed user booger@example.com.\n" +
"DavicalPrune - Removed user booger@example.com (Principal ID: 2).\n" +
"DovecotPrune - Removed user booger@example.com " +
"(#{cfg.dovecot_mail_root()}/example.com/booger).\n" +
# Now make sure the database has what we expect.
- apr = AgendavPrune.new(cfg)
- actual = apr.list_users()
- expected = [User.new('adam@example.net')]
- assert_equal(expected, actual)
-
dpr = DavicalPrune.new(cfg)
actual = dpr.list_users()
expected = [User.new('alice@example.com')]
require 'common/domain'
require 'common/user'
require 'mailshears_test'
-require 'rm/plugins/agendav'
require 'rm/plugins/davical'
require 'rm/plugins/dovecot'
require 'rm/plugins/postfixadmin'
actual = output_buffer.string()
expected =
- "AgendavRm - Removed user adam@example.net.\n" +
"DavicalRm - User adam@example.net not found.\n" +
"DovecotRm - Removed user adam@example.net " +
"(#{cfg.dovecot_mail_root}/example.net/adam).\n" +
# Now make sure the database has what we expect.
- arm = AgendavRm.new(cfg)
- actual = arm.list_users()
- expected = [User.new('booger@example.com')]
- assert_equal(expected, actual)
-
- # Only try to remove this guy from the agendav database, to ensure
- # that "nonexistent" users have their shares removed.
- arm.remove_user('stinky@example.com')
- expected = 2
- actual = arm.count_shares()
- assert_equal(expected, actual)
-
drm = DavicalRm.new(cfg)
actual = drm.list_users()
expected = [User.new('alice@example.com'), User.new('booger@example.com')]
actual = output_buffer.string()
expected =
- "AgendavRm - Removed domain example.net.\n" +
"DavicalRm - Domain example.net not found.\n" +
"DovecotRm - Removed domain example.net " +
"(#{cfg.dovecot_mail_root}/example.net).\n" +
# Now make sure the database has what we expect.
- arm = AgendavRm.new(cfg)
- actual = arm.list_users()
- expected = [User.new('booger@example.com')]
- assert_equal(expected, actual)
-
drm = DavicalRm.new(cfg)
actual = drm.list_users()
expected = [User.new('alice@example.com'), User.new('booger@example.com')]