From c1e8294ba0f3b8c34d094682d8a727d08872ae45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gregor=20D=C3=BCster?= Date: Sat, 6 Jan 2024 13:13:23 +0100 Subject: [PATCH] Enable download of Apple configuration profile for agdsn WLAN For further reference of this file, see: https://developer.apple.com/business/documentation/Configuration-Profile-Reference.pdf --- sipa/blueprints/usersuite.py | 31 +++ sipa/static/js/usersuite_index.js | 4 + sipa/templates/apple-mobileconfig.xml.j2 | 250 +++++++++++++++++++ sipa/templates/usersuite/_index_status.html | 15 ++ sipa/translations/de/LC_MESSAGES/messages.po | 6 + sipa/translations/en/LC_MESSAGES/messages.mo | Bin 26802 -> 27022 bytes sipa/translations/en/LC_MESSAGES/messages.po | 6 + 7 files changed, 312 insertions(+) create mode 100644 sipa/templates/apple-mobileconfig.xml.j2 diff --git a/sipa/blueprints/usersuite.py b/sipa/blueprints/usersuite.py index 1c6cda44..182b4cde 100644 --- a/sipa/blueprints/usersuite.py +++ b/sipa/blueprints/usersuite.py @@ -3,6 +3,7 @@ from collections import OrderedDict import logging from datetime import datetime +from io import BytesIO from babel.numbers import format_currency from flask import ( @@ -14,6 +15,7 @@ abort, request, current_app, + send_file, ) from flask_babel import format_date, gettext from flask_login import current_user, login_required @@ -622,3 +624,32 @@ def reset_wifi_password(): return render_template('generic_form.html', page_title=gettext("Neues WLAN Passwort"), form_args=form_args) + + +@bp_usersuite.route("/get-apple-wlan-mobileconfig", methods=["GET"]) +@login_required +def get_apple_wlan_mobileconfig(): + """ + Get the mobileconfig for the agdsn WLAN for an Apple device. + """ + + login = current_user.login.raw_value + wifi_password = current_user.wifi_password.raw_value + + if not wifi_password: + abort(404) + + return send_file( + BytesIO( + bytes( + render_template( + "apple-mobileconfig.xml.j2", + login=login, + wifi_password=wifi_password, + ), + encoding="utf-8", + ) + ), + as_attachment=True, + download_name="agdsn.mobileconfig", + ) diff --git a/sipa/static/js/usersuite_index.js b/sipa/static/js/usersuite_index.js index d0d194fe..d448858d 100644 --- a/sipa/static/js/usersuite_index.js +++ b/sipa/static/js/usersuite_index.js @@ -11,3 +11,7 @@ tocbot.init({ positionFixedSelector: '#usersuite-sidebar-nav', extraLinkClasses: 'text-decoration-none' }); + +// Enable popovers, see https://getbootstrap.com/docs/5.3/components/popovers/ +const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); +const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl)); diff --git a/sipa/templates/apple-mobileconfig.xml.j2 b/sipa/templates/apple-mobileconfig.xml.j2 new file mode 100644 index 00000000..308e27e1 --- /dev/null +++ b/sipa/templates/apple-mobileconfig.xml.j2 @@ -0,0 +1,250 @@ + + + + + PayloadContent + + + PayloadCertificateFileName + 485bce6d-706a-2c6e-f08e-0d8cfd51760d.der + PayloadContent + +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 + PayloadDescription + Das Identitätsprovider CA Zertifikat + PayloadDisplayName + Identitätsprovider CA #1 (Wurzel) + PayloadIdentifier + org.1x-config.agdsn.de.ag_dsn.ag_dsn.credential.0 + PayloadOrganization + agdsn.1x-config.org + PayloadType + com.apple.security.root + PayloadUUID + 485bce6d-706a-2c6e-f08e-0d8cfd51760d + PayloadVersion + 1 + + + PayloadCertificateFileName + 226c10b6-f359-6eb7-282d-7a98772d17e8.der + PayloadContent + +MIIGiTCCBHGgAwIBAgIQAKlt3DnTbr8VpJk/0Ci0djANBgkqhkiG9w0BAQsFADBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMB4XDTIxMDgwMzEzNDcxOVoXDTM2MTAyMzEz +NDcxOVowUTELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkG +A1UEAxMiU3dpc3NTaWduIFJTQSBUTFMgUm9vdCBDQSAyMDIxIC0gMTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBALzhzfwsQT177CwJfd954sSdhnx9hFrZ +pI7GEBHcuDroMf3wbD9LlPLb9/WqwwD4KMadimwny9JZabm5IBJXG5o21NI02bV7 +0LLYRsogKkynMMlhCFmjb/aBhHWv0mpQZ6xR1FjOLzWOpRjCbOm31AHH9oZvTjbG +6F0ZnQHEOpTzlMIEUjI+VmHV8eIXgLw7TVrabOfk1mz9OuVgnYorrjSkz9JuBdp5 +iN+Y/iCi84v4hHBmaoArJP0EQM8bJXtpYyb5NPYonfbX6vHNBsyAkraXxQiuDr3w +GbXrYC3filOyg1dXTDRmKmNzNiiy942fKsmJ+3vXMYLjMYb/tUZcPeUCFMzLh7dQ +i4gsS24/Vy4JesGNy08JNzBrYnPZE42RYXm9nMbsPX8E0YyYx8b6xGsd4Rx3rMSz +zV+IoJoJnPGIeOnZJKF3AxsLxnlnUzOgchb8kl03fY59SZwrXh/kdcxWH9WfO7d2 +NShu2gDdk2t6wSkmbuXWvuzDCpClHDOkj5IaSd+7IvNDY28CrAzVj6dPRBglxhlK +qIjo3vAoj1XVIzwyM00X2GyDAbQ4GYYmFu6p8p2xKgLOmmTfuwAtddSCdMY9+Wt7 +dW+7BYOxph95De2Loi3W+c+PfFPj1lv5h9K4XbkvVPf/jjukRL/Nvg8inThYNY3g +KVx8rig3jINtAgMBAAGjggFnMIIBYzAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSA84/Z/IJogVPB5IuXUB6p+cYctTAfBgNVHSMEGDAWgBRbJXuWpGVRfrg588B4 +Zl7oOufw7jAOBgNVHQ8BAf8EBAMCAQYwgf8GA1UdHwSB9zCB9DBHoEWgQ4ZBaHR0 +cDovL2NybC5zd2lzc3NpZ24ubmV0LzVCMjU3Qjk2QTQ2NTUxN0VCODM5RjNDMDc4 +NjY1RUU4M0FFN0YwRUUwgaiggaWggaKGgZ9sZGFwOi8vZGlyZWN0b3J5LnN3aXNz +c2lnbi5uZXQvQ049NUIyNTdCOTZBNDY1NTE3RUI4MzlGM0MwNzg2NjVFRTgzQUU3 +RjBFRSUyQ089U3dpc3NTaWduJTJDQz1DSD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25M +aXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwDQYJKoZI +hvcNAQELBQADggIBAACe84DiHCZxPRIEZSRBIg2MzdYbmrMNPz9yiITWDxAGde85 +9NYhCDtTN1t1VVRG6PkSmMDOhZ/YFRZ49VYqTGmLpCQufSdGZZHGkTMRbrRge5Jr +HLMbZr6Jufhad049iEJW2Iwn3ESR4vxQ4nsYcbpvL+CZXspcXGYclCzlpjHsG7YW +biGWhMzT04fvOebO6JWDfRKWV8eEfpqIYaAMrfQBSqWK1Gi4zNFRo6g8jvDzIJch +BS7DSBLBxTcUkm4qAvtGrTvPtBMP9nZ+Q04BOXds72pS16R87lbhHx3hvgevIFDY +8iwdTl3o/9LfHc4zpaa8+lP/r08TBRXGhT614AhpFuzK+RHkaHKFPYjFA1Vd5DxU +6os5l/clzDBSB3VRfci+cXD6KmnkCuEhFuOjrpilTMVjJss1FbkSzyXecyNtbYia +k7aR3lXRjvqUPSN+JiRk/DvwrIb1VsqmlYmig0+UD6lrGGVlnxWq8A8krbOSIotf +SZxO85QgNalf17zyHuY6btD5nDDEjeecU9CpCbp8YuoGd1/TML+zovEMwJj57uNO +dwnuUojcDbtIw3t3PUb92ItVJj2Iljxi+E30rjgSjHzd74f2RzAvBF6zW8994Zzl +BHN5upD76jjk1rDSBEl6PJOoZCDiyVw17mhw10E44oCy+CtMKDL4lHI9RsGa + PayloadDescription + Das Identitätsprovider CA Zertifikat + PayloadDisplayName + Identitätsprovider CA #2 (Zwischenstelle) + PayloadIdentifier + org.1x-config.agdsn.de.ag_dsn.ag_dsn.credential.1 + PayloadOrganization + agdsn.1x-config.org + PayloadType + com.apple.security.root + PayloadUUID + 226c10b6-f359-6eb7-282d-7a98772d17e8 + PayloadVersion + 1 + + + PayloadCertificateFileName + fb9cb854-1ec3-8b65-4a9b-ff0d067aea7f.der + PayloadContent + +MIIHVzCCBT+gAwIBAgIQAN5Yhr4YCoH/Vnm/sJqnZzANBgkqhkiG9w0BAQsFADBR +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSswKQYDVQQDEyJT +d2lzc1NpZ24gUlNBIFRMUyBSb290IENBIDIwMjEgLSAxMB4XDTIxMDgwMzE4Mzcz +NFoXDTM2MDczMDE4MzczNFowUDELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNz +U2lnbiBBRzEqMCgGA1UEAxMhU3dpc3NTaWduIFJTQSBUTFMgRFYgSUNBIDIwMjEg +LSAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuS6VTH2Rj/ANrxtK +BirJk5ZOhgK9rk/g+/voIDIQ1HpLISBC5Rc8W5HRDmk2kOmmV1qsrjSXFM4Y4t+K +Gcgflg7fAw3V9OiKI5VjvC/Dg165rrfB0H5gr2VwKAQPP9cHiK/siOsj78gbOr88 ++chCDk5A2fZbvstBZrK8zpNVObcsMbWZgDHRM1brVPf8U1/7zHRk3BVHtdsETbzk ++F/gHjs85DkIxPs7kdeJba7AtReFDiw4sL9uUrYRXKICZES/gU7BbRaBGXloykk9 +ntfZkJ3xwhj7lk2hePFxs81nAb3VwHyLhhWOEryb63iHZn27ut9SVkCFfWGOyGgJ +op6K4HNDTFxqOSw2vpOMfzoZ/L8NlwJq/sRvxGBhhN430SkiA4sGukm55GZ2J6wM +C4IzRv4MQTRTFgIU1mo09ZC2wWestw4CeIPykNVq0K70OoqWs8YmLhkDLlhXN7P+ +ACFfwu8Lu2Adlby9oVpmCFq//3icIspzsc3MTHizGB8/f80/NjKgQhvDnH+wWdW8 +/TDrV3swugtMxmxh1IMNJXEEEBGl9snLDX5PI5UK3toZevfzDnQeK1WXGbL8A7de +5YCoA7ANMIPvSL0Zmvlxpy0xT9xKYfQFNKFzLzD8c2Dt28SDDbqM0txS9y58gsff +Qwe6fmQr3pKENfGHzBHgWxUuIxsCAwEAAaOCAiowggImMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB +/wIBADAdBgNVHQ4EFgQUPJ5SeQNjb0+cgRvTKHAMJFrqpYcwHwYDVR0jBBgwFoAU +gPOP2fyCaIFTweSLl1AeqfnGHLUwgf8GA1UdHwSB9zCB9DBHoEWgQ4ZBaHR0cDov +L2NybC5zd2lzc3NpZ24ubmV0LzgwRjM4RkQ5RkM4MjY4ODE1M0MxRTQ4Qjk3NTAx +RUE5RjlDNjFDQjUwgaiggaWggaKGgZ9sZGFwOi8vZGlyZWN0b3J5LnN3aXNzc2ln +bi5uZXQvQ049ODBGMzhGRDlGQzgyNjg4MTUzQzFFNDhCOTc1MDFFQTlGOUM2MUNC +NSUyQ089U3dpc3NTaWduJTJDQz1DSD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0 +P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwKQYDVR0gBCIw +IDAKBghghXQBWQIBATAIBgZngQwBAgEwCAYGBACPegEGMHQGCCsGAQUFBwEBBGgw +ZjBkBggrBgEFBQcwAoZYaHR0cDovL3N3aXNzc2lnbi5uZXQvY2dpLWJpbi9hdXRo +b3JpdHkvZG93bmxvYWQvODBGMzhGRDlGQzgyNjg4MTUzQzFFNDhCOTc1MDFFQTlG +OUM2MUNCNTANBgkqhkiG9w0BAQsFAAOCAgEAaw8l7Y1DquXDxSxTSnUc0YaC73V+ +uHw/ksizATpHy6R5PLZqhfRTd1L0MbSe8ixQ28h/++H1+OmV1MpKayS1U/8dmhQB +qOic9bCytIbuTeYEXzwSWDz6ZG5BZJveHj+z/v0wZCDU7ciF1zZbMY3deKOcAk+N +bD+LoAUxaMEarrkmAKRFRuYZPtKqcbRC3SQUVuem7sz/bpkIBJoaE/04G2Th/bNY +kRdcuVk239EjXWFerCfjmY6WrHYT3Bo7D1VEiZJEJwpY/TR89CNtRsgkLkDBbLdH +mXMpgxVfXcBPRfWrWYPdKTP5kuL3uBX6/UpnMs+GJWHUYoCU4NfDFFhAXglyjW0S +nDFr8efbPQf6S8gjs+P0Cviwcg8gSq3APfOrACgRs6M+jscQo0+ru+Jx0ACnhm6x +Xzl3Jq5Dbt9sAVrAlJRFYDD6Tm6ZfXPRFsSHUYtmEm8+wmJCNsrpQVkpwWEB8ATa +2ZeRpcPd201t/bYWdV++EIIkSwQYrbbT4Vwu6m/jOga7/GS/VazYOu3fnLZ9XcAO +bZ9uub9Xd8prtYO4DlEBxFHKAbBXlNLQEYYHrbF/OdqBCvVhtns0Dck2Q2Dr3bCU +7dNSJ5mtqaoIiqnYpWJoqUhwOo/vJSbgFkzVGheRWo8yaUh7JfswYAkW5hPaShF0 +LMVpEb0hwOAW4Fg= + PayloadDescription + Das Identitätsprovider CA Zertifikat + PayloadDisplayName + Identitätsprovider CA #3 (Zwischenstelle) + PayloadIdentifier + org.1x-config.agdsn.de.ag_dsn.ag_dsn.credential.2 + PayloadOrganization + agdsn.1x-config.org + PayloadType + com.apple.security.root + PayloadUUID + fb9cb854-1ec3-8b65-4a9b-ff0d067aea7f + PayloadVersion + 1 + + + EAPClientConfiguration + + AcceptEAPTypes + + 21 + + UserName + {{ login }} + UserPassword + {{ wifi_password }} + EAPFASTProvisionPAC + + EAPFASTUsePAC + + EAPFastProvisionPACAnonymously + + OneTimeUserPassword + + OuterIdentity + wifi@agdsn.de + PayloadCertificateAnchorUUID + + 485bce6d-706a-2c6e-f08e-0d8cfd51760d + 226c10b6-f359-6eb7-282d-7a98772d17e8 + fb9cb854-1ec3-8b65-4a9b-ff0d067aea7f + + TLSAllowTrustExceptions + + TLSTrustedServerNames + + radius.agdsn.de + + TTLSInnerAuthentication + PAP + + EncryptionType + WPA2 + HIDDEN_NETWORK + + PayloadDescription + Organisation Custom Network Konfiguration für das Netzwerk agdsn + PayloadDisplayName + Organisation Custom Network - SSID agdsn + PayloadIdentifier + org.1x-config.agdsn.de.ag_dsn.ag_dsn.de_DE.wifi.0 + PayloadOrganization + agdsn.1x-config.org + PayloadType + com.apple.wifi.managed + ProxyType + Auto + ProxyPACFallbackAllowed + + PayloadUUID + 8ee5540e-1d3c-f9ac-a662-8760e60a1e7e + PayloadVersion + 1 + SSID_STR + agdsn + + + PayloadDescription + Netzwerkprofil 'AG DSN' von 'AG DSN' - bereitgestellt von AG DSN + PayloadDisplayName + AG DSN + PayloadIdentifier + org.1x-config.agdsn.de.ag_dsn.ag_dsn.de_DE + PayloadOrganization + AG DSN + PayloadType + Configuration + PayloadUUID + d483aa96-74cd-0219-b9ff-4709da705721 + PayloadVersion + 1 + + diff --git a/sipa/templates/usersuite/_index_status.html b/sipa/templates/usersuite/_index_status.html index fec98db8..092c87b6 100644 --- a/sipa/templates/usersuite/_index_status.html +++ b/sipa/templates/usersuite/_index_status.html @@ -48,6 +48,21 @@

