We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Change data message to notification message
Currently the push notification is sent as data message.
Check, if it is sensible to send the message as notification type.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `privacyidea/lib/smsprovider/FirebaseProvider.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 #    2019-02-12 Cornelius Kölbel <cornelius.koelbel@netknights.it>
4 #
5 #
6 #    This program is free software: you can redistribute it and/or
7 #    modify it under the terms of the GNU Affero General Public
8 #    License, version 3, as published by the Free Software Foundation.
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU Affero General Public License for more details.
14 #
15 #    You should have received a copy of the
16 #               GNU Affero General Public License
17 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19 #
20 
21 __doc__ = """This is the provider class that communicates with Googles
22 Firebase Cloud Messaging Service.
23 This provider is used for the push token and can be used for SMS tokens.
24 """
25 
26 from privacyidea.lib.smsprovider.SMSProvider import (ISMSProvider)
27 from privacyidea.lib.error import ConfigAdminError
28 from privacyidea.lib.framework import get_app_local_store
29 from privacyidea.lib import _
30 import logging
31 from google.oauth2 import service_account
32 from google.auth.transport.requests import AuthorizedSession
33 import json
34 import time
35 
36 FIREBASE_URL_SEND = 'https://fcm.googleapis.com/v1/projects/{0!s}/messages:send'
37 SCOPES = ['https://www.googleapis.com/auth/cloud-platform',
38           'https://www.googleapis.com/auth/datastore',
39           'https://www.googleapis.com/auth/devstorage.read_write',
40           'https://www.googleapis.com/auth/firebase',
41           'https://www.googleapis.com/auth/identitytoolkit',
42           'https://www.googleapis.com/auth/userinfo.email']
43 
44 log = logging.getLogger(__name__)
45 
46 
47 def get_firebase_access_token(config_file_name):
48     """
49     This returns the access token for a given JSON config file name
50 
51     :param config_file_name: The json file with the Service account credentials
52     :type config_file_name: str
53     :return: Firebase credentials
54     :rtype: google.oauth2.service_account.Credentials
55     """
56     fbt = "firebase_token"
57     app_store = get_app_local_store()
58 
59     if fbt not in app_store or not isinstance(app_store[fbt], dict):
60         # initialize the firebase_token in the app_store as dict
61         app_store[fbt] = {}
62 
63     if not isinstance(app_store[fbt].get(config_file_name), service_account.Credentials) or \
64             app_store[fbt].get(config_file_name).expired:
65         # If the type of the config is not of class Credentials or if the token
66         # has expired we get new scoped access token credentials
67         credentials = service_account.Credentials.from_service_account_file(config_file_name,
68                                                                             scopes=SCOPES)
69 
70         log.debug("Fetching a new access_token for {!r} from firebase...".format(config_file_name))
71         # We do not use a lock here: The worst that could happen is that two threads
72         # fetch new auth tokens concurrently. In this case, one of them wins and
73         # is written to the dictionary.
74         app_store[fbt][config_file_name] = credentials
75         readable_time = credentials.expiry.isoformat() if credentials.expiry else 'Never'
76         log.debug(u"Setting the expiration for {!r} of the new access_token "
77                   u"to {!s}.".format(config_file_name, readable_time))
78 
79     return app_store[fbt][config_file_name]
80 
81 
82 class FIREBASE_CONFIG:
83     REGISTRATION_URL = "registration URL"
84     TTL = "time to live"
85     JSON_CONFIG = "JSON config file"
86     PROJECT_ID = "projectid"
87     PROJECT_NUMBER = "projectnumber"
88     APP_ID = "appid"
89     API_KEY = "apikey"
90     APP_ID_IOS = "appidios"
91     API_KEY_IOS = "apikeyios"
92     HTTPS_PROXY = "httpsproxy"
93 
94 
95 class FirebaseProvider(ISMSProvider):
96 
97     def __init__(self, db_smsprovider_object=None, smsgateway=None):
98         ISMSProvider.__init__(self, db_smsprovider_object, smsgateway)
99         self.access_token_info = None
100         self.access_token_expires_at = 0
101 
102     def submit_message(self, firebase_token, data):
103         """
104         send a message to a registered Firebase client
105         This can be a simple OTP value or a cryptographic challenge response.
106 
107         :param firebase_token: The firebase token of the smartphone
108         :type firebase_token: str
109         :param data: the data dictionary part of the message to submit to the phone
110         :type data: dict
111         :return: bool
112         """
113         res = False
114 
115         credentials = get_firebase_access_token(self.smsgateway.option_dict.get(
116             FIREBASE_CONFIG.JSON_CONFIG))
117 
118         authed_session = AuthorizedSession(credentials)
119 
120         headers = {
121             'Content-Type': 'application/json; UTF-8',
122         }
123         fcm_message = {
124             "message": {
125                         "data": data,
126                         "token": firebase_token,
127                         "android": {
128                                     "priority": "HIGH",
129                                     "ttl": "120s",
130                                     "fcm_options": {"analytics_label": "AndroidPushToken"}
131                                    },
132                         "apns": {
133                                  "headers": {
134                                              "apns-priority": "10",
135                                              "apns-push-type": "alert",
136                                              "apns-collapse-id": "privacyidea.pushtoken",
137                                              "apns-expiration": str(int(time.time()) + 120)
138                                             },
139                                  "payload": {
140                                              "aps": {
141                                                      "alert": {
142                                                                "title": data.get("title"),
143                                                                "body": data.get("question"),
144                                                               },
145                                                      "sound": "default",
146                                                      "category": "PUSH_AUTHENTICATION"
147                                                     },
148                                             },
149                                  "fcm_options": {"analytics_label": "iOSPushToken"}
150                                 }
151                        }
152             }
153 
154         proxies = {}
155         if self.smsgateway.option_dict.get(FIREBASE_CONFIG.HTTPS_PROXY):
156             proxies["https"] = self.smsgateway.option_dict.get(FIREBASE_CONFIG.HTTPS_PROXY)
157         url = FIREBASE_URL_SEND.format(self.smsgateway.option_dict.get(FIREBASE_CONFIG.PROJECT_ID))
158         resp = authed_session.post(url, data=json.dumps(fcm_message), headers=headers, proxies=proxies)
159 
160         if resp.status_code == 200:
161             log.debug("Message sent successfully to Firebase service.")
162             res = True
163         else:
164             log.warning(u"Failed to send message to firebase service: {0!s}".format(resp.text))
165 
166         return res
167 
168     def check_configuration(self):
169         """
170         This method checks the sanity of the configuration of this provider.
171         If there is a configuration error, than an exception is raised.
172         :return:
173         """
174         json_file = self.smsgateway.option_dict.get(FIREBASE_CONFIG.JSON_CONFIG)
175         server_config = None
176         with open(json_file) as f:
177             server_config = json.load(f)
178         if server_config:
179             if server_config.get("type") != "service_account":
180                 raise ConfigAdminError(description="The JSON file is not a valid firebase credentials file.")
181             project_id = self.smsgateway.option_dict.get(FIREBASE_CONFIG.PROJECT_ID)
182             if server_config.get("project_id") != project_id:
183                 raise ConfigAdminError(description="The project_id you entered does not match the project_id from the JSON file.")
184 
185         else:
186             raise ConfigAdminError(description="Please check your configuration. Can not load JSON file.")
187 
188         # We need at least
189         #         FIREBASE_CONFIG.API_KEY_IOS and FIREBASE_CONFIG.APP_ID_IOS
190         # or
191         #         FIREBASE_CONFIG.API_KEY and FIREBASE_CONFIG.APP_ID
192         android_configured = bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.APP_ID)) and \
193                              bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.API_KEY))
194         ios_configured = bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.APP_ID_IOS)) and \
195                              bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.API_KEY_IOS))
196         if not android_configured and not ios_configured:
197             raise ConfigAdminError(description="You need to at least configure either app_id and api_key or"
198                                                " app_id_ios and api_key_ios.")
199 
200     @classmethod
201     def parameters(cls):
202         """
203         Return a dictionary, that describes the parameters and options for the
204         Firebase Provider
205         Parameters are required keys to values.
206 
207         :return: dict
208         """
209         params = {"options_allowed": False,
210                   "headers_allowed": False,
211                   "parameters": {
212                       FIREBASE_CONFIG.PROJECT_ID: {
213                           "required": True,
214                           "description": _("The project ID, that the client should use. Get it from your Firebase console.")
215                       },
216                       FIREBASE_CONFIG.PROJECT_NUMBER: {
217                           "required": True,
218                           "description": _(
219                               "The project number, that the client should use. Get it from your Firebase console.")
220                       },
221                       FIREBASE_CONFIG.APP_ID: {
222                           "required": False,
223                           "description": _(
224                               "The App ID, that the Android client should use. Get it from your Firebase console.")
225                       },
226                       FIREBASE_CONFIG.API_KEY: {
227                           "required": False,
228                           "description": _(
229                               "The API Key, that the Android client should use. Get it from your Firebase console.")
230                       },
231                       FIREBASE_CONFIG.APP_ID_IOS:{
232                           "required": False,
233                           "description": _(
234                               "The App ID, that the iOS client should use. Get it from your Firebase console.")
235                       },
236                       FIREBASE_CONFIG.API_KEY_IOS: {
237                           "required": False,
238                           "description": _(
239                               "The API Key, that the iOS client should use. Get it from your Firebase console.")
240                       },
241                       FIREBASE_CONFIG.JSON_CONFIG: {
242                           "required": True,
243                           "description": _("The filename of the JSON config file, that allows privacyIDEA to talk"
244                                            " to the Firebase REST API.")
245                       },
246                       FIREBASE_CONFIG.HTTPS_PROXY: {
247                           "required": False,
248                           "description": _("Proxy setting for HTTPS connections to googleapis.com.")
249                       }
250                   }
251                   }
252         return params
253
```
--- 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/privacyidea/lib/smsprovider/FirebaseProvider.py b/privacyidea/lib/smsprovider/FirebaseProvider.py
--- a/privacyidea/lib/smsprovider/FirebaseProvider.py
+++ b/privacyidea/lib/smsprovider/FirebaseProvider.py
@@ -124,6 +124,10 @@
             "message": {
                         "data": data,
                         "token": firebase_token,
+                        "notification": {
+                            "title": data.get("title"),
+                            "body": data.get("question")
+                        },
                         "android": {
                                     "priority": "HIGH",
                                     "ttl": "120s",

{"golden_diff": "diff --git a/privacyidea/lib/smsprovider/FirebaseProvider.py b/privacyidea/lib/smsprovider/FirebaseProvider.py\n--- a/privacyidea/lib/smsprovider/FirebaseProvider.py\n+++ b/privacyidea/lib/smsprovider/FirebaseProvider.py\n@@ -124,6 +124,10 @@\n             \"message\": {\n                         \"data\": data,\n                         \"token\": firebase_token,\n+                        \"notification\": {\n+                            \"title\": data.get(\"title\"),\n+                            \"body\": data.get(\"question\")\n+                        },\n                         \"android\": {\n                                     \"priority\": \"HIGH\",\n                                     \"ttl\": \"120s\",\n", "issue": "Change data message to notification message\nCurrently the push notification is sent as data message.\r\nCheck, if it is sensible to send the message as notification type.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#    2019-02-12 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#\n#\n#    This program is free software: you can redistribute it and/or\n#    modify it under the terms of the GNU Affero General Public\n#    License, version 3, as published by the Free Software Foundation.\n#\n#    This program is distributed in the hope that it will be useful,\n#    but WITHOUT ANY WARRANTY; without even the implied warranty of\n#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n#    GNU Affero General Public License for more details.\n#\n#    You should have received a copy of the\n#               GNU Affero General Public License\n#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n#\n\n__doc__ = \"\"\"This is the provider class that communicates with Googles\nFirebase Cloud Messaging Service.\nThis provider is used for the push token and can be used for SMS tokens.\n\"\"\"\n\nfrom privacyidea.lib.smsprovider.SMSProvider import (ISMSProvider)\nfrom privacyidea.lib.error import ConfigAdminError\nfrom privacyidea.lib.framework import get_app_local_store\nfrom privacyidea.lib import _\nimport logging\nfrom google.oauth2 import service_account\nfrom google.auth.transport.requests import AuthorizedSession\nimport json\nimport time\n\nFIREBASE_URL_SEND = 'https://fcm.googleapis.com/v1/projects/{0!s}/messages:send'\nSCOPES = ['https://www.googleapis.com/auth/cloud-platform',\n          'https://www.googleapis.com/auth/datastore',\n          'https://www.googleapis.com/auth/devstorage.read_write',\n          'https://www.googleapis.com/auth/firebase',\n          'https://www.googleapis.com/auth/identitytoolkit',\n          'https://www.googleapis.com/auth/userinfo.email']\n\nlog = logging.getLogger(__name__)\n\n\ndef get_firebase_access_token(config_file_name):\n    \"\"\"\n    This returns the access token for a given JSON config file name\n\n    :param config_file_name: The json file with the Service account credentials\n    :type config_file_name: str\n    :return: Firebase credentials\n    :rtype: google.oauth2.service_account.Credentials\n    \"\"\"\n    fbt = \"firebase_token\"\n    app_store = get_app_local_store()\n\n    if fbt not in app_store or not isinstance(app_store[fbt], dict):\n        # initialize the firebase_token in the app_store as dict\n        app_store[fbt] = {}\n\n    if not isinstance(app_store[fbt].get(config_file_name), service_account.Credentials) or \\\n            app_store[fbt].get(config_file_name).expired:\n        # If the type of the config is not of class Credentials or if the token\n        # has expired we get new scoped access token credentials\n        credentials = service_account.Credentials.from_service_account_file(config_file_name,\n                                                                            scopes=SCOPES)\n\n        log.debug(\"Fetching a new access_token for {!r} from firebase...\".format(config_file_name))\n        # We do not use a lock here: The worst that could happen is that two threads\n        # fetch new auth tokens concurrently. In this case, one of them wins and\n        # is written to the dictionary.\n        app_store[fbt][config_file_name] = credentials\n        readable_time = credentials.expiry.isoformat() if credentials.expiry else 'Never'\n        log.debug(u\"Setting the expiration for {!r} of the new access_token \"\n                  u\"to {!s}.\".format(config_file_name, readable_time))\n\n    return app_store[fbt][config_file_name]\n\n\nclass FIREBASE_CONFIG:\n    REGISTRATION_URL = \"registration URL\"\n    TTL = \"time to live\"\n    JSON_CONFIG = \"JSON config file\"\n    PROJECT_ID = \"projectid\"\n    PROJECT_NUMBER = \"projectnumber\"\n    APP_ID = \"appid\"\n    API_KEY = \"apikey\"\n    APP_ID_IOS = \"appidios\"\n    API_KEY_IOS = \"apikeyios\"\n    HTTPS_PROXY = \"httpsproxy\"\n\n\nclass FirebaseProvider(ISMSProvider):\n\n    def __init__(self, db_smsprovider_object=None, smsgateway=None):\n        ISMSProvider.__init__(self, db_smsprovider_object, smsgateway)\n        self.access_token_info = None\n        self.access_token_expires_at = 0\n\n    def submit_message(self, firebase_token, data):\n        \"\"\"\n        send a message to a registered Firebase client\n        This can be a simple OTP value or a cryptographic challenge response.\n\n        :param firebase_token: The firebase token of the smartphone\n        :type firebase_token: str\n        :param data: the data dictionary part of the message to submit to the phone\n        :type data: dict\n        :return: bool\n        \"\"\"\n        res = False\n\n        credentials = get_firebase_access_token(self.smsgateway.option_dict.get(\n            FIREBASE_CONFIG.JSON_CONFIG))\n\n        authed_session = AuthorizedSession(credentials)\n\n        headers = {\n            'Content-Type': 'application/json; UTF-8',\n        }\n        fcm_message = {\n            \"message\": {\n                        \"data\": data,\n                        \"token\": firebase_token,\n                        \"android\": {\n                                    \"priority\": \"HIGH\",\n                                    \"ttl\": \"120s\",\n                                    \"fcm_options\": {\"analytics_label\": \"AndroidPushToken\"}\n                                   },\n                        \"apns\": {\n                                 \"headers\": {\n                                             \"apns-priority\": \"10\",\n                                             \"apns-push-type\": \"alert\",\n                                             \"apns-collapse-id\": \"privacyidea.pushtoken\",\n                                             \"apns-expiration\": str(int(time.time()) + 120)\n                                            },\n                                 \"payload\": {\n                                             \"aps\": {\n                                                     \"alert\": {\n                                                               \"title\": data.get(\"title\"),\n                                                               \"body\": data.get(\"question\"),\n                                                              },\n                                                     \"sound\": \"default\",\n                                                     \"category\": \"PUSH_AUTHENTICATION\"\n                                                    },\n                                            },\n                                 \"fcm_options\": {\"analytics_label\": \"iOSPushToken\"}\n                                }\n                       }\n            }\n\n        proxies = {}\n        if self.smsgateway.option_dict.get(FIREBASE_CONFIG.HTTPS_PROXY):\n            proxies[\"https\"] = self.smsgateway.option_dict.get(FIREBASE_CONFIG.HTTPS_PROXY)\n        url = FIREBASE_URL_SEND.format(self.smsgateway.option_dict.get(FIREBASE_CONFIG.PROJECT_ID))\n        resp = authed_session.post(url, data=json.dumps(fcm_message), headers=headers, proxies=proxies)\n\n        if resp.status_code == 200:\n            log.debug(\"Message sent successfully to Firebase service.\")\n            res = True\n        else:\n            log.warning(u\"Failed to send message to firebase service: {0!s}\".format(resp.text))\n\n        return res\n\n    def check_configuration(self):\n        \"\"\"\n        This method checks the sanity of the configuration of this provider.\n        If there is a configuration error, than an exception is raised.\n        :return:\n        \"\"\"\n        json_file = self.smsgateway.option_dict.get(FIREBASE_CONFIG.JSON_CONFIG)\n        server_config = None\n        with open(json_file) as f:\n            server_config = json.load(f)\n        if server_config:\n            if server_config.get(\"type\") != \"service_account\":\n                raise ConfigAdminError(description=\"The JSON file is not a valid firebase credentials file.\")\n            project_id = self.smsgateway.option_dict.get(FIREBASE_CONFIG.PROJECT_ID)\n            if server_config.get(\"project_id\") != project_id:\n                raise ConfigAdminError(description=\"The project_id you entered does not match the project_id from the JSON file.\")\n\n        else:\n            raise ConfigAdminError(description=\"Please check your configuration. Can not load JSON file.\")\n\n        # We need at least\n        #         FIREBASE_CONFIG.API_KEY_IOS and FIREBASE_CONFIG.APP_ID_IOS\n        # or\n        #         FIREBASE_CONFIG.API_KEY and FIREBASE_CONFIG.APP_ID\n        android_configured = bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.APP_ID)) and \\\n                             bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.API_KEY))\n        ios_configured = bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.APP_ID_IOS)) and \\\n                             bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.API_KEY_IOS))\n        if not android_configured and not ios_configured:\n            raise ConfigAdminError(description=\"You need to at least configure either app_id and api_key or\"\n                                               \" app_id_ios and api_key_ios.\")\n\n    @classmethod\n    def parameters(cls):\n        \"\"\"\n        Return a dictionary, that describes the parameters and options for the\n        Firebase Provider\n        Parameters are required keys to values.\n\n        :return: dict\n        \"\"\"\n        params = {\"options_allowed\": False,\n                  \"headers_allowed\": False,\n                  \"parameters\": {\n                      FIREBASE_CONFIG.PROJECT_ID: {\n                          \"required\": True,\n                          \"description\": _(\"The project ID, that the client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.PROJECT_NUMBER: {\n                          \"required\": True,\n                          \"description\": _(\n                              \"The project number, that the client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.APP_ID: {\n                          \"required\": False,\n                          \"description\": _(\n                              \"The App ID, that the Android client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.API_KEY: {\n                          \"required\": False,\n                          \"description\": _(\n                              \"The API Key, that the Android client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.APP_ID_IOS:{\n                          \"required\": False,\n                          \"description\": _(\n                              \"The App ID, that the iOS client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.API_KEY_IOS: {\n                          \"required\": False,\n                          \"description\": _(\n                              \"The API Key, that the iOS client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.JSON_CONFIG: {\n                          \"required\": True,\n                          \"description\": _(\"The filename of the JSON config file, that allows privacyIDEA to talk\"\n                                           \" to the Firebase REST API.\")\n                      },\n                      FIREBASE_CONFIG.HTTPS_PROXY: {\n                          \"required\": False,\n                          \"description\": _(\"Proxy setting for HTTPS connections to googleapis.com.\")\n                      }\n                  }\n                  }\n        return params\n", "path": "privacyidea/lib/smsprovider/FirebaseProvider.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#    2019-02-12 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#\n#\n#    This program is free software: you can redistribute it and/or\n#    modify it under the terms of the GNU Affero General Public\n#    License, version 3, as published by the Free Software Foundation.\n#\n#    This program is distributed in the hope that it will be useful,\n#    but WITHOUT ANY WARRANTY; without even the implied warranty of\n#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n#    GNU Affero General Public License for more details.\n#\n#    You should have received a copy of the\n#               GNU Affero General Public License\n#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n#\n\n__doc__ = \"\"\"This is the provider class that communicates with Googles\nFirebase Cloud Messaging Service.\nThis provider is used for the push token and can be used for SMS tokens.\n\"\"\"\n\nfrom privacyidea.lib.smsprovider.SMSProvider import (ISMSProvider)\nfrom privacyidea.lib.error import ConfigAdminError\nfrom privacyidea.lib.framework import get_app_local_store\nfrom privacyidea.lib import _\nimport logging\nfrom google.oauth2 import service_account\nfrom google.auth.transport.requests import AuthorizedSession\nimport json\nimport time\n\nFIREBASE_URL_SEND = 'https://fcm.googleapis.com/v1/projects/{0!s}/messages:send'\nSCOPES = ['https://www.googleapis.com/auth/cloud-platform',\n          'https://www.googleapis.com/auth/datastore',\n          'https://www.googleapis.com/auth/devstorage.read_write',\n          'https://www.googleapis.com/auth/firebase',\n          'https://www.googleapis.com/auth/identitytoolkit',\n          'https://www.googleapis.com/auth/userinfo.email']\n\nlog = logging.getLogger(__name__)\n\n\ndef get_firebase_access_token(config_file_name):\n    \"\"\"\n    This returns the access token for a given JSON config file name\n\n    :param config_file_name: The json file with the Service account credentials\n    :type config_file_name: str\n    :return: Firebase credentials\n    :rtype: google.oauth2.service_account.Credentials\n    \"\"\"\n    fbt = \"firebase_token\"\n    app_store = get_app_local_store()\n\n    if fbt not in app_store or not isinstance(app_store[fbt], dict):\n        # initialize the firebase_token in the app_store as dict\n        app_store[fbt] = {}\n\n    if not isinstance(app_store[fbt].get(config_file_name), service_account.Credentials) or \\\n            app_store[fbt].get(config_file_name).expired:\n        # If the type of the config is not of class Credentials or if the token\n        # has expired we get new scoped access token credentials\n        credentials = service_account.Credentials.from_service_account_file(config_file_name,\n                                                                            scopes=SCOPES)\n\n        log.debug(\"Fetching a new access_token for {!r} from firebase...\".format(config_file_name))\n        # We do not use a lock here: The worst that could happen is that two threads\n        # fetch new auth tokens concurrently. In this case, one of them wins and\n        # is written to the dictionary.\n        app_store[fbt][config_file_name] = credentials\n        readable_time = credentials.expiry.isoformat() if credentials.expiry else 'Never'\n        log.debug(u\"Setting the expiration for {!r} of the new access_token \"\n                  u\"to {!s}.\".format(config_file_name, readable_time))\n\n    return app_store[fbt][config_file_name]\n\n\nclass FIREBASE_CONFIG:\n    REGISTRATION_URL = \"registration URL\"\n    TTL = \"time to live\"\n    JSON_CONFIG = \"JSON config file\"\n    PROJECT_ID = \"projectid\"\n    PROJECT_NUMBER = \"projectnumber\"\n    APP_ID = \"appid\"\n    API_KEY = \"apikey\"\n    APP_ID_IOS = \"appidios\"\n    API_KEY_IOS = \"apikeyios\"\n    HTTPS_PROXY = \"httpsproxy\"\n\n\nclass FirebaseProvider(ISMSProvider):\n\n    def __init__(self, db_smsprovider_object=None, smsgateway=None):\n        ISMSProvider.__init__(self, db_smsprovider_object, smsgateway)\n        self.access_token_info = None\n        self.access_token_expires_at = 0\n\n    def submit_message(self, firebase_token, data):\n        \"\"\"\n        send a message to a registered Firebase client\n        This can be a simple OTP value or a cryptographic challenge response.\n\n        :param firebase_token: The firebase token of the smartphone\n        :type firebase_token: str\n        :param data: the data dictionary part of the message to submit to the phone\n        :type data: dict\n        :return: bool\n        \"\"\"\n        res = False\n\n        credentials = get_firebase_access_token(self.smsgateway.option_dict.get(\n            FIREBASE_CONFIG.JSON_CONFIG))\n\n        authed_session = AuthorizedSession(credentials)\n\n        headers = {\n            'Content-Type': 'application/json; UTF-8',\n        }\n        fcm_message = {\n            \"message\": {\n                        \"data\": data,\n                        \"token\": firebase_token,\n                        \"notification\": {\n                            \"title\": data.get(\"title\"),\n                            \"body\": data.get(\"question\")\n                        },\n                        \"android\": {\n                                    \"priority\": \"HIGH\",\n                                    \"ttl\": \"120s\",\n                                    \"fcm_options\": {\"analytics_label\": \"AndroidPushToken\"}\n                                   },\n                        \"apns\": {\n                                 \"headers\": {\n                                             \"apns-priority\": \"10\",\n                                             \"apns-push-type\": \"alert\",\n                                             \"apns-collapse-id\": \"privacyidea.pushtoken\",\n                                             \"apns-expiration\": str(int(time.time()) + 120)\n                                            },\n                                 \"payload\": {\n                                             \"aps\": {\n                                                     \"alert\": {\n                                                               \"title\": data.get(\"title\"),\n                                                               \"body\": data.get(\"question\"),\n                                                              },\n                                                     \"sound\": \"default\",\n                                                     \"category\": \"PUSH_AUTHENTICATION\"\n                                                    },\n                                            },\n                                 \"fcm_options\": {\"analytics_label\": \"iOSPushToken\"}\n                                }\n                       }\n            }\n\n        proxies = {}\n        if self.smsgateway.option_dict.get(FIREBASE_CONFIG.HTTPS_PROXY):\n            proxies[\"https\"] = self.smsgateway.option_dict.get(FIREBASE_CONFIG.HTTPS_PROXY)\n        url = FIREBASE_URL_SEND.format(self.smsgateway.option_dict.get(FIREBASE_CONFIG.PROJECT_ID))\n        resp = authed_session.post(url, data=json.dumps(fcm_message), headers=headers, proxies=proxies)\n\n        if resp.status_code == 200:\n            log.debug(\"Message sent successfully to Firebase service.\")\n            res = True\n        else:\n            log.warning(u\"Failed to send message to firebase service: {0!s}\".format(resp.text))\n\n        return res\n\n    def check_configuration(self):\n        \"\"\"\n        This method checks the sanity of the configuration of this provider.\n        If there is a configuration error, than an exception is raised.\n        :return:\n        \"\"\"\n        json_file = self.smsgateway.option_dict.get(FIREBASE_CONFIG.JSON_CONFIG)\n        server_config = None\n        with open(json_file) as f:\n            server_config = json.load(f)\n        if server_config:\n            if server_config.get(\"type\") != \"service_account\":\n                raise ConfigAdminError(description=\"The JSON file is not a valid firebase credentials file.\")\n            project_id = self.smsgateway.option_dict.get(FIREBASE_CONFIG.PROJECT_ID)\n            if server_config.get(\"project_id\") != project_id:\n                raise ConfigAdminError(description=\"The project_id you entered does not match the project_id from the JSON file.\")\n\n        else:\n            raise ConfigAdminError(description=\"Please check your configuration. Can not load JSON file.\")\n\n        # We need at least\n        #         FIREBASE_CONFIG.API_KEY_IOS and FIREBASE_CONFIG.APP_ID_IOS\n        # or\n        #         FIREBASE_CONFIG.API_KEY and FIREBASE_CONFIG.APP_ID\n        android_configured = bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.APP_ID)) and \\\n                             bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.API_KEY))\n        ios_configured = bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.APP_ID_IOS)) and \\\n                             bool(self.smsgateway.option_dict.get(FIREBASE_CONFIG.API_KEY_IOS))\n        if not android_configured and not ios_configured:\n            raise ConfigAdminError(description=\"You need to at least configure either app_id and api_key or\"\n                                               \" app_id_ios and api_key_ios.\")\n\n    @classmethod\n    def parameters(cls):\n        \"\"\"\n        Return a dictionary, that describes the parameters and options for the\n        Firebase Provider\n        Parameters are required keys to values.\n\n        :return: dict\n        \"\"\"\n        params = {\"options_allowed\": False,\n                  \"headers_allowed\": False,\n                  \"parameters\": {\n                      FIREBASE_CONFIG.PROJECT_ID: {\n                          \"required\": True,\n                          \"description\": _(\"The project ID, that the client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.PROJECT_NUMBER: {\n                          \"required\": True,\n                          \"description\": _(\n                              \"The project number, that the client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.APP_ID: {\n                          \"required\": False,\n                          \"description\": _(\n                              \"The App ID, that the Android client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.API_KEY: {\n                          \"required\": False,\n                          \"description\": _(\n                              \"The API Key, that the Android client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.APP_ID_IOS:{\n                          \"required\": False,\n                          \"description\": _(\n                              \"The App ID, that the iOS client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.API_KEY_IOS: {\n                          \"required\": False,\n                          \"description\": _(\n                              \"The API Key, that the iOS client should use. Get it from your Firebase console.\")\n                      },\n                      FIREBASE_CONFIG.JSON_CONFIG: {\n                          \"required\": True,\n                          \"description\": _(\"The filename of the JSON config file, that allows privacyIDEA to talk\"\n                                           \" to the Firebase REST API.\")\n                      },\n                      FIREBASE_CONFIG.HTTPS_PROXY: {\n                          \"required\": False,\n                          \"description\": _(\"Proxy setting for HTTPS connections to googleapis.com.\")\n                      }\n                  }\n                  }\n        return params\n", "path": "privacyidea/lib/smsprovider/FirebaseProvider.py"}]}