feat: add auth support (default and paia)

This commit is contained in:
Michel Jonathan Schmitz
2022-01-24 18:43:00 +00:00
committed by Jovan Krunić
parent 046a95ba1d
commit b5f239ea4e
85 changed files with 3626 additions and 119 deletions

View File

@@ -41,7 +41,7 @@
"unicorn/no-array-callback-reference": "off", "unicorn/no-array-callback-reference": "off",
"unicorn/prefer-object-from-entries": "off", "unicorn/prefer-object-from-entries": "off",
"unicorn/prevent-abbreviations": [ "unicorn/prevent-abbreviations": [
"error", "warn",
{ {
"replacements": { "replacements": {
"ref": false, "ref": false,

View File

@@ -3,7 +3,7 @@ apply plugin: 'com.android.application'
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig { defaultConfig {
applicationId "de.any_school.app" applicationId "de.anyschool.app"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1 versionCode 1

View File

@@ -9,12 +9,15 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies { dependencies {
implementation project(':capacitor-community-http')
implementation project(':capacitor-app') implementation project(':capacitor-app')
implementation project(':capacitor-browser') implementation project(':capacitor-browser')
implementation project(':capacitor-haptics') implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard') implementation project(':capacitor-keyboard')
implementation project(':capacitor-splash-screen') implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar') implementation project(':capacitor-status-bar')
implementation project(':capacitor-storage')
implementation project(':capacitor-secure-storage-plugin')
implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.3.1" implementation "androidx.appcompat:appcompat:1.3.1"
} }

View File

@@ -1,49 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest package="de.anyschool.app" xmlns:android="http://schemas.android.com/apk/res/android">
package="de.any_school.app"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:label="@string/title_activity_main" android:launchMode="singleTask" android:name="de.anyschool.app.MainActivity" android:theme="@style/AppTheme.NoActionBarLaunch">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:name="de.any_school.app.MainActivity"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<intent-filter android:autoVerify="true"> <action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="@string/custom_url_scheme" />
<!-- NEXT LINE'S HOST TO BE GENERATED (SCHOOL SPECIFIC) --> </intent-filter>
<data android:scheme="https" android:host="mobile.app.uni-frankfurt.de" /> <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="mobile.app.uni-frankfurt.de" android:scheme="https" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true" android:name="androidx.core.content.FileProvider">
<provider <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider> </provider>
</application> </application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
</manifest> </manifest>

View File

@@ -1,5 +1,5 @@
{ {
"appId": "de.any_school.app", "appId": "de.anyschool.app",
"appName": "StApps", "appName": "StApps",
"webDir": "www", "webDir": "www",
"bundledWebRuntime": false, "bundledWebRuntime": false,

View File

@@ -1,4 +1,8 @@
[ [
{
"pkg": "@capacitor-community/http",
"classpath": "com.getcapacitor.plugin.http.Http"
},
{ {
"pkg": "@capacitor/app", "pkg": "@capacitor/app",
"classpath": "com.capacitorjs.plugins.app.AppPlugin" "classpath": "com.capacitorjs.plugins.app.AppPlugin"
@@ -22,5 +26,13 @@
{ {
"pkg": "@capacitor/status-bar", "pkg": "@capacitor/status-bar",
"classpath": "com.capacitorjs.plugins.statusbar.StatusBarPlugin" "classpath": "com.capacitorjs.plugins.statusbar.StatusBarPlugin"
},
{
"pkg": "@capacitor/storage",
"classpath": "com.capacitorjs.plugins.storage.StoragePlugin"
},
{
"pkg": "capacitor-secure-storage-plugin",
"classpath": "com.whitestein.securestorage.SecureStoragePlugin"
} }
] ]

View File

@@ -1,4 +1,4 @@
package de.any_school.app; package de.anyschool.app;
import com.getcapacitor.BridgeActivity; import com.getcapacitor.BridgeActivity;

View File

@@ -2,6 +2,6 @@
<resources> <resources>
<string name="app_name">StApps</string> <string name="app_name">StApps</string>
<string name="title_activity_main">StApps</string> <string name="title_activity_main">StApps</string>
<string name="package_name">de.any_school.app</string> <string name="package_name">de.anyschool.app</string>
<string name="custom_url_scheme">de.any_school.app</string> <string name="custom_url_scheme">de.anyschool.app</string>
</resources> </resources>

View File

@@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.google.gms:google-services:4.3.5' classpath 'com.google.gms:google-services:4.3.5'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View File

@@ -2,6 +2,9 @@
include ':capacitor-android' include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-community-http'
project(':capacitor-community-http').projectDir = new File('../node_modules/@capacitor-community/http/android')
include ':capacitor-app' include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
@@ -19,3 +22,9 @@ project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capa
include ':capacitor-status-bar' include ':capacitor-status-bar'
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
include ':capacitor-storage'
project(':capacitor-storage').projectDir = new File('../node_modules/@capacitor/storage/android')
include ':capacitor-secure-storage-plugin'
project(':capacitor-secure-storage-plugin').projectDir = new File('../node_modules/capacitor-secure-storage-plugin/android')

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -1,7 +1,7 @@
import {CapacitorConfig} from '@capacitor/cli'; import {CapacitorConfig} from '@capacitor/cli';
const config: CapacitorConfig = { const config: CapacitorConfig = {
appId: 'de.any_school.app', appId: 'de.anyschool.app',
appName: 'StApps', appName: 'StApps',
webDir: 'www', webDir: 'www',
bundledWebRuntime: false, bundledWebRuntime: false,

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<widget id="de.any_school.app" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <widget id="de.anyschool.app" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>StApps</name> <name>StApps</name>
<description>An awesome Ionic/Cordova app.</description> <description>An awesome Ionic/Cordova app.</description>
<author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author> <author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author>
@@ -21,10 +21,6 @@
<preference name="SplashScreen" value="screen" /> <preference name="SplashScreen" value="screen" />
<preference name="SplashScreenDelay" value="3000" /> <preference name="SplashScreenDelay" value="3000" />
<plugin name="cordova-plugin-whitelist" spec="1.3.3" /> <plugin name="cordova-plugin-whitelist" spec="1.3.3" />
<plugin name="cordova-plugin-device" spec="2.0.2" />
<plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
<plugin name="cordova-plugin-ionic-webview" spec="2.0.0" />
<plugin name="cordova-plugin-ionic-keyboard" spec="2.0.5" />
<plugin name="cordova-plugin-geolocation" spec="4.0.1"> <plugin name="cordova-plugin-geolocation" spec="4.0.1">
<variable name="GEOLOCATION_USAGE_DESCRIPTION" value="The app will use your location to provide features for navigation or distances information." /> <variable name="GEOLOCATION_USAGE_DESCRIPTION" value="The app will use your location to provide features for navigation or distances information." />
</plugin> </plugin>

9
ios/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
App/build
App/Pods
App/Podfile.lock
App/App/public
DerivedData
xcuserdata
# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins

View File

@@ -0,0 +1,428 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
E2D249FB277CB255005492AC /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
504EC3011FED79650016851F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
isa = PBXGroup;
children = (
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
504EC2FB1FED79650016851F = {
isa = PBXGroup;
children = (
504EC3061FED79650016851F /* App */,
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
);
sourceTree = "<group>";
};
504EC3051FED79650016851F /* Products */ = {
isa = PBXGroup;
children = (
504EC3041FED79650016851F /* App.app */,
);
name = Products;
sourceTree = "<group>";
};
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
E2D249FB277CB255005492AC /* App.entitlements */,
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */,
504EC30E1FED79650016851F /* Assets.xcassets */,
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
504EC3131FED79650016851F /* Info.plist */,
2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */,
);
path = App;
sourceTree = "<group>";
};
7F8756D8B27F46E3366F6CEA /* Pods */ = {
isa = PBXGroup;
children = (
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
504EC3031FED79650016851F /* App */ = {
isa = PBXNativeTarget;
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
buildPhases = (
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
504EC3001FED79650016851F /* Sources */,
504EC3011FED79650016851F /* Frameworks */,
504EC3021FED79650016851F /* Resources */,
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
0C3780443725062B779B937E /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = App;
productName = App;
productReference = 504EC3041FED79650016851F /* App.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
504EC2FC1FED79650016851F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0920;
TargetAttributes = {
504EC3031FED79650016851F = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 504EC2FB1FED79650016851F;
productRefGroup = 504EC3051FED79650016851F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
504EC3031FED79650016851F /* App */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
504EC3021FED79650016851F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
50B271D11FEDC1A000F3C39B /* public in Resources */,
504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0C3780443725062B779B937E /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-resources.sh\"\n";
showEnvVarsInLog = 0;
};
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
504EC3001FED79650016851F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
504EC30B1FED79650016851F /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC30C1FED79650016851F /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC3111FED79650016851F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
504EC3141FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
504EC3151FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
504EC3171FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 8C99SX84P9;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = de.anyschool.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
504EC3181FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 8C99SX84P9;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = de.anyschool.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3141FED79650016851F /* Debug */,
504EC3151FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3171FED79650016851F /* Debug */,
504EC3181FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 504EC2FC1FED79650016851F /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:App.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:App.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:mobile.app.uni-frankfurt.de</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,60 @@
import UIKit
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// Called when the app was launched with an activity, including Universal Links.
// Feel free to add additional processing here, but if you want the App API to support
// tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let statusBarRect = UIApplication.shared.statusBarFrame
guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }
if statusBarRect.contains(touchPoint) {
NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil)
}
}
}

View File

@@ -0,0 +1,116 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "AppIcon-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "AppIcon-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon-29x29@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "AppIcon-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "AppIcon-20x20@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon-40x40@2x-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "AppIcon-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "AppIcon-512@2x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "splash-2732x2732-2.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "splash-2732x2732-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "splash-2732x2732.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</imageView>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="Splash" width="1366" height="1366"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
</dependencies>
<scenes>
<!--Bridge View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

68
ios/App/App/Info.plist Normal file
View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>StApps</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>de.anyschool.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>de.anyschool.app</string>
</array>
</dict>
<dict/>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,19 @@
{
"appId": "de.anyschool.app",
"appName": "StApps",
"webDir": "www",
"bundledWebRuntime": false,
"cordova": {
"preferences": {
"AndroidXEnabled": "true",
"ScrollEnabled": "false",
"android-minSdkVersion": "22",
"BackupWebStorage": "none",
"SplashMaintainAspectRatio": "true",
"FadeSplashScreenDuration": "300",
"SplashShowOnlyFirstTime": "false",
"SplashScreen": "screen",
"SplashScreenDelay": "3000"
}
}
}

86
ios/App/App/config.xml Normal file
View File

@@ -0,0 +1,86 @@
<?xml version='1.0' encoding='utf-8'?>
<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<access origin="*" />
<feature name="Device">
<param name="ios-package" value="CDVDevice"/>
</feature>
<feature name="Notification">
<param name="ios-package" value="CDVNotification"/>
</feature>
<feature name="Geolocation">
<param name="ios-package" value="CDVLocation"/>
</feature>
<feature name="NetworkStatus">
<param name="ios-package" value="CDVConnection"/>
</feature>
<feature name="Diagnostic">
<param name="ios-package" value="Diagnostic"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Location">
<param name="ios-package" value="Diagnostic_Location"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Bluetooth">
<param name="ios-package" value="Diagnostic_Bluetooth"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Wifi">
<param name="ios-package" value="Diagnostic_Wifi"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Camera">
<param name="ios-package" value="Diagnostic_Camera"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Notifications">
<param name="ios-package" value="Diagnostic_Notifications"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Microphone">
<param name="ios-package" value="Diagnostic_Microphone"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Contacts">
<param name="ios-package" value="Diagnostic_Contacts"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Calendar">
<param name="ios-package" value="Diagnostic_Calendar"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Reminders">
<param name="ios-package" value="Diagnostic_Reminders"/>
<param name="onload" value="true"/>
</feature>
<feature name="Diagnostic_Motion">
<param name="ios-package" value="Diagnostic_Motion"/>
<param name="onload" value="true"/>
</feature>
<preference name="AndroidXEnabled" value="true" />
<preference name="ScrollEnabled" value="false" />
<preference name="android-minSdkVersion" value="22" />
<preference name="BackupWebStorage" value="none" />
<preference name="SplashMaintainAspectRatio" value="true" />
<preference name="FadeSplashScreenDuration" value="300" />
<preference name="SplashShowOnlyFirstTime" value="false" />
<preference name="SplashScreen" value="screen" />
<preference name="SplashScreenDelay" value="3000" />
</widget>

28
ios/App/Podfile Normal file
View File

@@ -0,0 +1,28 @@
platform :ios, '12.0'
use_frameworks!
# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCommunityHttp', :path => '../../node_modules/@capacitor-community/http'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorBrowser', :path => '../../node_modules/@capacitor/browser'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
pod 'CapacitorStorage', :path => '../../node_modules/@capacitor/storage'
pod 'CapacitorSecureStoragePlugin', :path => '../../node_modules/capacitor-secure-storage-plugin'
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
pod 'CordovaPluginsResources', :path => '../capacitor-cordova-ios-plugins'
end
target 'App' do
capacitor_pods
# Add your Pods here
end

238
package-lock.json generated
View File

@@ -2067,11 +2067,21 @@
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
}, },
"@capacitor-community/http": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@capacitor-community/http/-/http-1.4.1.tgz",
"integrity": "sha512-+pCkBXrwfm97UfjOgjV950H/qZ8SE36Mrcb46BlL1ps3VIsGuIO+AulL8GqTC6LewheRVtGJpRspNtneXQotNA==",
"requires": {
"@capacitor/android": "^3.0.0",
"@capacitor/core": "^3.0.0",
"@capacitor/filesystem": "^1.0.0",
"@capacitor/ios": "^3.0.0"
}
},
"@capacitor/android": { "@capacitor/android": {
"version": "3.3.2", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-3.3.2.tgz", "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-3.4.0.tgz",
"integrity": "sha512-TG+tGz0KxkT/BgvSLQfbQwQ9c4Budub5TRijIGdmMbB1ZYB76TFhwvVuwWZ52HFSlKS3sx/UYLlbULL7UQ2aug==", "integrity": "sha512-O2hHGVzdTH2Lsmz58EI8zHY5byEFIWl9KrW60WSrO/tV6u9DpfaUq56FaejvfU27GFXDZkmoQNa33EvDYWp4wA=="
"dev": true
}, },
"@capacitor/app": { "@capacitor/app": {
"version": "1.0.6", "version": "1.0.6",
@@ -2152,6 +2162,11 @@
} }
} }
}, },
"@capacitor/filesystem": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@capacitor/filesystem/-/filesystem-1.1.0.tgz",
"integrity": "sha512-8O3UuvL8HNUEJvZnmn8yUmvgB1evtXfcF0oxIo3YbSlylqywJwS3JTiuhKmsvSxCdpbTy8IaTsutVh3gZgWbKg=="
},
"@capacitor/haptics": { "@capacitor/haptics": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-1.1.3.tgz",
@@ -2160,8 +2175,7 @@
"@capacitor/ios": { "@capacitor/ios": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-3.3.2.tgz", "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-3.3.2.tgz",
"integrity": "sha512-qXbWo9zTtinIYDxKsLdkXXuZD8H+wlJII3+ZF9QzH+38IYyd+sohOG3NLC8EaX7GRtwCymd+mGotoN420SfQ4Q==", "integrity": "sha512-qXbWo9zTtinIYDxKsLdkXXuZD8H+wlJII3+ZF9QzH+38IYyd+sohOG3NLC8EaX7GRtwCymd+mGotoN420SfQ4Q=="
"dev": true
}, },
"@capacitor/keyboard": { "@capacitor/keyboard": {
"version": "1.1.3", "version": "1.1.3",
@@ -2178,6 +2192,11 @@
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-1.0.6.tgz", "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-1.0.6.tgz",
"integrity": "sha512-5MGWFq76iiKvHpbZ/Xc0Zig3WZyzWZ62wvC4qxak8OuVHBNG4fA1p/XXY9teQPaU3SupEJHnLkw6Gn1LuDp+ew==" "integrity": "sha512-5MGWFq76iiKvHpbZ/Xc0Zig3WZyzWZ62wvC4qxak8OuVHBNG4fA1p/XXY9teQPaU3SupEJHnLkw6Gn1LuDp+ew=="
}, },
"@capacitor/storage": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@capacitor/storage/-/storage-1.2.3.tgz",
"integrity": "sha512-Rc5CKS53sfxokF5dxzNQDhig4lnZonky6VqskHZKTe3Ltl37FKmrG+I8ttZCinFZ5MPWfGSuP44m93hsQqitjQ=="
},
"@compodoc/compodoc": { "@compodoc/compodoc": {
"version": "1.1.14", "version": "1.1.14",
"resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.14.tgz", "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.1.14.tgz",
@@ -2532,6 +2551,40 @@
"@types/cordova": "^0.0.34" "@types/cordova": "^0.0.34"
} }
}, },
"@ionic-native/http": {
"version": "5.35.0",
"resolved": "https://registry.npmjs.org/@ionic-native/http/-/http-5.35.0.tgz",
"integrity": "sha512-AaLvi59gdOlxpM3x9krk0l8UcLI9OLBm+o7DNu8y1pgsUKIvf7+ZbY4xU1fJG1HDvUyrU8+NQLrvXgpXx6CmAQ==",
"optional": true,
"requires": {
"@types/cordova": "^0.0.34"
},
"dependencies": {
"@types/cordova": {
"version": "0.0.34",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=",
"optional": true
}
}
},
"@ionic-native/in-app-browser": {
"version": "5.36.0",
"resolved": "https://registry.npmjs.org/@ionic-native/in-app-browser/-/in-app-browser-5.36.0.tgz",
"integrity": "sha512-tX/FBT0jpkgEefZ8iorv5eDKfgP/ExbYr1AWg6okORQ0dwLfXsD5KDJgKHN9GFZvyuLNeaLpC1mN7CvwvLvmgA==",
"optional": true,
"requires": {
"@types/cordova": "^0.0.34"
},
"dependencies": {
"@types/cordova": {
"version": "0.0.34",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=",
"optional": true
}
}
},
"@ionic-native/network": { "@ionic-native/network": {
"version": "5.36.0", "version": "5.36.0",
"resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-5.36.0.tgz", "resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-5.36.0.tgz",
@@ -2540,6 +2593,40 @@
"@types/cordova": "^0.0.34" "@types/cordova": "^0.0.34"
} }
}, },
"@ionic-native/safari-view-controller": {
"version": "5.36.0",
"resolved": "https://registry.npmjs.org/@ionic-native/safari-view-controller/-/safari-view-controller-5.36.0.tgz",
"integrity": "sha512-pvqnzro3bBZ0bQOMjBRKhmjHDaLKfDS75QY7uqe9UzjufMnHtBUUWgMvTuL7MsjTXRj8iRhe1wnUv8aBkz4SVA==",
"optional": true,
"requires": {
"@types/cordova": "^0.0.34"
},
"dependencies": {
"@types/cordova": {
"version": "0.0.34",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=",
"optional": true
}
}
},
"@ionic-native/secure-storage": {
"version": "5.35.0",
"resolved": "https://registry.npmjs.org/@ionic-native/secure-storage/-/secure-storage-5.35.0.tgz",
"integrity": "sha512-QJSMGsvYOFWX95zMbCMMfNdtOnx9su6GKPmw6p+lLYWTXa5bqqRMSRtPInQmv1dz6GCcAMdXbUGADjBgwQQsaA==",
"optional": true,
"requires": {
"@types/cordova": "^0.0.34"
},
"dependencies": {
"@types/cordova": {
"version": "0.0.34",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
"integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=",
"optional": true
}
}
},
"@ionic/angular": { "@ionic/angular": {
"version": "5.7.0", "version": "5.7.0",
"resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.7.0.tgz", "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.7.0.tgz",
@@ -3216,6 +3303,31 @@
} }
} }
}, },
"@openid/appauth": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@openid/appauth/-/appauth-1.3.1.tgz",
"integrity": "sha512-e54kpi219wES2ijPzeHe1kMnT8VKH8YeTd1GAn9BzVBmutz3tBgcG1y8a4pziNr4vNjFnuD4W446Ua7ELnNDiA==",
"requires": {
"@types/base64-js": "^1.3.0",
"@types/jquery": "^3.5.5",
"base64-js": "^1.5.1",
"follow-redirects": "^1.13.3",
"form-data": "^4.0.0",
"opener": "^1.5.2"
},
"dependencies": {
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@openstapps/api": { "@openstapps/api": {
"version": "0.35.0", "version": "0.35.0",
"resolved": "https://registry.npmjs.org/@openstapps/api/-/api-0.35.0.tgz", "resolved": "https://registry.npmjs.org/@openstapps/api/-/api-0.35.0.tgz",
@@ -3504,6 +3616,11 @@
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA=="
}, },
"@types/base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw=="
},
"@types/body-parser": { "@types/body-parser": {
"version": "1.19.2", "version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@@ -3698,6 +3815,14 @@
"@types/jasmine": "*" "@types/jasmine": "*"
} }
}, },
"@types/jquery": {
"version": "3.5.8",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.8.tgz",
"integrity": "sha512-cXk6NwqjDYg+UI9p2l3x0YmPa4m7RrXqmbK4IpVVpRJiYXU/QTo+UZrn54qfE1+9Gao4qpYqUnxm5ZCy2FTXAw==",
"requires": {
"@types/sizzle": "*"
}
},
"@types/json-patch": { "@types/json-patch": {
"version": "0.0.30", "version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/json-patch/-/json-patch-0.0.30.tgz", "resolved": "https://registry.npmjs.org/@types/json-patch/-/json-patch-0.0.30.tgz",
@@ -3884,6 +4009,11 @@
} }
} }
}, },
"@types/sizzle": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
},
"@types/source-list-map": { "@types/source-list-map": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@@ -5108,8 +5238,7 @@
"base64-js": { "base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
"dev": true
}, },
"base64id": { "base64id": {
"version": "2.0.0", "version": "2.0.0",
@@ -5315,6 +5444,11 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
} }
} }
}, },
@@ -5638,7 +5772,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2" "get-intrinsic": "^1.0.2"
@@ -5704,6 +5837,14 @@
"integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
"dev": true "dev": true
}, },
"capacitor-secure-storage-plugin": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/capacitor-secure-storage-plugin/-/capacitor-secure-storage-plugin-0.6.2.tgz",
"integrity": "sha512-f05BLb98TG5qqxN8FvTOLPNvtoebdZyrWAueoyw25Xip7nJBkFqrGxZtcFDMONi4DZRz/z9DMz1Bw8F/8fPAJA==",
"requires": {
"@capacitor/core": "^3.0.0"
}
},
"caseless": { "caseless": {
"version": "0.12.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -8770,6 +8911,11 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
} }
} }
}, },
@@ -9298,7 +9444,6 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"dev": true,
"requires": { "requires": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
@@ -9579,6 +9724,11 @@
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
}, },
"guid-typescript": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
"integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ=="
},
"handle-thing": { "handle-thing": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@@ -9661,8 +9811,7 @@
"has-symbols": { "has-symbols": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
"dev": true
}, },
"has-tostringtag": { "has-tostringtag": {
"version": "1.0.0", "version": "1.0.0",
@@ -10307,6 +10456,33 @@
"ipaddr.js": "^1.9.0" "ipaddr.js": "^1.9.0"
} }
}, },
"ionic-appauth": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/ionic-appauth/-/ionic-appauth-0.8.5.tgz",
"integrity": "sha512-4GyFasdqLboGz4mej71UcsSM7XodpFXOcBYdtGvUKJ29p0iIB3/c5BHuGAmpiTqvUfAnGD8insimaQEE3Iensw==",
"requires": {
"@capacitor/browser": "^1.0.2",
"@capacitor/core": "^3.1.2",
"@capacitor/storage": "^1.0.3",
"@ionic-native/core": "^5.34.0",
"@ionic-native/http": "^5.34.0",
"@ionic-native/in-app-browser": "^5.34.0",
"@ionic-native/safari-view-controller": "^5.34.0",
"@ionic-native/secure-storage": "^5.34.0",
"@ionic/storage": "^3.0.6",
"@openid/appauth": "^1.3.1",
"capacitor-secure-storage-plugin": "^0.6.2",
"guid-typescript": "^1.0.9",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"ionicons": { "ionicons": {
"version": "5.5.4", "version": "5.5.4",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.4.tgz", "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.4.tgz",
@@ -11069,6 +11245,11 @@
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
"dev": true "dev": true
}, },
"jsonpath-plus": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz",
"integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw=="
},
"jsonpointer": { "jsonpointer": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz",
@@ -12882,8 +13063,7 @@
"object-inspect": { "object-inspect": {
"version": "1.11.0", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz",
"integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg=="
"dev": true
}, },
"object-is": { "object-is": {
"version": "1.1.5", "version": "1.1.5",
@@ -12997,6 +13177,11 @@
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
"dev": true "dev": true
}, },
"opener": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
},
"opening_hours": { "opening_hours": {
"version": "3.7.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/opening_hours/-/opening_hours-3.7.0.tgz", "resolved": "https://registry.npmjs.org/opening_hours/-/opening_hours-3.7.0.tgz",
@@ -15413,9 +15598,12 @@
"dev": true "dev": true
}, },
"qs": { "qs": {
"version": "6.7.0", "version": "6.10.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==",
"requires": {
"side-channel": "^1.0.4"
}
}, },
"querystring": { "querystring": {
"version": "0.2.0", "version": "0.2.0",
@@ -16147,9 +16335,9 @@
} }
}, },
"rxjs": { "rxjs": {
"version": "6.6.3", "version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"requires": { "requires": {
"tslib": "^1.9.0" "tslib": "^1.9.0"
}, },
@@ -16547,6 +16735,16 @@
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
"integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E="
}, },
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"requires": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
}
},
"signal-exit": { "signal-exit": {
"version": "3.0.6", "version": "3.0.6",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",

View File

@@ -54,6 +54,7 @@
"@angular/router": "12.2.13", "@angular/router": "12.2.13",
"@asymmetrik/ngx-leaflet": "8.1.0", "@asymmetrik/ngx-leaflet": "8.1.0",
"@asymmetrik/ngx-leaflet-markercluster": "5.0.1", "@asymmetrik/ngx-leaflet-markercluster": "5.0.1",
"@capacitor-community/http": "1.4.1",
"@capacitor/app": "1.0.6", "@capacitor/app": "1.0.6",
"@capacitor/browser": "1.0.6", "@capacitor/browser": "1.0.6",
"@capacitor/core": "3.3.1", "@capacitor/core": "3.3.1",
@@ -61,6 +62,7 @@
"@capacitor/keyboard": "1.1.3", "@capacitor/keyboard": "1.1.3",
"@capacitor/splash-screen": "1.1.6", "@capacitor/splash-screen": "1.1.6",
"@capacitor/status-bar": "1.0.6", "@capacitor/status-bar": "1.0.6",
"@capacitor/storage": "1.2.3",
"@ionic-native/core": "5.36.0", "@ionic-native/core": "5.36.0",
"@ionic-native/diagnostic": "5.36.0", "@ionic-native/diagnostic": "5.36.0",
"@ionic-native/dialogs": "5.36.0", "@ionic-native/dialogs": "5.36.0",
@@ -73,6 +75,7 @@
"@openstapps/api": "0.35.0", "@openstapps/api": "0.35.0",
"@openstapps/configuration": "0.28.1", "@openstapps/configuration": "0.28.1",
"@openstapps/core": "0.53.0", "@openstapps/core": "0.53.0",
"capacitor-secure-storage-plugin": "0.6.2",
"cordova-plugin-device": "2.0.3", "cordova-plugin-device": "2.0.3",
"cordova-plugin-dialogs": "2.0.2", "cordova-plugin-dialogs": "2.0.2",
"cordova-plugin-geolocation": "4.1.0", "cordova-plugin-geolocation": "4.1.0",
@@ -83,6 +86,8 @@
"deepmerge": "3.3.0", "deepmerge": "3.3.0",
"form-data": "2.5.0", "form-data": "2.5.0",
"geojson": "0.5.0", "geojson": "0.5.0",
"ionic-appauth": "0.8.5",
"jsonpath-plus": "6.0.1",
"leaflet": "1.7.1", "leaflet": "1.7.1",
"leaflet.markercluster": "1.5.1", "leaflet.markercluster": "1.5.1",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
@@ -91,7 +96,8 @@
"ngx-markdown": "12.0.1", "ngx-markdown": "12.0.1",
"ngx-moment": "5.0.0", "ngx-moment": "5.0.0",
"opening_hours": "3.7.0", "opening_hours": "3.7.0",
"rxjs": "6.6.3", "qs": "6.10.1",
"rxjs": "6.6.7",
"swiper": "7.1.0", "swiper": "7.1.0",
"tslib": "2.0.0", "tslib": "2.0.0",
"zone.js": "0.11.4" "zone.js": "0.11.4"
@@ -110,7 +116,7 @@
"@angular/compiler": "12.2.13", "@angular/compiler": "12.2.13",
"@angular/compiler-cli": "12.2.13", "@angular/compiler-cli": "12.2.13",
"@angular/language-service": "12.2.13", "@angular/language-service": "12.2.13",
"@capacitor/android": "3.3.2", "@capacitor/android": "3.4.0",
"@capacitor/cli": "3.3.2", "@capacitor/cli": "3.3.2",
"@capacitor/ios": "3.3.2", "@capacitor/ios": "3.3.2",
"@compodoc/compodoc": "1.1.14", "@compodoc/compodoc": "1.1.14",
@@ -123,6 +129,7 @@
"@types/leaflet.markercluster": "1.4.5", "@types/leaflet.markercluster": "1.4.5",
"@types/lodash-es": "4.17.4", "@types/lodash-es": "4.17.4",
"@types/node": "14.17.16", "@types/node": "14.17.16",
"@types/qs": "6.9.7",
"@typescript-eslint/eslint-plugin": "4.32.0", "@typescript-eslint/eslint-plugin": "4.32.0",
"@typescript-eslint/parser": "4.32.0", "@typescript-eslint/parser": "4.32.0",
"conventional-changelog-cli": "2.1.1", "conventional-changelog-cli": "2.1.1",

View File

@@ -30478,7 +30478,7 @@ export const sampleResources = [{
'steps': [ 'steps': [
{ {
'type': 'location', 'type': 'location',
'location': '#/b-tu/main' 'location': '/b-tu/main'
}, },
{ {
'type': 'tooltip', 'type': 'tooltip',
@@ -30494,7 +30494,7 @@ export const sampleResources = [{
'text': 'Öffne die Suche.', 'text': 'Öffne die Suche.',
'resolved': { 'resolved': {
'location': { 'location': {
'is': '#/b-tu/search' 'is': '/b-tu/search'
} }
}, },
'position': 'bottom' 'position': 'bottom'
@@ -30529,7 +30529,7 @@ export const sampleResources = [{
'text': 'Klicke auf eine Veranstaltung...', 'text': 'Klicke auf eine Veranstaltung...',
'resolved': { 'resolved': {
'location': { 'location': {
'match': '#/b-tu/data/detail/Event' 'match': '/b-tu/data/detail/Event'
} }
}, },
'position': 'top' 'position': 'top'
@@ -30553,7 +30553,7 @@ export const sampleResources = [{
'text': 'Öffne deinen Stundenplan.', 'text': 'Öffne deinen Stundenplan.',
'resolved': { 'resolved': {
'location': { 'location': {
'is': '#/b-tu/events' 'is': '/b-tu/events'
} }
}, },
'position': 'right' 'position': 'right'
@@ -30585,7 +30585,7 @@ export const sampleResources = [{
'steps': [ 'steps': [
{ {
'type': 'location', 'type': 'location',
'location': '#/b-tu/main' 'location': '/b-tu/main'
}, },
{ {
'type': 'tooltip', 'type': 'tooltip',
@@ -30632,7 +30632,7 @@ export const sampleResources = [{
'text': 'Das ist das Widget, dass dir die Speisepläne deiner favorisierten Essensorte anzeigt. Klicke auf "Essensorte", um zur Übersicht der Essensorte zu gelangen.', 'text': 'Das ist das Widget, dass dir die Speisepläne deiner favorisierten Essensorte anzeigt. Klicke auf "Essensorte", um zur Übersicht der Essensorte zu gelangen.',
'resolved': { 'resolved': {
'location': { 'location': {
'is': '#/b-tu/places?types=FoodEstablishment' 'is': '/b-tu/places?types=FoodEstablishment'
} }
} }
}, },
@@ -30642,7 +30642,7 @@ export const sampleResources = [{
'text': 'Wähle die "Mathe Cafeteria" aus, um sie zu favorisieren.', 'text': 'Wähle die "Mathe Cafeteria" aus, um sie zu favorisieren.',
'resolved': { 'resolved': {
'location': { 'location': {
'is': '#/b-tu/map?place=MA%20Mathe%20Cafeteria' 'is': '/b-tu/map?place=MA%20Mathe%20Cafeteria'
} }
} }
}, },
@@ -30668,7 +30668,7 @@ export const sampleResources = [{
'text': 'Klicke auf das TU-Logo oder "StApps", um zur Startseite zurückzukehren.', 'text': 'Klicke auf das TU-Logo oder "StApps", um zur Startseite zurückzukehren.',
'resolved': { 'resolved': {
'location': { 'location': {
'is': '#/b-tu/main' 'is': '/b-tu/main'
} }
} }
}, },
@@ -30691,7 +30691,7 @@ export const sampleResources = [{
'text': 'Öffne deine Favoriten.', 'text': 'Öffne deine Favoriten.',
'resolved': { 'resolved': {
'location': { 'location': {
'is': '#/b-tu/favorites' 'is': '/b-tu/favorites'
} }
} }
}, },

View File

@@ -13,7 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router'; import {PreloadAllModules, RouterModule, Routes} from '@angular/router';
const routes: Routes = [{path: '', redirectTo: '/news', pathMatch: 'full'}]; const routes: Routes = [{path: '', redirectTo: '/news', pathMatch: 'full'}];
@@ -22,6 +22,11 @@ const routes: Routes = [{path: '', redirectTo: '/news', pathMatch: 'full'}];
*/ */
@NgModule({ @NgModule({
exports: [RouterModule], exports: [RouterModule],
imports: [RouterModule.forRoot(routes, {relativeLinkResolution: 'legacy'})], imports: [
RouterModule.forRoot(routes, {
enableTracing: true,
preloadingStrategy: PreloadAllModules,
}),
],
}) })
export class AppRoutingModule {} export class AppRoutingModule {}

View File

@@ -20,7 +20,9 @@ import {Platform} from '@ionic/angular';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {ThingTranslateService} from './translation/thing-translate.service'; import {ThingTranslateService} from './translation/thing-translate.service';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {AppComponent} from './app.component'; import {AppComponent} from './app.component';
import {AuthModule} from './modules/auth/auth.module';
import {ConfigProvider} from './modules/config/config.provider'; import {ConfigProvider} from './modules/config/config.provider';
import {SettingsProvider} from './modules/settings/settings.provider'; import {SettingsProvider} from './modules/settings/settings.provider';
import {NGXLogger} from 'ngx-logger'; import {NGXLogger} from 'ngx-logger';
@@ -35,10 +37,16 @@ describe('AppComponent', () => {
let configProvider: jasmine.SpyObj<ConfigProvider>; let configProvider: jasmine.SpyObj<ConfigProvider>;
let ngxLogger: jasmine.SpyObj<NGXLogger>; let ngxLogger: jasmine.SpyObj<NGXLogger>;
let platformIsSpy;
beforeEach( beforeEach(
waitForAsync(() => { waitForAsync(() => {
platformReadySpy = Promise.resolve(); platformReadySpy = Promise.resolve();
platformSpy = jasmine.createSpyObj('Platform', {ready: platformReadySpy}); platformIsSpy = Promise.resolve();
platformSpy = jasmine.createSpyObj('Platform', {
ready: platformReadySpy,
is: platformIsSpy,
});
translateServiceSpy = jasmine.createSpyObj('TranslateService', [ translateServiceSpy = jasmine.createSpyObj('TranslateService', [
'setDefaultLang', 'setDefaultLang',
'use', 'use',
@@ -55,7 +63,11 @@ describe('AppComponent', () => {
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']); ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])], imports: [
RouterTestingModule.withRoutes([]),
HttpClientTestingModule,
AuthModule,
],
declarations: [AppComponent], declarations: [AppComponent],
providers: [ providers: [
{provide: Platform, useValue: platformSpy}, {provide: Platform, useValue: platformSpy},

View File

@@ -16,10 +16,14 @@ import {Component, NgZone} from '@angular/core';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {App, URLOpenListenerEvent} from '@capacitor/app'; import {App, URLOpenListenerEvent} from '@capacitor/app';
import {SplashScreen} from '@capacitor/splash-screen'; import {SplashScreen} from '@capacitor/splash-screen';
import {Platform} from '@ionic/angular'; import {Platform, ToastController} from '@ionic/angular';
import {NGXLogger} from 'ngx-logger'; import {NGXLogger} from 'ngx-logger';
import {ConfigProvider} from './modules/config/config.provider'; import {ConfigProvider} from './modules/config/config.provider';
import {SettingsProvider} from './modules/settings/settings.provider'; import {SettingsProvider} from './modules/settings/settings.provider';
import {PAIAAuthService} from './modules/auth/paia/paia-auth.service';
import {DefaultAuthService} from './modules/auth/default-auth.service';
import {environment} from '../environments/environment';
import {AuthHelperService} from './modules/auth/auth-helper.service';
/** /**
* TODO * TODO
@@ -51,6 +55,10 @@ export class AppComponent {
* @param logger An angular logger * @param logger An angular logger
* @param router The angular router * @param router The angular router
* @param zone The angular zone * @param zone The angular zone
* @param defaultAuth Auth Service
* @param paiaAuth Auth Service
* @param authHelperService Helper service for OAuth providers
* @param toastController Toast controller
*/ */
constructor( constructor(
private readonly platform: Platform, private readonly platform: Platform,
@@ -59,6 +67,10 @@ export class AppComponent {
private readonly logger: NGXLogger, private readonly logger: NGXLogger,
private readonly router: Router, private readonly router: Router,
private readonly zone: NgZone, private readonly zone: NgZone,
private readonly defaultAuth: DefaultAuthService,
private readonly paiaAuth: PAIAAuthService,
private readonly authHelperService: AuthHelperService,
private readonly toastController: ToastController,
) { ) {
void this.initializeApp(); void this.initializeApp();
} }
@@ -69,7 +81,7 @@ export class AppComponent {
async initializeApp() { async initializeApp() {
App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => { App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => {
this.zone.run(() => { this.zone.run(() => {
const slug = event.url.split('.de').pop(); const slug = event.url.split(environment.appDomain).pop();
if (slug) { if (slug) {
this.router.navigateByUrl(slug); this.router.navigateByUrl(slug);
} }
@@ -78,6 +90,9 @@ export class AppComponent {
}); });
}); });
this.platform.ready().then(async () => { this.platform.ready().then(async () => {
await this.authInit();
await this.defaultAuth.init();
await this.paiaAuth.init();
await SplashScreen.hide(); await SplashScreen.hide();
// initialise the configProvider // initialise the configProvider
@@ -102,4 +117,28 @@ export class AppComponent {
]); ]);
}); });
} }
private async authInit() {
await this.defaultAuth.init();
await this.paiaAuth.init();
this.defaultAuth.events$.subscribe(action =>
this.showMessage(
this.authHelperService.getAuthMessage('default', action),
),
);
this.paiaAuth.events$.subscribe(action =>
this.showMessage(this.authHelperService.getAuthMessage('paia', action)),
);
}
private async showMessage(message?: string) {
if (typeof message === 'undefined') {
return;
}
const toast = await this.toastController.create({
message: message,
duration: 2000,
});
await toast.present();
}
} }

View File

@@ -18,7 +18,7 @@ import {
PathLocationStrategy, PathLocationStrategy,
registerLocaleData, registerLocaleData,
} from '@angular/common'; } from '@angular/common';
import {HttpClient} from '@angular/common/http'; import {HttpClient, HttpClientModule} from '@angular/common/http';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import {APP_INITIALIZER, NgModule, Provider} from '@angular/core'; import {APP_INITIALIZER, NgModule, Provider} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
@@ -55,10 +55,13 @@ import {initLogger} from './_helpers/ts-logger';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {AboutModule} from './modules/about/about.module'; import {AboutModule} from './modules/about/about.module';
import {FavoritesModule} from './modules/favorites/favorites.module'; import {FavoritesModule} from './modules/favorites/favorites.module';
import {ProfilePageModule} from './modules/profile/profile.module';
import {EndSessionPageModule} from './modules/auth/end-session/end-session.module';
import {FeedbackModule} from './modules/feedback/feedback.module'; import {FeedbackModule} from './modules/feedback/feedback.module';
import {DebugDataCollectorService} from './modules/data/debug-data-collector.service'; import {DebugDataCollectorService} from './modules/data/debug-data-collector.service';
import {Browser} from './util/browser.factory'; import {Browser} from './util/browser.factory';
import {browserFactory} from './util/browser.factory'; import {browserFactory} from './util/browser.factory';
import {AuthModule} from './modules/auth/auth.module';
registerLocaleData(localeDe); registerLocaleData(localeDe);
@@ -143,14 +146,18 @@ const providers: Provider[] = [
imports: [ imports: [
AboutModule, AboutModule,
AppRoutingModule, AppRoutingModule,
AuthModule,
BrowserModule, BrowserModule,
BrowserAnimationsModule, BrowserAnimationsModule,
CatalogModule, CatalogModule,
CommonModule, CommonModule,
ConfigModule, ConfigModule,
DataModule, DataModule,
EndSessionPageModule,
IonicModule.forRoot(), IonicModule.forRoot(),
FavoritesModule, FavoritesModule,
HttpClientModule,
ProfilePageModule,
FeedbackModule, FeedbackModule,
MapModule, MapModule,
MenuModule, MenuModule,

View File

@@ -0,0 +1,3 @@
<div class="centeredMessageContainer">
<p>{{ 'auth.messages.default.authorizing' | translate }}</p>
</div>

View File

@@ -0,0 +1,34 @@
import {Component, OnInit, OnDestroy} from '@angular/core';
import {NavController} from '@ionic/angular';
import {Router} from '@angular/router';
import {IAuthAction} from 'ionic-appauth';
import {Subscription} from 'rxjs';
import {DefaultAuthService} from '../../default-auth.service';
@Component({
selector: 'auth-callback',
templateUrl: './auth-callback-page.component.html',
styleUrls: ['./auth-callback-page.component.scss'],
})
export class AuthCallbackPageComponent implements OnInit, OnDestroy {
sub: Subscription;
constructor(
private auth: DefaultAuthService,
private navCtrl: NavController,
private router: Router,
) {}
ngOnInit() {
this.sub = this.auth.events$.subscribe(action => this.postCallback(action));
this.auth.authorizationCallback(window.location.origin + this.router.url);
}
ngOnDestroy() {
this.sub.unsubscribe();
}
async postCallback(_action: IAuthAction) {
await this.navCtrl.navigateRoot('profile');
}
}

View File

@@ -0,0 +1,42 @@
import {Injectable} from '@angular/core';
import {CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {DefaultAuthService} from './default-auth.service';
import {PAIAAuthService} from './paia/paia-auth.service';
import {IAuthService} from 'ionic-appauth';
import {ActivatedAuthRouteSnapshot} from './auth-routes';
@Injectable({
providedIn: 'root',
})
export class AuthGuardService implements CanActivate {
authService: IAuthService | PAIAAuthService;
constructor(
private defaultAuth: DefaultAuthService,
private paiaAuth: PAIAAuthService,
private router: Router,
) {}
public async canActivate(
route: ActivatedAuthRouteSnapshot,
_state: RouterStateSnapshot,
) {
switch (route.data.authProvider) {
case 'paia':
this.authService = this.paiaAuth;
break;
default:
this.authService = this.defaultAuth;
break;
}
try {
await this.authService.getValidToken();
} catch {
this.router.navigate(['profile']);
return false;
}
return true;
}
}

View File

@@ -0,0 +1,50 @@
import {Injectable} from '@angular/core';
import {
SCAuthorizationProviderType,
SCUserConfiguration,
userMapping,
} from '../profile/user';
import {IPAIAAuthAction} from './paia/paia-auth-action';
import {AuthActions, IAuthAction} from 'ionic-appauth';
import {TranslateService} from '@ngx-translate/core';
import {JSONFile} from '@angular/cli/utilities/json-file';
import {JSONPath} from 'jsonpath-plus';
@Injectable({
providedIn: 'root',
})
export class AuthHelperService {
constructor(private translateService: TranslateService) {}
public getAuthMessage(
provider: SCAuthorizationProviderType,
action: IAuthAction | IPAIAAuthAction,
) {
let message: string | undefined;
switch (action.action) {
case AuthActions.SignInSuccess:
message = this.translateService.instant(
`auth.messages.${provider}.logged_in_success`,
);
break;
case AuthActions.SignOutSuccess:
message = this.translateService.instant(
`auth.messages.${provider}.logged_out_success`,
);
break;
}
return message;
}
getUserFromUserInfo(userInfo: JSONFile) {
const user: SCUserConfiguration = {id: '', name: '', role: 'student'};
for (const key in userMapping) {
user[key as keyof SCUserConfiguration] = JSONPath({
path: userMapping[key as keyof SCUserConfiguration] as string,
json: userInfo,
})[0];
}
return user;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {ActivatedRouteSnapshot, Data, Route} from '@angular/router';
import {SCAuthorizationProviderType} from '../profile/user';
export interface AuthRoute extends Route {
data: {
authProvider: SCAuthorizationProviderType;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
};
}
export class ActivatedAuthRouteSnapshot extends ActivatedRouteSnapshot {
data: Data & {authProvider: AuthRoute['data']['authProvider']};
}
export type AuthRoutes = AuthRoute[];

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {RouterModule, Routes} from '@angular/router';
import {NgModule} from '@angular/core';
import {AuthCallbackPageComponent} from './auth-callback/page/auth-callback-page.component';
import {PAIAAuthCallbackPageComponent} from './paia/auth-callback/page/auth-callback-page.component';
const authRoutes: Routes = [
{path: 'auth/callback', component: AuthCallbackPageComponent},
{path: 'auth/paia/callback', component: PAIAAuthCallbackPageComponent},
];
/**
* Module defining routes for auth module
*/
@NgModule({
exports: [RouterModule],
imports: [RouterModule.forChild(authRoutes)],
})
export class AuthRoutingModule {}

View File

@@ -0,0 +1,47 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {Platform} from '@ionic/angular';
import {Requestor, StorageBackend} from '@openid/appauth';
import {authFactory, paiaAuthFactory, storageFactory} from './factories';
import {DefaultAuthService} from './default-auth.service';
import {Browser} from 'ionic-appauth';
import {CapacitorBrowser} from 'ionic-appauth/lib/capacitor';
import {httpFactory} from './factories/http.factory';
import {HttpClient} from '@angular/common/http';
import {PAIAAuthService} from './paia/paia-auth.service';
import {AuthRoutingModule} from './auth-routing.module';
import {TranslateModule} from '@ngx-translate/core';
import {AuthCallbackPageComponent} from './auth-callback/page/auth-callback-page.component';
import {PAIAAuthCallbackPageComponent} from './paia/auth-callback/page/auth-callback-page.component';
@NgModule({
declarations: [AuthCallbackPageComponent, PAIAAuthCallbackPageComponent],
imports: [CommonModule, AuthRoutingModule, TranslateModule],
providers: [
{
provide: StorageBackend,
useFactory: storageFactory,
deps: [Platform],
},
{
provide: Requestor,
useFactory: httpFactory,
deps: [Platform, HttpClient],
},
{
provide: Browser,
useClass: CapacitorBrowser,
},
{
provide: DefaultAuthService,
useFactory: authFactory,
deps: [Requestor, Browser, StorageBackend],
},
{
provide: PAIAAuthService,
useFactory: paiaAuthFactory,
deps: [Requestor, Browser, StorageBackend],
},
],
})
export class AuthModule {}

View File

@@ -0,0 +1,53 @@
import {Requestor} from '@openid/appauth';
import {Http, HttpHeaders, HttpResponse} from '@capacitor-community/http';
import {XhrSettings} from 'ionic-appauth/lib/cordova';
import qs from 'qs';
// REQUIRES CAPACITOR PLUGIN
// @capacitor-community/http
export class CapacitorRequestor extends Requestor {
constructor() {
super();
}
public async xhr<T>(settings: XhrSettings): Promise<T> {
if (!settings.method) settings.method = 'GET';
switch (settings.method) {
case 'GET':
return this.get(settings.url, settings.headers);
case 'POST':
return this.post(settings.url, settings.data, settings.headers);
case 'PUT':
return this.put(settings.url, settings.data, settings.headers);
case 'DELETE':
return this.delete(settings.url, settings.headers);
}
}
private async get<T>(url: string, headers: HttpHeaders) {
return Http.get({url, headers}).then(
(response: HttpResponse) => response.data as T,
);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async post<T>(url: string, data: any, headers: HttpHeaders) {
return Http.post({url, data: qs.parse(data), headers}).then(
(response: HttpResponse) => response.data as T,
);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async put<T>(url: string, data: any, headers: HttpHeaders) {
return Http.put({url, data, headers}).then(
(response: HttpResponse) => response.data as T,
);
}
private async delete<T>(url: string, headers: HttpHeaders) {
return Http.del({url, headers}).then(
(response: HttpResponse) => response.data as T,
);
}
}

View File

@@ -0,0 +1,53 @@
import {AuthorizationRequestHandler} from '@openid/appauth';
import {
StorageBackend,
Requestor,
AuthorizationServiceConfiguration,
LocalStorageBackend,
JQueryRequestor,
TokenRequestHandler,
} from '@openid/appauth';
import {
UserInfoHandler,
EndSessionHandler,
Browser,
DefaultBrowser,
AuthService,
AuthActionBuilder,
} from 'ionic-appauth';
const TOKEN_RESPONSE_KEY = 'token_response';
export class DefaultAuthService extends AuthService {
public localConfiguration: AuthorizationServiceConfiguration;
protected tokenHandler: TokenRequestHandler;
protected userInfoHandler: UserInfoHandler;
protected requestHandler: AuthorizationRequestHandler;
protected endSessionHandler: EndSessionHandler;
constructor(
protected browser: Browser = new DefaultBrowser(),
protected storage: StorageBackend = new LocalStorageBackend(),
protected requestor: Requestor = new JQueryRequestor(),
) {
super(browser, storage, requestor);
}
get configuration(): Promise<AuthorizationServiceConfiguration> {
if (!this.localConfiguration)
throw new Error('Local Configuration Not Defined');
return Promise.resolve(this.localConfiguration);
}
public async signOut() {
await this.storage.removeItem(TOKEN_RESPONSE_KEY).catch(error => {
this.notifyActionListers(AuthActionBuilder.SignOutFailed(error));
});
this.notifyActionListers(AuthActionBuilder.SignOutSuccess());
}
}

View File

@@ -0,0 +1,26 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {Routes, RouterModule} from '@angular/router';
import {IonicModule} from '@ionic/angular';
import {EndSessionPageComponent} from './page/end-session-page.component';
const routes: Routes = [
{
path: 'logout',
component: EndSessionPageComponent,
},
];
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild(routes),
],
declarations: [EndSessionPageComponent],
})
export class EndSessionPageModule {}

View File

@@ -0,0 +1 @@
<p>Signing out...</p>

View File

@@ -0,0 +1,20 @@
import {Component, OnInit} from '@angular/core';
import {NavController} from '@ionic/angular';
import {DefaultAuthService} from '../../default-auth.service';
@Component({
selector: 'end-session',
templateUrl: './end-session-page.component.html',
styleUrls: ['./end-session-page.component.scss'],
})
export class EndSessionPageComponent implements OnInit {
constructor(
private auth: DefaultAuthService,
private navCtrl: NavController,
) {}
async ngOnInit() {
this.auth.endSessionCallback();
await this.navCtrl.navigateRoot('profile');
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
StorageBackend,
Requestor,
AuthorizationServiceConfiguration,
} from '@openid/appauth';
import {Browser} from 'ionic-appauth';
import {environment} from 'src/environments/environment';
import {DefaultAuthService} from '../default-auth.service';
import {PAIAAuthService} from '../paia/paia-auth.service';
export const authFactory = (
requestor: Requestor,
browser: Browser,
storage: StorageBackend,
) => {
const authService = new DefaultAuthService(browser, storage, requestor);
authService.authConfig = environment.oauth2.client.his;
authService.localConfiguration = new AuthorizationServiceConfiguration(
environment.oauth2.service.his,
);
return authService;
};
export const paiaAuthFactory = (
requestor: Requestor,
browser: Browser,
storage: StorageBackend,
) => {
const authService = new PAIAAuthService(browser, storage, requestor);
authService.authConfig = environment.oauth2.client.paia;
authService.localConfiguration = new AuthorizationServiceConfiguration(
environment.oauth2.service.paia,
);
return authService;
};

View File

@@ -0,0 +1,9 @@
import {Platform} from '@ionic/angular';
import {DefaultBrowser} from 'ionic-appauth';
import {CapacitorBrowser} from 'ionic-appauth/lib/capacitor';
export const browserFactory = (platform: Platform) => {
return platform.is('capacitor')
? new CapacitorBrowser()
: new DefaultBrowser();
};

View File

@@ -0,0 +1,10 @@
import {HttpClient} from '@angular/common/http';
import {Platform} from '@ionic/angular';
import {CapacitorRequestor} from '../capacitor-requestor';
import {NgHttpService} from '../ng-http.service';
export const httpFactory = (platform: Platform, httpClient: HttpClient) => {
return platform.is('capacitor')
? new CapacitorRequestor()
: new NgHttpService(httpClient);
};

View File

@@ -0,0 +1,3 @@
export * from './auth.factory';
export * from './browser.factory';
export * from './storage.factory';

View File

@@ -0,0 +1,9 @@
import {Platform} from '@ionic/angular';
import {CapacitorSecureStorage} from 'ionic-appauth/lib/capacitor';
import {IonicStorage} from 'ionic-appauth/lib';
export const storageFactory = (platform: Platform) => {
return platform.is('capacitor')
? new CapacitorSecureStorage()
: new IonicStorage();
};

View File

@@ -0,0 +1,53 @@
import {Injectable} from '@angular/core';
import {Requestor} from '@openid/appauth';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {XhrSettings} from 'ionic-appauth/lib/cordova';
@Injectable({
providedIn: 'root',
})
export class NgHttpService implements Requestor {
constructor(private http: HttpClient) {}
public async xhr<T>(settings: XhrSettings): Promise<T> {
if (!settings.method) {
settings.method = 'GET';
}
switch (settings.method) {
case 'GET':
return this.http
.get<T>(settings.url, {headers: this.getHeaders(settings.headers)})
.toPromise();
case 'POST':
return this.http
.post<T>(settings.url, settings.data, {
headers: this.getHeaders(settings.headers),
})
.toPromise();
case 'PUT':
return this.http
.put<T>(settings.url, settings.data, {
headers: this.getHeaders(settings.headers),
})
.toPromise();
case 'DELETE':
return this.http
.delete<T>(settings.url, {headers: this.getHeaders(settings.headers)})
.toPromise();
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private getHeaders(headers: any): HttpHeaders {
let httpHeaders: HttpHeaders = new HttpHeaders();
if (headers !== undefined) {
for (const key of Object.keys(headers)) {
httpHeaders = httpHeaders.append(key, headers[key]);
}
}
return httpHeaders;
}
}

View File

@@ -0,0 +1,3 @@
<div class="centeredMessageContainer">
<p>{{ 'auth.messages.paia.authorizing' | translate }}</p>
</div>

View File

@@ -0,0 +1,34 @@
import {Component, OnInit, OnDestroy} from '@angular/core';
import {NavController} from '@ionic/angular';
import {Router} from '@angular/router';
import {IAuthAction} from 'ionic-appauth';
import {Subscription} from 'rxjs';
import {PAIAAuthService} from '../../paia-auth.service';
@Component({
selector: 'auth-callback',
templateUrl: './auth-callback-page.component.html',
styleUrls: ['./auth-callback-page.component.scss'],
})
export class PAIAAuthCallbackPageComponent implements OnInit, OnDestroy {
sub: Subscription;
constructor(
private auth: PAIAAuthService,
private navCtrl: NavController,
private router: Router,
) {}
ngOnInit() {
this.sub = this.auth.events$.subscribe(action => this.postCallback(action));
this.auth.authorizationCallback(window.location.origin + this.router.url);
}
ngOnDestroy() {
this.sub.unsubscribe();
}
async postCallback(_action: IAuthAction) {
this.navCtrl.navigateRoot('profile');
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
StorageBackend,
BasicQueryStringUtils,
DefaultCrypto,
AuthorizationServiceConfiguration,
AuthorizationRequest,
StringMap,
AuthorizationError,
AuthorizationErrorJson,
} from '@openid/appauth';
import {Browser} from 'ionic-appauth';
import {PAIAAuthorizationNotifier} from './paia-authorization-notifier';
import {PAIAAuthorizationRequestResponse} from './authorization-request-response';
import {
PAIAAuthorizationResponse,
PAIAAuthorizationResponseJson,
} from './paia-authorization-response';
/** key for authorization request. */
const authorizationRequestKey = (handle: string) => {
return `${handle}_appauth_authorization_request`;
};
/** key in local storage which represents the current authorization request. */
const AUTHORIZATION_REQUEST_HANDLE_KEY =
'appauth_current_authorization_request';
export const AUTHORIZATION_RESPONSE_KEY = 'auth_response';
// TODO: PAIA specific ...!!! use whatever you can from the parent class !
export class PAIAAuthorizationRequestHandler {
notifier: PAIAAuthorizationNotifier;
constructor(
private browser: Browser,
private storage: StorageBackend,
public utils = new BasicQueryStringUtils(),
protected crypto = new Crypto(),
private generateRandom = new DefaultCrypto(),
) {}
public async performAuthorizationRequest(
configuration: AuthorizationServiceConfiguration,
request: AuthorizationRequest,
): Promise<void> {
const handle = this.generateRandom.generateRandom(10);
await this.storage.setItem(AUTHORIZATION_REQUEST_HANDLE_KEY, handle);
await this.storage.setItem(
authorizationRequestKey(handle),
JSON.stringify(await request.toJson()),
);
const url = this.buildRequestUrl(configuration, request);
const returnedUrl: string | undefined = await this.browser.showWindow(
url,
request.redirectUri,
);
// callback may come from showWindow or via another method
if (typeof returnedUrl !== 'undefined') {
await this.storage.setItem(AUTHORIZATION_RESPONSE_KEY, returnedUrl);
await this.completeAuthorizationRequestIfPossible();
}
}
protected async completeAuthorizationRequest(): Promise<PAIAAuthorizationRequestResponse> {
const handle = await this.storage.getItem(AUTHORIZATION_REQUEST_HANDLE_KEY);
if (!handle) {
throw new Error('Handle Not Available');
}
const request: AuthorizationRequest = this.getAuthorizationRequest(
await this.storage.getItem(authorizationRequestKey(handle)),
);
const queryParameters = this.getQueryParams(
await this.storage.getItem(AUTHORIZATION_RESPONSE_KEY),
);
void this.removeItemsFromStorage(handle);
// const state: string | undefined = queryParams['state'];
const error: string | undefined = queryParameters['error'];
// TODO: we need state from PAIA (we don't get state at the moment)
// if (state !== request.state) {
// throw new Error("State Does Not Match");
// }
return <PAIAAuthorizationRequestResponse>{
request: request, // request
response: !error
? this.getAuthorizationResponse(queryParameters)
: undefined,
error: error ? this.getAuthorizationError(queryParameters) : undefined,
};
}
private getAuthorizationRequest(
authRequest: string | null,
): AuthorizationRequest {
if (authRequest == undefined) {
throw new Error('No Auth Request Available');
}
return new AuthorizationRequest(JSON.parse(authRequest));
}
private getAuthorizationError(
queryParameters: StringMap,
): AuthorizationError {
const authorizationErrorJSON: AuthorizationErrorJson = {
error: queryParameters['error'],
error_description: queryParameters['error_description'],
error_uri: undefined,
state: queryParameters['state'],
};
return new AuthorizationError(authorizationErrorJSON);
}
private getAuthorizationResponse(
queryParameters: StringMap,
): PAIAAuthorizationResponse {
const authorizationResponseJSON: PAIAAuthorizationResponseJson = {
code: queryParameters['code'],
patron: queryParameters['patron'],
// TODO: currently PAIA is not providing state
state: queryParameters['state'] ?? '',
};
return new PAIAAuthorizationResponse(authorizationResponseJSON);
}
private async removeItemsFromStorage(handle: string): Promise<void> {
await this.storage.removeItem(AUTHORIZATION_REQUEST_HANDLE_KEY);
await this.storage.removeItem(authorizationRequestKey(handle));
await this.storage.removeItem(AUTHORIZATION_RESPONSE_KEY);
}
private getQueryParams(authResponse: string | null): StringMap {
if (authResponse != undefined) {
const querySide: string = authResponse.split('#')[0];
const parts: string[] = querySide.split('?');
if (parts.length !== 2) throw new Error('Invalid auth response string');
const hash = parts[1];
return this.utils.parseQueryString(hash);
} else {
return {};
}
}
setAuthorizationNotifier(
notifier: PAIAAuthorizationNotifier,
): PAIAAuthorizationRequestHandler {
this.notifier = notifier;
return this;
}
completeAuthorizationRequestIfPossible(): Promise<void> {
// call complete authorization if possible to see there might
// be a response that needs to be delivered.
console.log(
`Checking to see if there is an authorization response to be delivered.`,
);
if (!this.notifier) {
console.log(`Notifier is not present on AuthorizationRequest handler.
No delivery of result will be possible`);
}
return this.completeAuthorizationRequest().then(result => {
if (!result) {
console.log(`No result is available yet.`);
}
if (result && this.notifier) {
this.notifier.onAuthorizationComplete(
result.request,
result.response,
result.error,
);
}
});
}
/**
* A utility method to be able to build the authorization request URL.
*/
protected buildRequestUrl(
configuration: AuthorizationServiceConfiguration,
request: AuthorizationRequest,
) {
// build the query string
// coerce to any type for convenience
const requestMap: StringMap = {
redirect_uri: request.redirectUri,
client_id: request.clientId,
response_type: request.responseType,
state: request.state,
scope: request.scope,
};
const query = this.utils.stringify(requestMap);
const baseUrl = configuration.authorizationEndpoint;
return `${baseUrl}?${query}&grant_type=client_credentials`;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {AuthorizationError, AuthorizationRequest} from '@openid/appauth';
import {PAIAAuthorizationResponse} from './paia-authorization-response';
/**
* Represents a structural type holding both authorization request and response.
*/
export interface PAIAAuthorizationRequestResponse {
request: AuthorizationRequest;
response: PAIAAuthorizationResponse | null;
error: AuthorizationError | null;
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {PAIATokenResponse} from './paia-token-response';
import {AuthActionBuilder, IAuthAction} from 'ionic-appauth';
export interface IPAIAAuthAction extends IAuthAction {
tokenResponse?: PAIATokenResponse;
}
export class PAIAAuthActionBuilder extends AuthActionBuilder {
public static Init(): IPAIAAuthAction {
return AuthActionBuilder.Init() as IPAIAAuthAction;
}
public static SignOutSuccess(): IPAIAAuthAction {
return AuthActionBuilder.SignOutSuccess() as IPAIAAuthAction;
}
public static SignOutFailed(error: Error): IPAIAAuthAction {
return AuthActionBuilder.SignOutFailed(error) as IPAIAAuthAction;
}
public static RefreshSuccess(
tokenResponse: PAIATokenResponse,
): IPAIAAuthAction {
return AuthActionBuilder.RefreshSuccess(tokenResponse) as IPAIAAuthAction;
}
public static RefreshFailed(error: Error): IPAIAAuthAction {
return AuthActionBuilder.RefreshFailed(error) as IPAIAAuthAction;
}
public static SignInSuccess(
tokenResponse: PAIATokenResponse,
): IPAIAAuthAction {
return AuthActionBuilder.SignInSuccess(tokenResponse) as IPAIAAuthAction;
}
public static SignInFailed(error: Error): IPAIAAuthAction {
return AuthActionBuilder.SignInFailed(error) as IPAIAAuthAction;
}
public static LoadTokenFromStorageSuccess(
tokenResponse: PAIATokenResponse,
): IPAIAAuthAction {
return AuthActionBuilder.LoadTokenFromStorageSuccess(
tokenResponse,
) as IPAIAAuthAction;
}
public static LoadTokenFromStorageFailed(error: Error): IPAIAAuthAction {
return AuthActionBuilder.LoadTokenFromStorageFailed(
error,
) as IPAIAAuthAction;
}
public static RevokeTokensSuccess(): IPAIAAuthAction {
return AuthActionBuilder.RevokeTokensSuccess() as IPAIAAuthAction;
}
public static RevokeTokensFailed(error: Error): IPAIAAuthAction {
return AuthActionBuilder.RevokeTokensFailed(error) as IPAIAAuthAction;
}
public static LoadUserInfoSuccess(user: Error): IPAIAAuthAction {
return AuthActionBuilder.LoadUserInfoSuccess(user) as IPAIAAuthAction;
}
public static LoadUserInfoFailed(error: Error): IPAIAAuthAction {
return AuthActionBuilder.LoadUserInfoFailed(error) as IPAIAAuthAction;
}
}

View File

@@ -0,0 +1,342 @@
import {
AuthorizationError,
AuthorizationRequest,
AuthorizationRequestJson,
AuthorizationServiceConfiguration,
BasicQueryStringUtils,
DefaultCrypto,
JQueryRequestor,
LocalStorageBackend,
Requestor,
StorageBackend,
StringMap,
} from '@openid/appauth';
import {
AuthActions,
AUTHORIZATION_RESPONSE_KEY,
AuthSubject,
Browser,
DefaultBrowser,
EndSessionHandler,
IAuthConfig,
IonicEndSessionHandler,
IonicUserInfoHandler,
UserInfoHandler,
} from 'ionic-appauth';
import {BehaviorSubject, Observable} from 'rxjs';
import {PAIATokenRequestHandler} from './token-request-handler';
import {PAIAAuthorizationRequestHandler} from './authorization-request-handler';
import {PAIATokenRequest, PAIATokenRequestJson} from './paia-token-request';
import {PAIAAuthorizationResponse} from './paia-authorization-response';
import {PAIAAuthorizationNotifier} from './paia-authorization-notifier';
import {PAIATokenResponse} from './paia-token-response';
import {IPAIAAuthAction, PAIAAuthActionBuilder} from './paia-auth-action';
const TOKEN_KEY = 'auth_paia_token';
const AUTH_EXPIRY_BUFFER = 10 * 60 * -1; // 10 mins in seconds
export interface IAuthService {
signIn(authExtras?: StringMap, state?: string): void;
signOut(state?: string, revokeTokens?: boolean): void;
loadUserInfo(): void;
authorizationCallback(callbackUrl: string): void;
loadTokenFromStorage(): void;
getValidToken(buffer?: number): Promise<PAIATokenResponse>;
}
export class PAIAAuthService implements IAuthService {
private _authConfig?: IAuthConfig;
private _authSubject: AuthSubject = new AuthSubject();
private _authSubjectV2 = new BehaviorSubject<IPAIAAuthAction>(
PAIAAuthActionBuilder.Init(),
);
private _tokenSubject = new BehaviorSubject<PAIATokenResponse | undefined>(
undefined,
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _userSubject = new BehaviorSubject<any>(undefined);
private _authenticatedSubject = new BehaviorSubject<boolean>(false);
private _initComplete = new BehaviorSubject<boolean>(false);
protected tokenHandler: PAIATokenRequestHandler;
protected userInfoHandler: UserInfoHandler;
protected requestHandler: PAIAAuthorizationRequestHandler;
protected endSessionHandler: EndSessionHandler;
public localConfiguration: AuthorizationServiceConfiguration;
constructor(
protected browser: Browser = new DefaultBrowser(),
protected storage: StorageBackend = new LocalStorageBackend(),
protected requestor: Requestor = new JQueryRequestor(),
utils = new BasicQueryStringUtils(),
) {
this.tokenHandler = new PAIATokenRequestHandler(requestor);
this.userInfoHandler = new IonicUserInfoHandler(requestor);
this.requestHandler = new PAIAAuthorizationRequestHandler(
browser,
storage,
utils,
crypto,
);
this.endSessionHandler = new IonicEndSessionHandler(browser);
}
get token$(): Observable<PAIATokenResponse | undefined> {
return this._tokenSubject.asObservable();
}
get isAuthenticated$(): Observable<boolean> {
return this._authenticatedSubject.asObservable();
}
get initComplete$(): Observable<boolean> {
return this._initComplete.asObservable();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get user$(): Observable<any> {
return this._userSubject.asObservable();
}
get events$(): Observable<IPAIAAuthAction> {
return this._authSubjectV2.asObservable();
}
get authConfig(): IAuthConfig {
if (!this._authConfig) throw new Error('AuthConfig Not Defined');
return this._authConfig;
}
set authConfig(value: IAuthConfig) {
this._authConfig = value;
}
get configuration(): Promise<AuthorizationServiceConfiguration> {
if (!this.localConfiguration)
throw new Error('Local Configuration Not Defined');
return Promise.resolve(this.localConfiguration);
}
public async init() {
this.setupAuthorizationNotifier();
this.loadTokenFromStorage();
}
protected notifyActionListers(action: IPAIAAuthAction) {
this._authSubjectV2.next(action);
this._authSubject.notify(action);
/* eslint-disable unicorn/no-useless-undefined */
switch (action.action) {
case AuthActions.SignInFailed:
case AuthActions.SignOutSuccess:
case AuthActions.SignOutFailed:
this._tokenSubject.next(undefined);
this._userSubject.next(undefined);
this._authenticatedSubject.next(false);
break;
case AuthActions.LoadTokenFromStorageFailed:
this._tokenSubject.next(undefined);
this._userSubject.next(undefined);
this._authenticatedSubject.next(false);
this._initComplete.next(true);
break;
case AuthActions.SignInSuccess:
this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next(true);
break;
case AuthActions.LoadTokenFromStorageSuccess:
this._tokenSubject.next(action.tokenResponse);
this._authenticatedSubject.next(true);
this._initComplete.next(true);
break;
case AuthActions.RevokeTokensSuccess:
this._tokenSubject.next(undefined);
break;
case AuthActions.LoadUserInfoSuccess:
this._userSubject.next(action.user);
break;
case AuthActions.LoadUserInfoFailed:
this._userSubject.next(undefined);
break;
}
}
protected setupAuthorizationNotifier() {
const notifier = new PAIAAuthorizationNotifier();
this.requestHandler.setAuthorizationNotifier(notifier);
notifier.setAuthorizationListener((request, response, error) =>
this.onAuthorizationNotification(request, response, error),
);
}
protected onAuthorizationNotification(
request: AuthorizationRequest,
response: PAIAAuthorizationResponse | null,
error: AuthorizationError | null,
) {
const codeVerifier: string | undefined =
request.internal != undefined && this.authConfig.pkce
? request.internal.code_verifier
: undefined;
if (response != undefined) {
this.requestAccessToken(response.code, response.patron, codeVerifier);
} else if (error != undefined) {
throw new Error(error.errorDescription);
} else {
throw new Error('Unknown Error With Authentication');
}
}
protected async internalAuthorizationCallback(url: string) {
this.browser.closeWindow();
await this.storage.setItem(AUTHORIZATION_RESPONSE_KEY, url);
return this.requestHandler.completeAuthorizationRequestIfPossible();
}
protected async performAuthorizationRequest(
authExtras?: StringMap,
state?: string,
): Promise<void> {
const requestJson: AuthorizationRequestJson = {
response_type: AuthorizationRequest.RESPONSE_TYPE_CODE,
client_id: this.authConfig.client_id,
redirect_uri: this.authConfig.redirect_url,
scope: this.authConfig.scopes,
extras: authExtras,
state: state || undefined,
};
const request = new AuthorizationRequest(
requestJson,
new DefaultCrypto(),
this.authConfig.pkce,
);
if (this.authConfig.pkce) await request.setupCodeVerifier();
return this.requestHandler.performAuthorizationRequest(
await this.configuration,
request,
);
}
protected async requestAccessToken(
code: string,
patron: string,
codeVerifier?: string,
): Promise<void> {
const requestJSON: PAIATokenRequestJson = {
code: code,
patron: patron,
extras: codeVerifier
? {
code_verifier: codeVerifier,
}
: {},
};
const token: PAIATokenResponse =
await this.tokenHandler.performTokenRequest(
await this.configuration,
new PAIATokenRequest(requestJSON),
);
await this.storage.setItem(TOKEN_KEY, JSON.stringify(token.toJson()));
this.notifyActionListers(PAIAAuthActionBuilder.SignInSuccess(token));
}
public async revokeTokens() {
// Note: only locally
await this.storage.removeItem(TOKEN_KEY);
this.notifyActionListers(PAIAAuthActionBuilder.RevokeTokensSuccess());
}
public async signOut() {
await this.revokeTokens().catch(error =>
this.notifyActionListers(PAIAAuthActionBuilder.SignOutFailed(error)),
);
this.notifyActionListers(PAIAAuthActionBuilder.SignOutSuccess());
}
protected async internalLoadTokenFromStorage() {
let token: PAIATokenResponse | undefined;
const tokenResponseString: string | null = await this.storage.getItem(
TOKEN_KEY,
);
if (tokenResponseString != undefined) {
token = new PAIATokenResponse(JSON.parse(tokenResponseString));
if (token) {
return this.notifyActionListers(
PAIAAuthActionBuilder.LoadTokenFromStorageSuccess(token),
);
}
}
throw new Error('No Token In Storage');
}
protected async internalRequestUserInfo() {
if (this._tokenSubject.value) {
const userInfo = await this.userInfoHandler.performUserInfoRequest(
await this.configuration,
this._tokenSubject.value,
);
this.notifyActionListers(
PAIAAuthActionBuilder.LoadUserInfoSuccess(userInfo),
);
} else {
throw new Error('No Token Available');
}
}
public async loadTokenFromStorage() {
await this.internalLoadTokenFromStorage().catch(error => {
this.notifyActionListers(
PAIAAuthActionBuilder.LoadTokenFromStorageFailed(error),
);
});
}
public async signIn(authExtras?: StringMap, state?: string) {
await this.performAuthorizationRequest(authExtras, state).catch(error => {
this.notifyActionListers(PAIAAuthActionBuilder.SignInFailed(error));
});
}
public async loadUserInfo() {
await this.internalRequestUserInfo().catch(error => {
this.notifyActionListers(PAIAAuthActionBuilder.LoadUserInfoFailed(error));
});
}
public authorizationCallback(callbackUrl: string): void {
this.internalAuthorizationCallback(callbackUrl).catch(error => {
this.notifyActionListers(PAIAAuthActionBuilder.SignInFailed(error));
});
}
public async getValidToken(
buffer: number = AUTH_EXPIRY_BUFFER,
): Promise<PAIATokenResponse> {
if (this._tokenSubject.value && this._tokenSubject.value.isValid(buffer)) {
return this._tokenSubject.value;
}
throw new Error('Unable To Obtain Valid Token');
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {AuthorizationError, AuthorizationRequest} from '@openid/appauth';
import {PAIAAuthorizationResponse} from './paia-authorization-response';
export type PAIAAuthorizationListener = (
request: AuthorizationRequest,
response: PAIAAuthorizationResponse | null,
error: AuthorizationError | null,
) => void;

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {PAIAAuthorizationListener} from './paia-authorization-listener';
import {AuthorizationError, AuthorizationRequest} from '@openid/appauth';
import {PAIAAuthorizationResponse} from './paia-authorization-response';
export class PAIAAuthorizationNotifier {
// eslint-disable-next-line unicorn/no-null
private listener: PAIAAuthorizationListener | null = null;
setAuthorizationListener(listener: PAIAAuthorizationListener) {
this.listener = listener;
}
/**
* The authorization complete callback.
*/
onAuthorizationComplete(
request: AuthorizationRequest,
response: PAIAAuthorizationResponse | null,
error: AuthorizationError | null,
): void {
if (this.listener) {
// complete authorization request
this.listener(request, response, error);
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
export interface PAIAAuthorizationResponseJson {
code: string;
state: string;
patron: string;
}
export class PAIAAuthorizationResponse {
code: string;
state: string;
patron: string;
constructor(response: PAIAAuthorizationResponseJson) {
this.code = response.code;
this.state = response.state;
this.patron = response.patron;
}
toJson(): PAIAAuthorizationResponseJson {
return {code: this.code, state: this.state, patron: this.patron};
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {StringMap} from '@openid/appauth';
// TODO: add documentation
export interface PAIATokenRequestJson {
code: string;
patron: string;
extras?: StringMap;
}
export class PAIATokenRequest {
code: string;
patron: string;
extras?: StringMap;
constructor(request: PAIATokenRequestJson) {
this.code = request.code;
this.patron = request.patron;
this.extras = request.extras;
}
/**
* Serializes a TokenRequest to a JavaScript object.
*/
toJson(): PAIATokenRequestJson {
return {
code: this.code,
patron: this.patron,
extras: this.extras,
};
}
toStringMap(): StringMap {
const map: StringMap = {
patron: this.patron,
code: this.code,
};
// copy over extras
if (this.extras) {
for (const extra in this.extras) {
if (this.extras.hasOwnProperty(extra) && !map.hasOwnProperty(extra)) {
// check before inserting to requestMap
map[extra] = this.extras[extra];
}
}
}
return map;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {TokenResponse, TokenResponseJson} from '@openid/appauth';
import {nowInSeconds} from '@openid/appauth';
const AUTH_EXPIRY_BUFFER = 10 * 60 * -1; // 10 mins in seconds
export interface PAIATokenResponseJson extends TokenResponseJson {
patron: string;
}
export class PAIATokenResponse extends TokenResponse {
patron: string;
constructor(response: PAIATokenResponseJson) {
super(response);
this.patron = response.patron;
}
toJson(): PAIATokenResponseJson {
return {
access_token: this.accessToken,
id_token: this.idToken,
refresh_token: this.refreshToken,
scope: this.scope,
token_type: this.tokenType,
issued_at: this.issuedAt,
expires_in: this.expiresIn?.toString(),
patron: this.patron,
};
}
isValid(buffer: number = AUTH_EXPIRY_BUFFER): boolean {
if (this.expiresIn) {
const now = nowInSeconds();
return now < this.issuedAt + this.expiresIn + buffer;
} else {
return true;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {AuthorizationServiceConfiguration, Requestor} from '@openid/appauth';
import {PAIATokenResponse} from './paia-token-response';
export interface UserInfoHandler {
performUserInfoRequest(
configuration: AuthorizationServiceConfiguration,
token: PAIATokenResponse,
): Promise<unknown>;
}
export class PAIAUserInfoHandler implements UserInfoHandler {
constructor(private requestor: Requestor) {}
public async performUserInfoRequest(
configuration: AuthorizationServiceConfiguration,
token: PAIATokenResponse,
): Promise<unknown> {
const settings: JQueryAjaxSettings = {
url: `${configuration.userInfoEndpoint}/${token.patron}`,
method: 'GET',
headers: {
Authorization: `${
token.tokenType == 'bearer' ? 'Bearer' : token.tokenType
} ${token.accessToken}`,
},
};
return this.requestor.xhr(settings);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2021 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
TokenErrorJson,
AuthorizationServiceConfiguration,
RevokeTokenRequest,
Requestor,
JQueryRequestor,
BasicQueryStringUtils,
QueryStringUtils,
AppAuthError,
TokenError,
} from '@openid/appauth';
import {PAIATokenRequest} from './paia-token-request';
import {PAIATokenResponse, PAIATokenResponseJson} from './paia-token-response';
export class PAIATokenRequestHandler {
constructor(
public readonly requestor: Requestor = new JQueryRequestor(),
public readonly utils: QueryStringUtils = new BasicQueryStringUtils(),
) {}
private isTokenResponse(
response: PAIATokenResponseJson | TokenErrorJson,
): response is PAIATokenResponseJson {
return (response as TokenErrorJson).error === undefined;
}
performRevokeTokenRequest(
configuration: AuthorizationServiceConfiguration,
request: RevokeTokenRequest,
): Promise<boolean> {
const revokeTokenResponse = this.requestor.xhr<boolean>({
url: configuration.revocationEndpoint,
method: 'GET',
// headers: {'Content-Type': 'application/x-www-form-urlencoded'},
data: this.utils.stringify(request.toStringMap()),
});
return revokeTokenResponse.then(_response => {
return true;
});
}
performTokenRequest(
configuration: AuthorizationServiceConfiguration,
request: PAIATokenRequest,
): Promise<PAIATokenResponse> {
const tokenResponse = this.requestor.xhr<
PAIATokenResponseJson | TokenErrorJson
>({
url: configuration.tokenEndpoint,
method: 'POST',
data: {
patron: request.patron,
grant_type: 'client_credentials',
},
headers: {
'Authorization': `Basic ${request.code}`,
'Content-Type': 'application/json',
},
});
return tokenResponse.then(response => {
return this.isTokenResponse(response)
? new PAIATokenResponse(response)
: Promise.reject<PAIATokenResponse>(
new AppAuthError(response.error, new TokenError(response)),
);
});
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
export interface IUserInfo {
display_name: string;
role: string;
email: string;
user_name: string;
}

View File

@@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Component} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core'; import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import { import {
SCAppConfigurationMenuCategory, SCAppConfigurationMenuCategory,
@@ -20,8 +20,7 @@ import {
SCThingTranslator, SCThingTranslator,
SCTranslations, SCTranslations,
} from '@openstapps/core'; } from '@openstapps/core';
import {NGXLogger} from 'ngx-logger'; import {NavigationService} from './navigation.service';
import {ConfigProvider} from '../../config/config.provider';
/** /**
* Generated class for the MenuPage page. * Generated class for the MenuPage page.
@@ -34,7 +33,7 @@ import {ConfigProvider} from '../../config/config.provider';
styleUrls: ['navigation.scss'], styleUrls: ['navigation.scss'],
templateUrl: 'navigation.html', templateUrl: 'navigation.html',
}) })
export class NavigationComponent { export class NavigationComponent implements OnInit {
/** /**
* Possible languages to be used for translation * Possible languages to be used for translation
*/ */
@@ -60,11 +59,9 @@ export class NavigationComponent {
translator: SCThingTranslator; translator: SCThingTranslator;
constructor( constructor(
private readonly configProvider: ConfigProvider,
public translateService: TranslateService, public translateService: TranslateService,
private readonly logger: NGXLogger, private navigationService: NavigationService,
) { ) {
void this.loadMenuEntries();
translateService.onLangChange.subscribe((event: LangChangeEvent) => { translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.language = event.lang as keyof SCTranslations<SCLanguage>; this.language = event.lang as keyof SCTranslations<SCLanguage>;
this.translator = new SCThingTranslator(this.language); this.translator = new SCThingTranslator(this.language);
@@ -72,20 +69,7 @@ export class NavigationComponent {
this.translator = new SCThingTranslator('en'); this.translator = new SCThingTranslator('en');
} }
/** async ngOnInit() {
* Loads menu entries from configProvider this.menu = await this.navigationService.getMenu();
*/
async loadMenuEntries() {
try {
this.menu = (await this.configProvider.getValue(
'menus',
)) as SCAppConfigurationMenuCategory[];
} catch (error) {
this.logger.error(`error from loading menu entries: ${error}`);
}
} }
// openPage(page) {
// this.nav.setRoot(page.component);
// }
} }

View File

@@ -0,0 +1,40 @@
import {Injectable} from '@angular/core';
import {SCAppConfigurationMenuCategory} from '@openstapps/core';
import {ConfigProvider} from '../../config/config.provider';
import {NGXLogger} from 'ngx-logger';
@Injectable({
providedIn: 'root',
})
export class NavigationService {
constructor(
private configProvider: ConfigProvider,
private logger: NGXLogger,
) {}
async getMenu() {
let menu: SCAppConfigurationMenuCategory[] = [];
try {
menu = (await this.configProvider.getValue(
'menus',
)) as SCAppConfigurationMenuCategory[];
} catch (error) {
this.logger.error(`error from loading menu entries: ${error}`);
}
// TODO: Load if from the backend (config)
menu[1].items.unshift({
icon: 'person',
route: '/profile',
title: 'profile',
translations: {
de: {
title: 'Profil',
},
en: {
title: 'profile',
},
},
});
return menu;
}
}

View File

@@ -0,0 +1,75 @@
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>{{ 'profile.title' | translate | titlecase }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<section>
<ion-grid>
<ion-row>
<ion-col>
<ion-card *ngIf="userInfo">
<ion-card-content>
<ion-grid>
<ion-row>
<ion-col size="3">
<ion-thumbnail>
<ion-icon name="person"></ion-icon>
</ion-thumbnail>
</ion-col>
<ion-col size="9">
<ion-row class="main-info">
<span
>{{ userInfo.givenName }}
{{ userInfo.familyName }}</span
>
</ion-row>
<ion-row class="additional-info">
<span>{{ userInfo.email }}</span>
<span
>{{
'profile.userInfo.studentId' | translate | titlecase
}}: {{ userInfo.studentId }}</span
>
</ion-row>
</ion-col>
</ion-row>
</ion-grid>
</ion-card-content>
</ion-card>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="login">
<a *ngIf="!data.default.loggedIn; else loggedIn" (click)="signIn()">{{
'profile.buttons.default.log_in' | translate | titlecase
}}</a>
<ng-template #loggedIn
><a (click)="signOut()">{{
'profile.buttons.default.log_out' | translate | titlecase
}}</a></ng-template
>
</ion-col>
</ion-row>
<ion-row>
<ion-col class="login">
<a
*ngIf="!data.paia.loggedIn; else paiaLoggedIn"
(click)="signInPAIA()"
>{{ 'profile.buttons.paia.log_in' | translate | titlecase }}</a
>
<ng-template #paiaLoggedIn
><a (click)="signOutPAIA()">{{
'profile.buttons.paia.log_out' | translate | titlecase
}}</a></ng-template
>
</ion-col>
</ion-row>
</ion-grid>
</section>
</ion-content>

View File

@@ -0,0 +1,25 @@
:host {
ion-col.login {
text-align: center;
a {
cursor: pointer;
}
}
ion-thumbnail {
background: var(--placeholder-gray);
--size: 64px;
align-items: center;
padding: 10px;
margin: 0;
ion-icon {
width: 100%;
height: 100%;
color: white;
display: block;
}
}
ion-row.main-info {
font-weight: bold;
margin-bottom: 2px;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {RouterTestingModule} from '@angular/router/testing';
import {AuthModule} from '../../auth/auth.module';
import {ProfilePageComponent} from './profile-page.component';
import {TranslateModule} from '@ngx-translate/core';
describe('ProfilePage', () => {
let component: ProfilePageComponent;
let fixture: ComponentFixture<ProfilePageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ProfilePageComponent],
imports: [
HttpClientTestingModule,
RouterTestingModule,
AuthModule,
TranslateModule.forRoot(),
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProfilePageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2021-2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, OnDestroy, OnInit} from '@angular/core';
import {IonicUserInfoHandler} from 'ionic-appauth';
import {DefaultAuthService} from '../../auth/default-auth.service';
import {Requestor, TokenResponse} from '@openid/appauth';
import {PAIAAuthService} from '../../auth/paia/paia-auth.service';
import {SCAuthorizationProviderType, SCUserConfiguration} from '../user';
import {Subscription} from 'rxjs';
import {AuthHelperService} from '../../auth/auth-helper.service';
@Component({
selector: 'app-home',
templateUrl: './profile-page.component.html',
styleUrls: ['./profile-page.component.scss'],
})
export class ProfilePageComponent implements OnInit, OnDestroy {
data: {[key in SCAuthorizationProviderType]: {loggedIn: boolean}} = {
default: {loggedIn: false},
paia: {loggedIn: false},
};
userInfo?: SCUserConfiguration;
subscriptions: Subscription[] = [];
constructor(
private defaultAuth: DefaultAuthService,
private paiaAuth: PAIAAuthService,
private requestor: Requestor,
private authHelperService: AuthHelperService,
) {}
ngOnInit() {
this.subscriptions.push(
this.defaultAuth.token$.subscribe(_token => {
this.defaultAuth
.getValidToken()
.then(token => {
this.data.default.loggedIn = true;
this.getUserInfo(token);
})
.catch(_error => {
this.data.default.loggedIn = false;
});
}),
this.paiaAuth.token$.subscribe(_token => {
this.paiaAuth
.getValidToken()
.then(_token => {
this.data.paia.loggedIn = true;
})
.catch(_error => {
this.data.paia.loggedIn = false;
});
}),
);
}
getUserInfo(token: TokenResponse) {
const userInfoHandler = new IonicUserInfoHandler(this.requestor);
userInfoHandler
.performUserInfoRequest(this.defaultAuth.localConfiguration, token)
.then(userInfo => {
this.userInfo = this.authHelperService.getUserFromUserInfo(userInfo);
});
}
public signIn() {
this.defaultAuth.signIn();
}
signInPAIA() {
this.paiaAuth.signIn();
}
async signOut() {
await this.defaultAuth.signOut();
this.userInfo = undefined;
}
async signOutPAIA() {
await this.paiaAuth.signOut();
}
ngOnDestroy(): void {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
}

View File

@@ -0,0 +1,26 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {Routes, RouterModule} from '@angular/router';
import {IonicModule} from '@ionic/angular';
import {ProfilePageComponent} from './page/profile-page.component';
import {TranslateModule} from '@ngx-translate/core';
const routes: Routes = [
{
path: 'profile',
component: ProfilePageComponent,
},
];
@NgModule({
declarations: [ProfilePageComponent],
imports: [
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild(routes),
TranslateModule,
],
})
export class ProfilePageModule {}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* TODO: Take it from the StApps Core
*/
import {SCAcademicPriceGroup} from '@openstapps/core';
/**
* A user configuration
*/
export interface SCUserConfiguration {
/**
* User's e-mail
*/
email?: string;
/**
* User's family name
*/
familyName?: string;
/**
* User's given name
*/
givenName?: string;
/**
* ID given to the user
*/
id: string;
/**
* The complete name of the user combining all the parts of the name into one
*/
name: string;
/**
* Role assigned to the user
*/
role: keyof SCAcademicPriceGroup;
/**
* Student ID given to the user
*/
studentId?: string;
}
/**
* TODO: Take it from the backend's config
*/
type mapping = {[key in keyof SCUserConfiguration]: string};
/**
* TODO: Take it from the backend's config
*/
export const userMapping: mapping = {
id: 'id',
name: 'sn',
role: 'attributes.eduPersonPrimaryAffiliation',
email: 'attributes.mailPrimaryAddress',
studentId: 'attributes.employeeNumber',
givenName: 'attributes.givenName',
familyName: 'attributes.sn',
};
/**
* TODO: Take it from the StApps Core
*/
export type SCAuthorizationProviderType = 'default' | 'paia';

View File

@@ -13,6 +13,20 @@
"UNKNOWN": "Unbekannter Fehler." "UNKNOWN": "Unbekannter Fehler."
} }
}, },
"auth": {
"messages": {
"default": {
"authorizing": "Autorisierung läuft...",
"logged_in_success": "Erfolgreich eingeloggt.",
"logged_out_success": "Erfolgreich ausgeloggt."
},
"paia": {
"authorizing": "Autorisierung (Bibliothek) läuft...",
"logged_in_success": "Erfolgreich ins Bibliothekskonto eingeloggt.",
"logged_out_success": "Erfolgreich aus dem Bibliothekskonto ausgeloggt."
}
}
},
"common": { "common": {
"openingHours": { "openingHours": {
"closed_until": "Geschlossen bis", "closed_until": "Geschlossen bis",
@@ -202,6 +216,21 @@
"pastEvent": "Event ist vorbei" "pastEvent": "Event ist vorbei"
} }
}, },
"profile": {
"buttons": {
"default": {
"log_in": "Login",
"log_out": "Ausloggen"
},
"paia": {
"log_in": "Login Bibliothek",
"log_out": "Ausloggen (Bibliothek)"
}
},
"userInfo": {
"studentId": "Matrikelnr."
}
},
"settings": { "settings": {
"resetAlert.title": "Alle Einstellungen zurücksetzen?", "resetAlert.title": "Alle Einstellungen zurücksetzen?",
"resetAlert.message": "Sind Sie sich sicher, alle Einstellungen auf ihre Anfangswerte zurückzusetzen?", "resetAlert.message": "Sind Sie sich sicher, alle Einstellungen auf ihre Anfangswerte zurückzusetzen?",

View File

@@ -13,6 +13,20 @@
"UNKNOWN": "Unknown problem." "UNKNOWN": "Unknown problem."
} }
}, },
"auth": {
"messages": {
"default": {
"authorizing": "Authorizing...",
"logged_in_success": "Successfully logged in.",
"logged_out_success": "Successfully logged out."
},
"paia": {
"authorizing": "Authorizing (library)...",
"logged_in_success": "Successfully logged in to library.",
"logged_out_success": "Successfully logged out from library."
}
}
},
"common": { "common": {
"openingHours": { "openingHours": {
"closed_until": "Closed until", "closed_until": "Closed until",
@@ -202,6 +216,22 @@
"pastEvent": "Event is over" "pastEvent": "Event is over"
} }
}, },
"profile": {
"title": "Profile",
"buttons": {
"default": {
"log_in": "Log In",
"log_out": "Log Out"
},
"paia": {
"log_in": "Library Login",
"log_out": "Log Out (Library)"
}
},
"userInfo": {
"studentId": "Matriculation Nr."
}
},
"settings": { "settings": {
"resetAlert.title": "Reset all settings?", "resetAlert.title": "Reset all settings?",
"resetAlert.message": "Are you sure to reset all settings to their default values?", "resetAlert.message": "Are you sure to reset all settings to their default values?",

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018-2020 StApps * Copyright (C) 2018-2022 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -12,9 +12,74 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
import {AuthorizationServiceConfigurationJson} from '@openid/appauth';
import {IAuthConfig} from 'ionic-appauth';
// import config from 'capacitor.config';
const appDomain = 'mobile.app.uni-frankfurt.de';
export const environment = { export const environment = {
backend_url: 'https://mobile.server.uni-frankfurt.de', backend_url: 'https://mobile.server.uni-frankfurt.de',
appDomain: 'mobile.app.uni-frankfurt.de',
backend_version: '2.0.0', backend_version: '2.0.0',
use_fake_backend: true, use_fake_backend: true,
production: false, production: false,
oauth2: {
client: {
his: {
client_id: '1cac3f99-33fa-4234-8438-979f07e0cdab',
client_secret: 'CLIENT_SECRET',
server_host: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0',
redirect_url: `https://${appDomain}/auth/callback`,
scopes: '',
pkce: true,
} as IAuthConfig,
paia: {
client_id: '',
client_secret: '',
server_host:
'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php',
// TODO: Use Custom URL Scheme (ideally bundle ID from capacitor.config)
redirect_url: `https://${appDomain}/auth/paia/callback`,
scopes: '',
pkce: true,
} as IAuthConfig,
},
service: {
his: {
authorization_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/authorize',
token_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/accessToken',
userinfo_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/profile',
} as AuthorizationServiceConfigurationJson,
paia: {
authorization_endpoint:
'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php',
token_endpoint: 'https://hds.hebis.de:8443/auth/login',
userinfo_endpoint: 'https://hds.hebis.de:8443/core',
} as AuthorizationServiceConfigurationJson,
},
endpointMappings: {
userinfo: {
id: 'employeeNumber',
given_name: 'givenName',
family_name: 'sn',
email: 'mailPrimaryAddress',
},
},
},
}; };
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018-2020 StApps * Copyright (C) 2018-2022 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -12,10 +12,74 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
// eslint-disable-next-line unicorn/prevent-abbreviations // The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
import {AuthorizationServiceConfigurationJson} from '@openid/appauth';
import {IAuthConfig} from 'ionic-appauth';
// import config from 'capacitor.config';
const appDomain = 'mobile.app.uni-frankfurt.de';
export const environment = { export const environment = {
backend_url: 'https://mobile.server.uni-frankfurt.de', backend_url: 'https://mobile.server.uni-frankfurt.de',
appDomain: 'mobile.app.uni-frankfurt.de',
backend_version: '2.0.0', backend_version: '2.0.0',
use_fake_backend: false, use_fake_backend: false,
production: true, production: false,
oauth2: {
client: {
his: {
client_id: '1cac3f99-33fa-4234-8438-979f07e0cdab',
client_secret: 'CLIENT_SECRET',
server_host: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0',
redirect_url: `https://${appDomain}/auth/callback`,
scopes: '',
pkce: true,
} as IAuthConfig,
paia: {
client_id: '',
client_secret: '',
server_host:
'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php',
// TODO: Use Custom URL Scheme (ideally bundle ID from capacitor.config)
redirect_url: `https://${appDomain}/auth/paia/callback`,
scopes: '',
pkce: true,
} as IAuthConfig,
},
service: {
his: {
authorization_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/authorize',
token_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/accessToken',
userinfo_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/profile',
} as AuthorizationServiceConfigurationJson,
paia: {
authorization_endpoint:
'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php',
token_endpoint: 'https://hds.hebis.de:8443/auth/login',
userinfo_endpoint: 'https://hds.hebis.de:8443/core',
} as AuthorizationServiceConfigurationJson,
},
endpointMappings: {
userinfo: {
id: 'employeeNumber',
given_name: 'givenName',
family_name: 'sn',
email: 'mailPrimaryAddress',
},
},
},
}; };
/*
* In development mode, to ignore zone related error stack frames such as
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
* import the following file, but please comment it out in production mode
* because it will have performance impact when throw error
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018-2020 StApps * Copyright (C) 2018-2022 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -16,11 +16,65 @@
// The build system defaults to the dev environment which uses `environment.ts`, but if you do // The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead. // `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`. // The list of which env maps to which file can be found in `.angular-cli.json`.
import {AuthorizationServiceConfigurationJson} from '@openid/appauth';
import {IAuthConfig} from 'ionic-appauth';
// import config from 'capacitor.config';
const appDomain = 'mobile.app.uni-frankfurt.de';
export const environment = { export const environment = {
backend_url: 'https://mobile.server.uni-frankfurt.de', backend_url: 'https://mobile.server.uni-frankfurt.de',
appDomain: 'mobile.app.uni-frankfurt.de',
backend_version: '2.0.0', backend_version: '2.0.0',
use_fake_backend: false, use_fake_backend: false,
production: false, production: false,
oauth2: {
client: {
his: {
client_id: '1cac3f99-33fa-4234-8438-979f07e0cdab',
client_secret: '',
server_host: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0',
redirect_url: `https://${appDomain}/auth/callback`,
scopes: '',
pkce: true,
} as IAuthConfig,
paia: {
client_id: '',
client_secret: '',
server_host:
'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php',
// TODO: Use Custom URL Scheme (ideally bundle ID from capacitor.config)
redirect_url: `https://${appDomain}/auth/paia/callback`,
scopes: '',
// TODO: PAIA need to support PKCE, it will then work "out-of-the-box"
pkce: true,
} as IAuthConfig,
},
service: {
his: {
authorization_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/authorize',
token_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/accessToken',
userinfo_endpoint:
'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/profile',
} as AuthorizationServiceConfigurationJson,
paia: {
authorization_endpoint:
'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php',
token_endpoint: 'https://hds.hebis.de:8443/auth/login',
userinfo_endpoint: 'https://hds.hebis.de:8443/core',
} as AuthorizationServiceConfigurationJson,
},
endpointMappings: {
userinfo: {
id: 'employeeNumber',
given_name: 'givenName',
family_name: 'sn',
email: 'mailPrimaryAddress',
},
},
},
}; };
/* /*