{{ _("Häufige Aktionen") }}

{% endif %} {% endif %} + +
+{% if current_user.wifi_password.raw_value %} + + + {{ _("Apple WLAN-Konfigurationsprofil herunterladen") }} + +{% else %} + {{ _("hier") }}.'> + + {{ _("Apple WLAN-Konfigurationsprofil herunterladen") }} + +{% endif %} +
+
diff --git a/sipa/translations/de/LC_MESSAGES/messages.po b/sipa/translations/de/LC_MESSAGES/messages.po index f0eb85c2..ee8080b8 100644 --- a/sipa/translations/de/LC_MESSAGES/messages.po +++ b/sipa/translations/de/LC_MESSAGES/messages.po @@ -1059,6 +1059,12 @@ msgstr "" msgid "Ausgehend" msgstr "" +msgid "Apple WLAN-Konfigurationsprofil herunterladen" +msgstr "" + +msgid "Du benötigst zuerst ein WLAN-Passwort. Erstelle eines" +msgstr "" + #~ msgid "Bezahlt, verbunden" #~ msgstr "" diff --git a/sipa/translations/en/LC_MESSAGES/messages.mo b/sipa/translations/en/LC_MESSAGES/messages.mo index e76343a59194e8e0d54037e5fe54a9a77d5f900c..c26d9a3c503ecd5ec8edecea8fd0155d9fcfd135 100644 GIT binary patch delta 5476 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