URL: https://github.com/freeipa/freeipa/pull/1946
Author: Rezney
 Title: #1946: [Backport][ipa-4-6] Test webui realm domains
Action: opened

PR body:
"""
This PR was opened automatically because PR #1724 was pushed to master and 
backport to ipa-4-6 is required.
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/1946/head:pr1946
git checkout pr1946
From d2dd6389ff1ed6ece4319b286b8d99e42ea450ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20=C4=8Cech?= <pc...@redhat.com>
Date: Thu, 1 Mar 2018 13:29:18 +0100
Subject: [PATCH] webui:tests: Add tests for realmd domains

This patch expands WebUI testing on realmd domains
page. The added tests are:
  test_add_single_labeled_domain
  test_dnszone_del_hooked_to_realmdomains_mod
  test_dns_reversezone_add_hooked_to_realmdomains_mod
  test_dnszone_add_hooked_to_realmdomains_mod
  test_del_domain_of_ipa_server_bug1035286
  test_add_non_dns_configured_domain_positive
  test_add_non_dns_configured_domain_negative
  test_del_domain_with_force_update
  test_del_domain_and_update
  test_del_domain_and_refresh
  test_del_domain_revert
  test_del_domain_undo_all
  test_del_domain_undo
  test_add_domain_and_update
  test_add_domain_with_trailing_space
  test_add_domain_with_leading_space
  test_add_empty_domain
  test_add_duplicate_domaini
  test_add_domain_and_revert
  test_add_domain_and_refresh
  test_add_domain_and_undo_all
  test_add_domain_and_undo
  test_add_domain_with_special_char
---
 ipatests/test_webui/test_realmdomains.py | 631 ++++++++++++++++++++++++++++++-
 ipatests/test_webui/ui_driver.py         |  12 +
 2 files changed, 622 insertions(+), 21 deletions(-)

diff --git a/ipatests/test_webui/test_realmdomains.py b/ipatests/test_webui/test_realmdomains.py
index 56fd791ec6..08aba2df5d 100644
--- a/ipatests/test_webui/test_realmdomains.py
+++ b/ipatests/test_webui/test_realmdomains.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # Authors:
 #   Petr Vobornik <pvobo...@redhat.com>
 #
@@ -19,12 +20,16 @@
 
 """
 Realm domains tests
+
+Update means Check DNS in WebUI.
+Force udpate means Force Update in WebUI.
 """
 
 from ipatests.test_webui.ui_driver import UI_driver
 from ipatests.test_webui.ui_driver import screenshot
 from ipatests.test_webui.data_dns import (
-    ZONE_ENTITY, ZONE_DATA, ZONE_PKEY, ZONE_DEFAULT_FACET
+    ZONE_ENTITY, FORWARD_ZONE_ENTITY, ZONE_DATA, FORWARD_ZONE_DATA,
+    ZONE_PKEY, FORWARD_ZONE_PKEY, ZONE_DEFAULT_FACET
 )
 import pytest
 
@@ -41,26 +46,10 @@ def del_realm_domain(self, realmdomain, button):
         self.wait_for_request()
         self.close_notifications()
 
-    @screenshot
-    def test_read(self):
+    def prepare_dns_zone(self, realmdomain):
         """
-        Realm domains mod tests
+        Prepare dns zone record for realmdomain
         """
-        self.init_app()
-        self.navigate_to_entity(ENTITY)
-
-        # add with force - skipping DNS check
-        self.add_multivalued('associateddomain', 'itest.bar')
-        self.facet_button_click('save')
-        self.dialog_button_click('force')
-        self.wait_for_request()
-        self.close_notifications()
-
-        # delete
-        self.del_realm_domain('itest.bar', 'force')
-        self.wait_for_request()
-
-        # Try adding and deleting with "Check DNS" (in html 'ok' button)
 
         # DNS check expects that the added domain will have DNS record:
         #    TXT _kerberos.$domain "$REALM"
@@ -74,7 +63,6 @@ def test_read(self):
         self.navigate_to_entity(ZONE_ENTITY)
         self.add_record(ZONE_ENTITY, ZONE_DATA)
 
-        realmdomain = ZONE_PKEY.strip('.')
         realm = self.config.get('ipa_realm')
 
         # remove the added domain from Realm Domain
@@ -97,14 +85,615 @@ def test_read(self):
         self.add_record(ZONE_ENTITY, DNS_RECORD_ADD_DATA,
                         facet=ZONE_DEFAULT_FACET, navigate=False)
 
+    def _add_associateddomain(self, values, force=False):
+        """
+        Add values to associated domains and click OK or Force
+        """
+        for val in values:
+            self.add_multivalued('associateddomain', val)
+        self.facet_button_click('save')
+        self.dialog_button_click('force' if force else 'ok')
+        self.wait_for_request()
+        self.close_notifications()
+
+    @screenshot
+    def test_read(self):
+        """
+        Realm domains mod tests
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        # add with force - skipping DNS check
+        self._add_associateddomain(['itest.bar'], force=True)
+        self.close_notifications()
+
+        # delete
+        self.del_realm_domain('itest.bar', 'force')
+        self.wait_for_request()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
         # add Realm Domain and Check DNS
         self.navigate_to_entity(ENTITY)
