We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Changing primary email address should change email address used for list subscriptions
## What kind of an issue is this?

- Bug report


## What is the expected behaviour?

If I change my primary email address I expect the mailing list services to be updated with my new address, whilst removing my old address.

## What is the current behaviour?

The old address is not removed and the new address is not added.


## How do you reproduce this problem? 

* (Add a new email to an account) (_Only required if there is no alternative email added to the account already_)
* Set the new email address as the primary
* _Notice that the action happens immediately_
    * (Vs. unsubscribing / subscribing to a list, which queries the mailing list service to add/remove addresses.)
* Verify that the old primary address is still on the list
* Verify that the new primary address is absent from the list

## Other information

The settings page tells the user that the primary address is the one used for mailing lists:

> **Adressen som er markert som primær brukes til alle våre e-postlister**, gjenoppretning av passord og andre varsler som sendes fra oss.

The [`set_primary`](https://github.com/dotkom/onlineweb4/blob/v4.24.28/apps/profiles/views.py#L326) function does not handle subscribing/unsubscribing to a list. (Neither does the [`Email model`](https://github.com/dotkom/onlineweb4/blob/v4.24.28/apps/authentication/models.py#L278) nor any signals attached to it.)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `onlineweb4/settings/gsuite.py`
Content:
```
1 import os
2 
3 from decouple import config
4 
5 from .base import PROJECT_ROOT_DIRECTORY
6 
7 OW4_GSUITE_CREDENTIALS_FILENAME = config('OW4_GSUITE_CREDENTIALS_FILENAME', default='gsuitecredentials.json')
8 OW4_GSUITE_CREDENTIALS_PATH = config('OW4_GSUITE_CREDENTIALS_PATH',
9                                      default=os.path.join(PROJECT_ROOT_DIRECTORY, OW4_GSUITE_CREDENTIALS_FILENAME))
10 
11 OW4_GSUITE_SETTINGS = {
12     'CREDENTIALS': OW4_GSUITE_CREDENTIALS_PATH,
13     'DOMAIN': config('OW4_GSUITE_SYNC_DOMAIN', default='online.ntnu.no'),
14     # DELEGATED_ACCOUNT: G Suite Account with proper permissions to perform insertions and removals.
15     'DELEGATED_ACCOUNT': config('OW4_GSUITE_DELEGATED_ACCOUNT', default=''),
16     'ENABLED': config('OW4_GSUITE_ENABLED', cast=bool, default=False),
17 }
18 
19 OW4_GSUITE_ACCOUNTS = {
20     'ENABLED': config('OW4_GSUITE_ACCOUNTS_ENABLED', cast=bool, default=False),
21     'ENABLE_INSERT': config('OW4_GSUITE_ACCOUNTS_ENABLE_INSERT', cast=bool, default=False),
22 }
23 
24 OW4_GSUITE_SYNC = {
25     'CREDENTIALS': OW4_GSUITE_SETTINGS.get('CREDENTIALS'),
26     'DOMAIN': OW4_GSUITE_SETTINGS.get('DOMAIN'),
27     'DELEGATED_ACCOUNT': OW4_GSUITE_SETTINGS.get('DELEGATED_ACCOUNT'),
28     'ENABLED': config('OW4_GSUITE_SYNC_ENABLED', cast=bool, default=False),
29     'ENABLE_INSERT': config('OW4_GSUITE_SYNC_ENABLE_INSERT', cast=bool, default=False),
30     'ENABLE_DELETE': config('OW4_GSUITE_SYNC_ENABLE_DELETE', cast=bool, default=False),
31     # OW4 name (lowercase) -> G Suite name (lowercase)
32     'GROUPS': {
33         'appkom': 'appkom',
34         'arrkom': 'arrkom',
35         'bankom': 'bankom',
36         'bedkom': 'bedkom',
37         'dotkom': 'dotkom',
38         'ekskom': 'ekskom',
39         'fagkom': 'fagkom',
40         'fond': 'fond',
41         'hovedstyret': 'hovedstyret',
42         'jubkom': 'jubkom',
43         'prokom': 'prokom',
44         'seniorkom': 'seniorkom',
45         'trikom': 'trikom',
46         'tillitsvalgte': 'tillitsvalgte',
47         'redaksjonen': 'redaksjonen',
48         'ekskom': 'ekskom',
49         'itex' : 'itex',
50         'velkom': 'velkom',
51         'interessegrupper': 'interessegrupper',
52         'online-il': 'online-il',
53         'techtalks' : 'techtalks'
54     }
55 }
56
```

Path: `apps/authentication/signals.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 import logging
3 import uuid
4 
5 from django.conf import settings
6 from django.contrib.auth import get_user_model
7 from django.contrib.auth.models import Group
8 from django.db.models.signals import m2m_changed, post_save, pre_delete
9 from django.dispatch import receiver
10 
11 from apps.authentication.models import GroupMember, OnlineGroup
12 from apps.authentication.tasks import SynchronizeGroups
13 from apps.gsuite.mail_syncer.main import update_g_suite_group, update_g_suite_user
14 
15 User = get_user_model()
16 logger = logging.getLogger("syncer.%s" % __name__)
17 sync_uuid = uuid.uuid1()
18 
19 
20 def run_group_syncer(user):
21     """
22     Tasks to run after User is changed.
23     :param user: The user instance to sync groups for.
24     :type user: OnlineUser
25     :return: None
26     """
27     SynchronizeGroups.run()
28     if settings.OW4_GSUITE_SYNC.get("ENABLED", False):
29         ow4_gsuite_domain = settings.OW4_GSUITE_SYNC.get("DOMAIN")
30         if isinstance(user, User):
31             logger.debug("Running G Suite syncer for user {}".format(user))
32             update_g_suite_user(ow4_gsuite_domain, user, suppress_http_errors=True)
33         elif isinstance(user, Group):
34             group = user
35             logger.debug("Running G Suite syncer for group {}".format(group))
36             update_g_suite_group(
37                 ow4_gsuite_domain, group.name, suppress_http_errors=True
38             )
39 
40 
41 @receiver(post_save, sender=Group)
42 def trigger_group_syncer(sender, instance, created=False, **kwargs):
43     """
44     :param sender: The model that triggered this hook
45     :param instance: The model instance triggering this hook
46     :param created: True if the instance was created, False if the instance was updated
47 
48     Calls the SynchronizeGroups Task if a group is updated. (Not if it's the initial creation of a group)
49     """
50     global sync_uuid
51 
52     if created:
53         # If a new instance is created, we do not need to trigger group sync.
54         pass
55     else:
56         # If sync is triggered by adding a user to group or a group to a user
57         # then we need to detach the signal hook listening to m2m changes on
58         # those models as they will trigger a recursive call to this method.
59         if sender == User.groups.through:
60             logger.debug(
61                 "Disconnect m2m_changed signal hook with uuid %s before synchronizing groups"
62                 % sync_uuid
63             )
64             if m2m_changed.disconnect(sender=sender, dispatch_uid=sync_uuid):
65                 logger.debug("Signal with uuid %s disconnected" % sync_uuid)
66                 run_group_syncer(instance)
67 
68             sync_uuid = uuid.uuid1()
69             logger.debug(
70                 "m2m_changed signal hook reconnected with uuid: %s" % sync_uuid
71             )
72             m2m_changed.connect(
73                 receiver=trigger_group_syncer,
74                 dispatch_uid=sync_uuid,
75                 sender=User.groups.through,
76             )
77         else:
78             run_group_syncer(instance)
79 
80 
81 m2m_changed.connect(
82     trigger_group_syncer, dispatch_uid=sync_uuid, sender=User.groups.through
83 )
84 
85 
86 @receiver(post_save, sender=GroupMember)
87 def add_online_group_member_to_django_group(
88     sender, instance: GroupMember, created=False, **kwargs
89 ):
90     online_group: OnlineGroup = instance.group
91     group: Group = online_group.group
92     user: User = instance.user
93     if user not in group.user_set.all():
94         group.user_set.add(user)
95 
96 
97 @receiver(pre_delete, sender=GroupMember)
98 def remove_online_group_members_from_django_group(
99     sender, instance: GroupMember, **kwargs
100 ):
101     online_group: OnlineGroup = instance.group
102     group: Group = online_group.group
103     user: User = instance.user
104     if user in group.user_set.all():
105         group.user_set.remove(user)
106
```

Path: `apps/gsuite/mail_syncer/signals.py`
Content:
```
1 import logging
2 
3 from django.contrib.auth import get_user_model
4 from django.db.models.signals import pre_save
5 from django.dispatch import receiver
6 
7 from .tasks import update_mailing_list
8 
9 User = get_user_model()
10 
11 MAILING_LIST_USER_FIELDS_TO_LIST_NAME = {"infomail": "info", "jobmail": "oppdrag"}
12 
13 logger = logging.getLogger(__name__)
14 
15 
16 def get_updated_mailing_list_fields(user):
17     updated_mailing_lists = []
18     try:
19         # Get the current user and find out what's about to change
20         current_user = User.objects.get(pk=user.pk)
21         if user.infomail != current_user.infomail:
22             updated_mailing_lists.append("infomail")
23         if user.jobmail != current_user.jobmail:
24             updated_mailing_lists.append("jobmail")
25     except User.DoesNotExist:
26         # Find out which mailing lists are opted into if the user did not previously exist
27         for mailing_list in MAILING_LIST_USER_FIELDS_TO_LIST_NAME.keys():
28             if getattr(user, mailing_list, False):
29                 updated_mailing_lists.append(mailing_list)
30 
31     return updated_mailing_lists
32 
33 
34 @receiver(pre_save, sender=User)
35 def toggle_mailing_lists(sender, instance, **kwargs):
36     update_fields = get_updated_mailing_list_fields(instance)
37 
38     if update_fields:
39         for mailing_list in MAILING_LIST_USER_FIELDS_TO_LIST_NAME.keys():
40             if mailing_list not in update_fields:
41                 # Skips toggle if mailing list field not changed.
42                 continue
43 
44             g_suite_mailing_list = MAILING_LIST_USER_FIELDS_TO_LIST_NAME[mailing_list]
45 
46             update_mailing_list.delay(
47                 g_suite_mailing_list,
48                 instance.primary_email,
49                 getattr(instance, mailing_list),
50             )
51
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/apps/authentication/signals.py b/apps/authentication/signals.py
--- a/apps/authentication/signals.py
+++ b/apps/authentication/signals.py
@@ -5,17 +5,20 @@
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Group
-from django.db.models.signals import m2m_changed, post_save, pre_delete
+from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save
 from django.dispatch import receiver
 
-from apps.authentication.models import GroupMember, OnlineGroup
+from apps.authentication.models import Email, GroupMember, OnlineGroup
 from apps.authentication.tasks import SynchronizeGroups
 from apps.gsuite.mail_syncer.main import update_g_suite_group, update_g_suite_user
+from apps.gsuite.mail_syncer.tasks import update_mailing_list
 
 User = get_user_model()
 logger = logging.getLogger("syncer.%s" % __name__)
 sync_uuid = uuid.uuid1()
 
+MAILING_LIST_USER_FIELDS_TO_LIST_NAME = settings.MAILING_LIST_USER_FIELDS_TO_LIST_NAME
+
 
 def run_group_syncer(user):
     """
@@ -103,3 +106,25 @@
     user: User = instance.user
     if user in group.user_set.all():
         group.user_set.remove(user)
+
+
+@receiver(pre_save, sender=Email)
+def re_subscribe_primary_email_to_lists(sender, instance: Email, **kwargs):
+    user: User = instance.user
+    jobmail = MAILING_LIST_USER_FIELDS_TO_LIST_NAME.get("jobmail")
+    infomail = MAILING_LIST_USER_FIELDS_TO_LIST_NAME.get("infomail")
+    if instance.pk:
+        stored_instance: Email = Email.objects.get(pk=instance.pk)
+
+        # Handle case when the instance is changed to primary
+        if instance.primary and not stored_instance.primary:
+            if user.jobmail:
+                update_mailing_list.delay(jobmail, email=instance.email, added=True)
+            if user.infomail:
+                update_mailing_list.delay(infomail, email=instance.email, added=True)
+        # Handle case when the instance is changed from primary
+        elif not instance.primary and stored_instance.primary:
+            if user.jobmail:
+                update_mailing_list.delay(jobmail, email=instance.email, added=False)
+            if user.infomail:
+                update_mailing_list.delay(infomail, email=instance.email, added=False)
diff --git a/apps/gsuite/mail_syncer/signals.py b/apps/gsuite/mail_syncer/signals.py
--- a/apps/gsuite/mail_syncer/signals.py
+++ b/apps/gsuite/mail_syncer/signals.py
@@ -1,5 +1,6 @@
 import logging
 
+from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.db.models.signals import pre_save
 from django.dispatch import receiver
@@ -8,7 +9,7 @@
 
 User = get_user_model()
 
-MAILING_LIST_USER_FIELDS_TO_LIST_NAME = {"infomail": "info", "jobmail": "oppdrag"}
+MAILING_LIST_USER_FIELDS_TO_LIST_NAME = settings.MAILING_LIST_USER_FIELDS_TO_LIST_NAME
 
 logger = logging.getLogger(__name__)
 
diff --git a/onlineweb4/settings/gsuite.py b/onlineweb4/settings/gsuite.py
--- a/onlineweb4/settings/gsuite.py
+++ b/onlineweb4/settings/gsuite.py
@@ -53,3 +53,8 @@
         'techtalks' : 'techtalks'
     }
 }
+
+MAILING_LIST_USER_FIELDS_TO_LIST_NAME = {
+    'infomail': 'info',
+    'jobmail': 'oppdrag',
+}

{"golden_diff": "diff --git a/apps/authentication/signals.py b/apps/authentication/signals.py\n--- a/apps/authentication/signals.py\n+++ b/apps/authentication/signals.py\n@@ -5,17 +5,20 @@\n from django.conf import settings\n from django.contrib.auth import get_user_model\n from django.contrib.auth.models import Group\n-from django.db.models.signals import m2m_changed, post_save, pre_delete\n+from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save\n from django.dispatch import receiver\n \n-from apps.authentication.models import GroupMember, OnlineGroup\n+from apps.authentication.models import Email, GroupMember, OnlineGroup\n from apps.authentication.tasks import SynchronizeGroups\n from apps.gsuite.mail_syncer.main import update_g_suite_group, update_g_suite_user\n+from apps.gsuite.mail_syncer.tasks import update_mailing_list\n \n User = get_user_model()\n logger = logging.getLogger(\"syncer.%s\" % __name__)\n sync_uuid = uuid.uuid1()\n \n+MAILING_LIST_USER_FIELDS_TO_LIST_NAME = settings.MAILING_LIST_USER_FIELDS_TO_LIST_NAME\n+\n \n def run_group_syncer(user):\n     \"\"\"\n@@ -103,3 +106,25 @@\n     user: User = instance.user\n     if user in group.user_set.all():\n         group.user_set.remove(user)\n+\n+\n+@receiver(pre_save, sender=Email)\n+def re_subscribe_primary_email_to_lists(sender, instance: Email, **kwargs):\n+    user: User = instance.user\n+    jobmail = MAILING_LIST_USER_FIELDS_TO_LIST_NAME.get(\"jobmail\")\n+    infomail = MAILING_LIST_USER_FIELDS_TO_LIST_NAME.get(\"infomail\")\n+    if instance.pk:\n+        stored_instance: Email = Email.objects.get(pk=instance.pk)\n+\n+        # Handle case when the instance is changed to primary\n+        if instance.primary and not stored_instance.primary:\n+            if user.jobmail:\n+                update_mailing_list.delay(jobmail, email=instance.email, added=True)\n+            if user.infomail:\n+                update_mailing_list.delay(infomail, email=instance.email, added=True)\n+        # Handle case when the instance is changed from primary\n+        elif not instance.primary and stored_instance.primary:\n+            if user.jobmail:\n+                update_mailing_list.delay(jobmail, email=instance.email, added=False)\n+            if user.infomail:\n+                update_mailing_list.delay(infomail, email=instance.email, added=False)\ndiff --git a/apps/gsuite/mail_syncer/signals.py b/apps/gsuite/mail_syncer/signals.py\n--- a/apps/gsuite/mail_syncer/signals.py\n+++ b/apps/gsuite/mail_syncer/signals.py\n@@ -1,5 +1,6 @@\n import logging\n \n+from django.conf import settings\n from django.contrib.auth import get_user_model\n from django.db.models.signals import pre_save\n from django.dispatch import receiver\n@@ -8,7 +9,7 @@\n \n User = get_user_model()\n \n-MAILING_LIST_USER_FIELDS_TO_LIST_NAME = {\"infomail\": \"info\", \"jobmail\": \"oppdrag\"}\n+MAILING_LIST_USER_FIELDS_TO_LIST_NAME = settings.MAILING_LIST_USER_FIELDS_TO_LIST_NAME\n \n logger = logging.getLogger(__name__)\n \ndiff --git a/onlineweb4/settings/gsuite.py b/onlineweb4/settings/gsuite.py\n--- a/onlineweb4/settings/gsuite.py\n+++ b/onlineweb4/settings/gsuite.py\n@@ -53,3 +53,8 @@\n         'techtalks' : 'techtalks'\n     }\n }\n+\n+MAILING_LIST_USER_FIELDS_TO_LIST_NAME = {\n+    'infomail': 'info',\n+    'jobmail': 'oppdrag',\n+}\n", "issue": "Changing primary email address should change email address used for list subscriptions\n## What kind of an issue is this?\r\n\r\n- Bug report\r\n\r\n\r\n## What is the expected behaviour?\r\n\r\nIf I change my primary email address I expect the mailing list services to be updated with my new address, whilst removing my old address.\r\n\r\n## What is the current behaviour?\r\n\r\nThe old address is not removed and the new address is not added.\r\n\r\n\r\n## How do you reproduce this problem? \r\n\r\n* (Add a new email to an account) (_Only required if there is no alternative email added to the account already_)\r\n* Set the new email address as the primary\r\n* _Notice that the action happens immediately_\r\n    * (Vs. unsubscribing / subscribing to a list, which queries the mailing list service to add/remove addresses.)\r\n* Verify that the old primary address is still on the list\r\n* Verify that the new primary address is absent from the list\r\n\r\n## Other information\r\n\r\nThe settings page tells the user that the primary address is the one used for mailing lists:\r\n\r\n> **Adressen som er markert som prim\u00e6r brukes til alle v\u00e5re e-postlister**, gjenoppretning av passord og andre varsler som sendes fra oss.\r\n\r\nThe [`set_primary`](https://github.com/dotkom/onlineweb4/blob/v4.24.28/apps/profiles/views.py#L326) function does not handle subscribing/unsubscribing to a list. (Neither does the [`Email model`](https://github.com/dotkom/onlineweb4/blob/v4.24.28/apps/authentication/models.py#L278) nor any signals attached to it.)\r\n\n", "before_files": [{"content": "import os\n\nfrom decouple import config\n\nfrom .base import PROJECT_ROOT_DIRECTORY\n\nOW4_GSUITE_CREDENTIALS_FILENAME = config('OW4_GSUITE_CREDENTIALS_FILENAME', default='gsuitecredentials.json')\nOW4_GSUITE_CREDENTIALS_PATH = config('OW4_GSUITE_CREDENTIALS_PATH',\n                                     default=os.path.join(PROJECT_ROOT_DIRECTORY, OW4_GSUITE_CREDENTIALS_FILENAME))\n\nOW4_GSUITE_SETTINGS = {\n    'CREDENTIALS': OW4_GSUITE_CREDENTIALS_PATH,\n    'DOMAIN': config('OW4_GSUITE_SYNC_DOMAIN', default='online.ntnu.no'),\n    # DELEGATED_ACCOUNT: G Suite Account with proper permissions to perform insertions and removals.\n    'DELEGATED_ACCOUNT': config('OW4_GSUITE_DELEGATED_ACCOUNT', default=''),\n    'ENABLED': config('OW4_GSUITE_ENABLED', cast=bool, default=False),\n}\n\nOW4_GSUITE_ACCOUNTS = {\n    'ENABLED': config('OW4_GSUITE_ACCOUNTS_ENABLED', cast=bool, default=False),\n    'ENABLE_INSERT': config('OW4_GSUITE_ACCOUNTS_ENABLE_INSERT', cast=bool, default=False),\n}\n\nOW4_GSUITE_SYNC = {\n    'CREDENTIALS': OW4_GSUITE_SETTINGS.get('CREDENTIALS'),\n    'DOMAIN': OW4_GSUITE_SETTINGS.get('DOMAIN'),\n    'DELEGATED_ACCOUNT': OW4_GSUITE_SETTINGS.get('DELEGATED_ACCOUNT'),\n    'ENABLED': config('OW4_GSUITE_SYNC_ENABLED', cast=bool, default=False),\n    'ENABLE_INSERT': config('OW4_GSUITE_SYNC_ENABLE_INSERT', cast=bool, default=False),\n    'ENABLE_DELETE': config('OW4_GSUITE_SYNC_ENABLE_DELETE', cast=bool, default=False),\n    # OW4 name (lowercase) -> G Suite name (lowercase)\n    'GROUPS': {\n        'appkom': 'appkom',\n        'arrkom': 'arrkom',\n        'bankom': 'bankom',\n        'bedkom': 'bedkom',\n        'dotkom': 'dotkom',\n        'ekskom': 'ekskom',\n        'fagkom': 'fagkom',\n        'fond': 'fond',\n        'hovedstyret': 'hovedstyret',\n        'jubkom': 'jubkom',\n        'prokom': 'prokom',\n        'seniorkom': 'seniorkom',\n        'trikom': 'trikom',\n        'tillitsvalgte': 'tillitsvalgte',\n        'redaksjonen': 'redaksjonen',\n        'ekskom': 'ekskom',\n        'itex' : 'itex',\n        'velkom': 'velkom',\n        'interessegrupper': 'interessegrupper',\n        'online-il': 'online-il',\n        'techtalks' : 'techtalks'\n    }\n}\n", "path": "onlineweb4/settings/gsuite.py"}, {"content": "# -*- coding: utf-8 -*-\nimport logging\nimport uuid\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.models import Group\nfrom django.db.models.signals import m2m_changed, post_save, pre_delete\nfrom django.dispatch import receiver\n\nfrom apps.authentication.models import GroupMember, OnlineGroup\nfrom apps.authentication.tasks import SynchronizeGroups\nfrom apps.gsuite.mail_syncer.main import update_g_suite_group, update_g_suite_user\n\nUser = get_user_model()\nlogger = logging.getLogger(\"syncer.%s\" % __name__)\nsync_uuid = uuid.uuid1()\n\n\ndef run_group_syncer(user):\n    \"\"\"\n    Tasks to run after User is changed.\n    :param user: The user instance to sync groups for.\n    :type user: OnlineUser\n    :return: None\n    \"\"\"\n    SynchronizeGroups.run()\n    if settings.OW4_GSUITE_SYNC.get(\"ENABLED\", False):\n        ow4_gsuite_domain = settings.OW4_GSUITE_SYNC.get(\"DOMAIN\")\n        if isinstance(user, User):\n            logger.debug(\"Running G Suite syncer for user {}\".format(user))\n            update_g_suite_user(ow4_gsuite_domain, user, suppress_http_errors=True)\n        elif isinstance(user, Group):\n            group = user\n            logger.debug(\"Running G Suite syncer for group {}\".format(group))\n            update_g_suite_group(\n                ow4_gsuite_domain, group.name, suppress_http_errors=True\n            )\n\n\n@receiver(post_save, sender=Group)\ndef trigger_group_syncer(sender, instance, created=False, **kwargs):\n    \"\"\"\n    :param sender: The model that triggered this hook\n    :param instance: The model instance triggering this hook\n    :param created: True if the instance was created, False if the instance was updated\n\n    Calls the SynchronizeGroups Task if a group is updated. (Not if it's the initial creation of a group)\n    \"\"\"\n    global sync_uuid\n\n    if created:\n        # If a new instance is created, we do not need to trigger group sync.\n        pass\n    else:\n        # If sync is triggered by adding a user to group or a group to a user\n        # then we need to detach the signal hook listening to m2m changes on\n        # those models as they will trigger a recursive call to this method.\n        if sender == User.groups.through:\n            logger.debug(\n                \"Disconnect m2m_changed signal hook with uuid %s before synchronizing groups\"\n                % sync_uuid\n            )\n            if m2m_changed.disconnect(sender=sender, dispatch_uid=sync_uuid):\n                logger.debug(\"Signal with uuid %s disconnected\" % sync_uuid)\n                run_group_syncer(instance)\n\n            sync_uuid = uuid.uuid1()\n            logger.debug(\n                \"m2m_changed signal hook reconnected with uuid: %s\" % sync_uuid\n            )\n            m2m_changed.connect(\n                receiver=trigger_group_syncer,\n                dispatch_uid=sync_uuid,\n                sender=User.groups.through,\n            )\n        else:\n            run_group_syncer(instance)\n\n\nm2m_changed.connect(\n    trigger_group_syncer, dispatch_uid=sync_uuid, sender=User.groups.through\n)\n\n\n@receiver(post_save, sender=GroupMember)\ndef add_online_group_member_to_django_group(\n    sender, instance: GroupMember, created=False, **kwargs\n):\n    online_group: OnlineGroup = instance.group\n    group: Group = online_group.group\n    user: User = instance.user\n    if user not in group.user_set.all():\n        group.user_set.add(user)\n\n\n@receiver(pre_delete, sender=GroupMember)\ndef remove_online_group_members_from_django_group(\n    sender, instance: GroupMember, **kwargs\n):\n    online_group: OnlineGroup = instance.group\n    group: Group = online_group.group\n    user: User = instance.user\n    if user in group.user_set.all():\n        group.user_set.remove(user)\n", "path": "apps/authentication/signals.py"}, {"content": "import logging\n\nfrom django.contrib.auth import get_user_model\nfrom django.db.models.signals import pre_save\nfrom django.dispatch import receiver\n\nfrom .tasks import update_mailing_list\n\nUser = get_user_model()\n\nMAILING_LIST_USER_FIELDS_TO_LIST_NAME = {\"infomail\": \"info\", \"jobmail\": \"oppdrag\"}\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_updated_mailing_list_fields(user):\n    updated_mailing_lists = []\n    try:\n        # Get the current user and find out what's about to change\n        current_user = User.objects.get(pk=user.pk)\n        if user.infomail != current_user.infomail:\n            updated_mailing_lists.append(\"infomail\")\n        if user.jobmail != current_user.jobmail:\n            updated_mailing_lists.append(\"jobmail\")\n    except User.DoesNotExist:\n        # Find out which mailing lists are opted into if the user did not previously exist\n        for mailing_list in MAILING_LIST_USER_FIELDS_TO_LIST_NAME.keys():\n            if getattr(user, mailing_list, False):\n                updated_mailing_lists.append(mailing_list)\n\n    return updated_mailing_lists\n\n\n@receiver(pre_save, sender=User)\ndef toggle_mailing_lists(sender, instance, **kwargs):\n    update_fields = get_updated_mailing_list_fields(instance)\n\n    if update_fields:\n        for mailing_list in MAILING_LIST_USER_FIELDS_TO_LIST_NAME.keys():\n            if mailing_list not in update_fields:\n                # Skips toggle if mailing list field not changed.\n                continue\n\n            g_suite_mailing_list = MAILING_LIST_USER_FIELDS_TO_LIST_NAME[mailing_list]\n\n            update_mailing_list.delay(\n                g_suite_mailing_list,\n                instance.primary_email,\n                getattr(instance, mailing_list),\n            )\n", "path": "apps/gsuite/mail_syncer/signals.py"}], "after_files": [{"content": "import os\n\nfrom decouple import config\n\nfrom .base import PROJECT_ROOT_DIRECTORY\n\nOW4_GSUITE_CREDENTIALS_FILENAME = config('OW4_GSUITE_CREDENTIALS_FILENAME', default='gsuitecredentials.json')\nOW4_GSUITE_CREDENTIALS_PATH = config('OW4_GSUITE_CREDENTIALS_PATH',\n                                     default=os.path.join(PROJECT_ROOT_DIRECTORY, OW4_GSUITE_CREDENTIALS_FILENAME))\n\nOW4_GSUITE_SETTINGS = {\n    'CREDENTIALS': OW4_GSUITE_CREDENTIALS_PATH,\n    'DOMAIN': config('OW4_GSUITE_SYNC_DOMAIN', default='online.ntnu.no'),\n    # DELEGATED_ACCOUNT: G Suite Account with proper permissions to perform insertions and removals.\n    'DELEGATED_ACCOUNT': config('OW4_GSUITE_DELEGATED_ACCOUNT', default=''),\n    'ENABLED': config('OW4_GSUITE_ENABLED', cast=bool, default=False),\n}\n\nOW4_GSUITE_ACCOUNTS = {\n    'ENABLED': config('OW4_GSUITE_ACCOUNTS_ENABLED', cast=bool, default=False),\n    'ENABLE_INSERT': config('OW4_GSUITE_ACCOUNTS_ENABLE_INSERT', cast=bool, default=False),\n}\n\nOW4_GSUITE_SYNC = {\n    'CREDENTIALS': OW4_GSUITE_SETTINGS.get('CREDENTIALS'),\n    'DOMAIN': OW4_GSUITE_SETTINGS.get('DOMAIN'),\n    'DELEGATED_ACCOUNT': OW4_GSUITE_SETTINGS.get('DELEGATED_ACCOUNT'),\n    'ENABLED': config('OW4_GSUITE_SYNC_ENABLED', cast=bool, default=False),\n    'ENABLE_INSERT': config('OW4_GSUITE_SYNC_ENABLE_INSERT', cast=bool, default=False),\n    'ENABLE_DELETE': config('OW4_GSUITE_SYNC_ENABLE_DELETE', cast=bool, default=False),\n    # OW4 name (lowercase) -> G Suite name (lowercase)\n    'GROUPS': {\n        'appkom': 'appkom',\n        'arrkom': 'arrkom',\n        'bankom': 'bankom',\n        'bedkom': 'bedkom',\n        'dotkom': 'dotkom',\n        'ekskom': 'ekskom',\n        'fagkom': 'fagkom',\n        'fond': 'fond',\n        'hovedstyret': 'hovedstyret',\n        'jubkom': 'jubkom',\n        'prokom': 'prokom',\n        'seniorkom': 'seniorkom',\n        'trikom': 'trikom',\n        'tillitsvalgte': 'tillitsvalgte',\n        'redaksjonen': 'redaksjonen',\n        'ekskom': 'ekskom',\n        'itex' : 'itex',\n        'velkom': 'velkom',\n        'interessegrupper': 'interessegrupper',\n        'online-il': 'online-il',\n        'techtalks' : 'techtalks'\n    }\n}\n\nMAILING_LIST_USER_FIELDS_TO_LIST_NAME = {\n    'infomail': 'info',\n    'jobmail': 'oppdrag',\n}\n", "path": "onlineweb4/settings/gsuite.py"}, {"content": "# -*- coding: utf-8 -*-\nimport logging\nimport uuid\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.models import Group\nfrom django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save\nfrom django.dispatch import receiver\n\nfrom apps.authentication.models import Email, GroupMember, OnlineGroup\nfrom apps.authentication.tasks import SynchronizeGroups\nfrom apps.gsuite.mail_syncer.main import update_g_suite_group, update_g_suite_user\nfrom apps.gsuite.mail_syncer.tasks import update_mailing_list\n\nUser = get_user_model()\nlogger = logging.getLogger(\"syncer.%s\" % __name__)\nsync_uuid = uuid.uuid1()\n\nMAILING_LIST_USER_FIELDS_TO_LIST_NAME = settings.MAILING_LIST_USER_FIELDS_TO_LIST_NAME\n\n\ndef run_group_syncer(user):\n    \"\"\"\n    Tasks to run after User is changed.\n    :param user: The user instance to sync groups for.\n    :type user: OnlineUser\n    :return: None\n    \"\"\"\n    SynchronizeGroups.run()\n    if settings.OW4_GSUITE_SYNC.get(\"ENABLED\", False):\n        ow4_gsuite_domain = settings.OW4_GSUITE_SYNC.get(\"DOMAIN\")\n        if isinstance(user, User):\n            logger.debug(\"Running G Suite syncer for user {}\".format(user))\n            update_g_suite_user(ow4_gsuite_domain, user, suppress_http_errors=True)\n        elif isinstance(user, Group):\n            group = user\n            logger.debug(\"Running G Suite syncer for group {}\".format(group))\n            update_g_suite_group(\n                ow4_gsuite_domain, group.name, suppress_http_errors=True\n            )\n\n\n@receiver(post_save, sender=Group)\ndef trigger_group_syncer(sender, instance, created=False, **kwargs):\n    \"\"\"\n    :param sender: The model that triggered this hook\n    :param instance: The model instance triggering this hook\n    :param created: True if the instance was created, False if the instance was updated\n\n    Calls the SynchronizeGroups Task if a group is updated. (Not if it's the initial creation of a group)\n    \"\"\"\n    global sync_uuid\n\n    if created:\n        # If a new instance is created, we do not need to trigger group sync.\n        pass\n    else:\n        # If sync is triggered by adding a user to group or a group to a user\n        # then we need to detach the signal hook listening to m2m changes on\n        # those models as they will trigger a recursive call to this method.\n        if sender == User.groups.through:\n            logger.debug(\n                \"Disconnect m2m_changed signal hook with uuid %s before synchronizing groups\"\n                % sync_uuid\n            )\n            if m2m_changed.disconnect(sender=sender, dispatch_uid=sync_uuid):\n                logger.debug(\"Signal with uuid %s disconnected\" % sync_uuid)\n                run_group_syncer(instance)\n\n            sync_uuid = uuid.uuid1()\n            logger.debug(\n                \"m2m_changed signal hook reconnected with uuid: %s\" % sync_uuid\n            )\n            m2m_changed.connect(\n                receiver=trigger_group_syncer,\n                dispatch_uid=sync_uuid,\n                sender=User.groups.through,\n            )\n        else:\n            run_group_syncer(instance)\n\n\nm2m_changed.connect(\n    trigger_group_syncer, dispatch_uid=sync_uuid, sender=User.groups.through\n)\n\n\n@receiver(post_save, sender=GroupMember)\ndef add_online_group_member_to_django_group(\n    sender, instance: GroupMember, created=False, **kwargs\n):\n    online_group: OnlineGroup = instance.group\n    group: Group = online_group.group\n    user: User = instance.user\n    if user not in group.user_set.all():\n        group.user_set.add(user)\n\n\n@receiver(pre_delete, sender=GroupMember)\ndef remove_online_group_members_from_django_group(\n    sender, instance: GroupMember, **kwargs\n):\n    online_group: OnlineGroup = instance.group\n    group: Group = online_group.group\n    user: User = instance.user\n    if user in group.user_set.all():\n        group.user_set.remove(user)\n\n\n@receiver(pre_save, sender=Email)\ndef re_subscribe_primary_email_to_lists(sender, instance: Email, **kwargs):\n    user: User = instance.user\n    jobmail = MAILING_LIST_USER_FIELDS_TO_LIST_NAME.get(\"jobmail\")\n    infomail = MAILING_LIST_USER_FIELDS_TO_LIST_NAME.get(\"infomail\")\n    if instance.pk:\n        stored_instance: Email = Email.objects.get(pk=instance.pk)\n\n        # Handle case when the instance is changed to primary\n        if instance.primary and not stored_instance.primary:\n            if user.jobmail:\n                update_mailing_list.delay(jobmail, email=instance.email, added=True)\n            if user.infomail:\n                update_mailing_list.delay(infomail, email=instance.email, added=True)\n        # Handle case when the instance is changed from primary\n        elif not instance.primary and stored_instance.primary:\n            if user.jobmail:\n                update_mailing_list.delay(jobmail, email=instance.email, added=False)\n            if user.infomail:\n                update_mailing_list.delay(infomail, email=instance.email, added=False)\n", "path": "apps/authentication/signals.py"}, {"content": "import logging\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.db.models.signals import pre_save\nfrom django.dispatch import receiver\n\nfrom .tasks import update_mailing_list\n\nUser = get_user_model()\n\nMAILING_LIST_USER_FIELDS_TO_LIST_NAME = settings.MAILING_LIST_USER_FIELDS_TO_LIST_NAME\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_updated_mailing_list_fields(user):\n    updated_mailing_lists = []\n    try:\n        # Get the current user and find out what's about to change\n        current_user = User.objects.get(pk=user.pk)\n        if user.infomail != current_user.infomail:\n            updated_mailing_lists.append(\"infomail\")\n        if user.jobmail != current_user.jobmail:\n            updated_mailing_lists.append(\"jobmail\")\n    except User.DoesNotExist:\n        # Find out which mailing lists are opted into if the user did not previously exist\n        for mailing_list in MAILING_LIST_USER_FIELDS_TO_LIST_NAME.keys():\n            if getattr(user, mailing_list, False):\n                updated_mailing_lists.append(mailing_list)\n\n    return updated_mailing_lists\n\n\n@receiver(pre_save, sender=User)\ndef toggle_mailing_lists(sender, instance, **kwargs):\n    update_fields = get_updated_mailing_list_fields(instance)\n\n    if update_fields:\n        for mailing_list in MAILING_LIST_USER_FIELDS_TO_LIST_NAME.keys():\n            if mailing_list not in update_fields:\n                # Skips toggle if mailing list field not changed.\n                continue\n\n            g_suite_mailing_list = MAILING_LIST_USER_FIELDS_TO_LIST_NAME[mailing_list]\n\n            update_mailing_list.delay(\n                g_suite_mailing_list,\n                instance.primary_email,\n                getattr(instance, mailing_list),\n            )\n", "path": "apps/gsuite/mail_syncer/signals.py"}]}