-        self.add_multivalued('associateddomain', realmdomain)
+        self._add_associateddomain([realmdomain])
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_add_single_labeled_domain(self):
+        """
+        Add domain with special_character
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        domain_with_special_char = u'ipa@123#'
+
+        # add with force - skipping DNS check
+        self._add_associateddomain([domain_with_special_char], force=True)
+        dialog = self.get_last_error_dialog()
+        assert ("invalid 'domain': single label domains are not supported"
+                in dialog.text)
+
+    @screenshot
+    def test_add_domain_with_special_char(self):
+        """
+        Add domain with special_character
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        domain_with_special_char = u'ipa@123#.com'
+
+        # add with force - skipping DNS check
+        self._add_associateddomain([domain_with_special_char], force=True)
+        dialog = self.get_last_error_dialog()
+        assert ("invalid 'domain': only letters, numbers, '-' are allowed. "
+                "DNS label may not start or end with '-'"
+                in dialog.text)
+
+    @screenshot
+    def test_add_domain_and_undo(self):
+        """
+        Add domain and undo
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        test_domain = u'itest.bar'
+
+        # add and undo
+        self.add_multivalued('associateddomain', test_domain)
+        self.undo_multivalued('associateddomain', test_domain)
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert test_domain not in domains
+
+    @screenshot
+    def test_add_domain_and_undo_all(self):
+        """
+        Add domain and undo all
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        test_domain = u'itest.bar'
+
+        # add and undo all
+        self.add_multivalued('associateddomain', test_domain)
+        self.undo_all_multivalued('associateddomain')
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert test_domain not in domains
+
+    @screenshot
+    def test_add_domain_and_update(self):
+        """
+        Add domain and update
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add Realm Domain and Check DNS
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_add_domain_and_refresh(self):
+        """
+        Add domain and refresh
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        test_domain = u'itest.bar'
+
+        # add and refresh
+        self.add_multivalued('associateddomain', test_domain)
+        self.facet_button_click('refresh')
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert test_domain not in domains
+
+    @screenshot
+    def test_add_domain_and_revert(self):
+        """
+        Add domain and revert
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        test_domain = u'itest.bar'
+
+        # add and revert
+        self.add_multivalued('associateddomain', test_domain)
+        self.facet_button_click('revert')
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert test_domain not in domains
+
+    @screenshot
+    def test_add_duplicate_domain(self):
+        """
+        Add duplicate domain
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        self.navigate_to_entity(ENTITY)
+
+        # add two (same) domains with force - skipping DNS check
+        self._add_associateddomain([realmdomain, realmdomain], force=True)
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'force')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_add_empty_domain(self):
+        """
+        Add empty domain
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        # add with force - skipping DNS check
+        self._add_associateddomain([''], force=True)
+
+        # check
+        dialog = self.get_last_error_dialog()
+        assert ("no modifications to be performed" in dialog.text)
+
+    @screenshot
+    def test_add_domain_with_leading_space(self):
+        """
+        Add domain with leading space
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        # add with force - skipping DNS check
+        self._add_associateddomain([' ipa.test'], force=True)
+
+        # check
+        dialog = self.get_last_error_dialog()
+        assert ("invalid 'domain': Leading and trailing spaces are not allowed"
+                in dialog.text)
+
+    @screenshot
+    def test_add_domain_with_trailing_space(self):
+        """
+        Add domain with trailing space
+        """
+        self.init_app()
+        self.navigate_to_entity(ENTITY)
+
+        # add with force - skipping DNS check
+        self._add_associateddomain(['ipa.test '], force=True)
+
+        # check
+        dialog = self.get_last_error_dialog()
+        assert ("invalid 'domain': Leading and trailing spaces are not allowed"
+                in dialog.text)
+
+    @screenshot
+    def test_del_domain_undo(self):
+        """
+        Undo after deleting existing domain
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        # check that domain is present
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # delete and undo
+        self.navigate_to_entity(ENTITY)
+        self.del_multivalued('associateddomain', realmdomain)
+        self.undo_multivalued('associateddomain', realmdomain)
+
+        # check that domain is present
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_del_domain_undo_all(self):
+        """
+        Undo all after deleting existing domain
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        # check that domain is present
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # delete and undo
+        self.navigate_to_entity(ENTITY)
+        self.del_multivalued('associateddomain', realmdomain)
+        self.undo_all_multivalued('associateddomain')
+
+        # check that domain is present
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_del_domain_revert(self):
+        """
+        Revert after deleting existing domain
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        # del and revert
+        self.navigate_to_entity(ENTITY)
+        self.del_multivalued('associateddomain', realmdomain)
+        self.facet_button_click('revert')
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_del_domain_and_refresh(self):
+        """
+        Delete domain and refresh
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        self.navigate_to_entity(ENTITY)
+
+        # delete
+        self.del_multivalued('associateddomain', realmdomain)
+        self.facet_button_click('refresh')
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_del_domain_and_update(self):
+        """
+        Delete and update
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        self.navigate_to_entity(ENTITY)
+
+        # delete
+        self.del_multivalued('associateddomain', realmdomain)
         self.facet_button_click('save')
         self.dialog_button_click('ok')
         self.wait_for_request()
+        self.close_notifications()
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain not in domains
+
+        # cleanup
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_del_domain_with_force_update(self):
+        """
+        Delete and force update
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        # add
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        self.navigate_to_entity(ENTITY)
+
+        # force delete
+        self.del_multivalued('associateddomain', realmdomain)
+        self.facet_button_click('save')
+        self.dialog_button_click('force')
+        self.wait_for_request()
+        self.close_notifications()
+
+        # check
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain not in domains
+
+        # cleanup
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_add_non_dns_configured_domain_negative(self):
+        """
+        Domain shouldn't be added after:
+        1) add DNS non configured domain
+        2) click update
+        3) check DNS
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        dialog = self.get_last_error_dialog()
+        assert ("invalid 'domain': DNS zone for each realmdomain must contain "
+                "SOA or NS records. "
+                "No records found for: " + realmdomain
+                in dialog.text)
+
+    @screenshot
+    def test_add_non_dns_configured_domain_positive(self):
+        """
+        Domain should be added fter:
+        1) add DNS non configured domain
+        2) click update
+        3) click force
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain], force=True)
+
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        # cleanup
+        self.del_realm_domain(realmdomain, 'ok')
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_del_domain_of_ipa_server_bug1035286(self):
+        """
+        Error should occur when:
+        1) delete Domain of Ipa server
+        2) select update
+        3) select force update
+        4) click "Cancel"
+        """
+        self.init_app()
+
+        ipadomain = self.config.get('ipa_domain')
+
+        realmdomain = ZONE_PKEY.strip('.')
+        self.prepare_dns_zone(realmdomain)
+
+        self.navigate_to_entity(ENTITY)
+        self._add_associateddomain([realmdomain])
+
+        self.navigate_to_entity(ENTITY)
+
+        self.del_multivalued('associateddomain', ipadomain)
+        self.facet_button_click('save')
+        self.dialog_button_click('force')
+        self.wait_for_request()
+
+        dialog = self.get_last_error_dialog()
+        assert ("invalid 'realmdomain list': "
+                "IPA server domain cannot be omitted" in dialog.text)
+        self.dialog_button_click('cancel')
+        self.facet_button_click('refresh')
 
         # cleanup
         self.del_realm_domain(realmdomain, 'ok')
         self.navigate_to_entity(ZONE_ENTITY)
         self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_dnszone_add_hooked_to_realmdomains_mod(self):
+        """
+        DNSZone is hooked to realmdomains:
+        1) Navigate Identity >> DNS
+        2) Add Dnszone (newdom.com)
+        3) go to DNS Resource Records(DNS Zone >> newdom.com)
+        4) verify TXT record is exists
+        5) navigate Identity >> RealmDomain
+        6) verify newly added domain (newdom.com) exists in realmdomain list
+        7) Delete domain (newdom.com) from realmdomain list
+        8) go to DNS Resource Records(DNS Zone >> newdom.com)
+        9) verify TXT record is not exists
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+        realm = self.config.get('ipa_realm')
+
+        # add DNS domain
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.add_record(ZONE_ENTITY, ZONE_DATA)
+        self.assert_record(ZONE_PKEY)
+
+        self.navigate_to_record(ZONE_PKEY)
+        self.assert_record('_kerberos')
+        self.assert_record_value('TXT', '_kerberos', 'type')
+        self.assert_record_value(realm, '_kerberos', 'data')
+
+        self.navigate_to_entity(ENTITY)
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        self.del_multivalued('associateddomain', realmdomain)
+        self.facet_button_click('save')
+        self.dialog_button_click('ok')
+        self.wait_for_request()
+
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.assert_record(ZONE_PKEY)
+
+        self.navigate_to_record(ZONE_PKEY)
+        self.facet_button_click('refresh')
+        self.assert_record('_kerberos', negative=True)
+
+        # cleanup
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+    @screenshot
+    def test_dns_reversezone_add_hooked_to_realmdomains_mod(self):
+        """
+        Reverse DNS domain is not automatically add domain to the list of
+        domain associated with IPA realm
+        1) Navigate Identity >> DNS
+        2) Add Dns Reverse Zone (222.65.10.in-addr.arpa.)
+        3) navigate Identity >> RealmDomain
+        4) verify newly added domain (222.65.10.in-addr.arpa.) is not exists
+           in realmdomain list
+        """
+        self.init_app()
+
+        realmdomain = FORWARD_ZONE_PKEY.strip('.')
+
+        # add DNS Reverse zone
+        self.navigate_to_entity(FORWARD_ZONE_ENTITY)
+        self.add_record(FORWARD_ZONE_ENTITY, FORWARD_ZONE_DATA)
+        self.assert_record(FORWARD_ZONE_PKEY)
+
+        self.navigate_to_entity(ENTITY)
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain not in domains
+
+        # cleanup
+        self.navigate_to_entity(FORWARD_ZONE_ENTITY)
+        self.delete_record(FORWARD_ZONE_PKEY)
+
+    @screenshot
+    def test_dnszone_del_hooked_to_realmdomains_mod(self):
+        """
+        ipa dnszone-del also removes the entry from realmdomains list
+        1) Navigate Identity >> DNS
+        2) Add Dnszone (newdom.com)
+        3) navigate Identity >> RealmDomain
+        4) verify newly added domain (newdom.com) exists in realmdomain list
+        7) Navigate Identity >> DNS
+        8) Delete Dnszone(newdom.com)
+        9) navigate Identity >> RealmDomain
+        10) verify domain (newdom.com) is not exists in realmdomain list
+        """
+        self.init_app()
+
+        realmdomain = ZONE_PKEY.strip('.')
+
+        # add DNS domain
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.add_record(ZONE_ENTITY, ZONE_DATA)
+        self.assert_record(ZONE_PKEY)
+
+        self.navigate_to_entity(ENTITY)
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain in domains
+
+        self.navigate_to_entity(ZONE_ENTITY)
+        self.delete_record(ZONE_PKEY)
+
+        self.navigate_to_entity(ENTITY)
+        domains = self.get_multivalued_value('associateddomain')
+        assert realmdomain not in domains
diff --git a/ipatests/test_webui/ui_driver.py b/ipatests/test_webui/ui_driver.py
index 822604dd0a..32a45f4470 100644
--- a/ipatests/test_webui/ui_driver.py
+++ b/ipatests/test_webui/ui_driver.py
@@ -879,6 +879,18 @@ def del_multivalued(self, name, value, parent=None):
 
         assert clicked, 'Value was not removed: %s' % value
 
+    def undo_all_multivalued(self, name, parent=None):
+        """
+        Undo all new values to multivalued textbox
+        """
+        if parent is None:
+            parent = self.get_form()
+        label = "div[name='{}'].multivalued-widget".format(name)
+        widget = self.find(label, By.CSS_SELECTOR, parent, strict=True)
+        add_btn = self.find("button[name=undo_all]", By.CSS_SELECTOR, widget,
+                            strict=True)
+        add_btn.click()
+
     def fill_multivalued(self, name, instructions, parent=None):
         """
         Add or delete a value from multivalued field
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedoraproject.org/archives/list/freeipa-devel@lists.fedorahosted.org/message/C762CJB3IRXUPMCB54HQZ4OMQAXDPAWL/

Reply via email to