diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..83c0d21 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "url": "http://localhost:8100", + "webRoot": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 4022ff6..fdac902 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -25,6 +25,7 @@ android { repositories { flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' } } diff --git a/angular.json b/angular.json index 459f742..dc2e342 100644 --- a/angular.json +++ b/angular.json @@ -73,8 +73,7 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "app:build", - "proxyConfig": "src/proxy.conf.json" + "browserTarget": "app:build" }, "configurations": { "production": { diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index e3d0e15..7893113 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -123,7 +123,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0920; + LastUpgradeCheck = 1420; TargetAttributes = { 504EC3031FED79650016851F = { CreatedOnToolsVersion = 9.2; @@ -247,6 +247,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -254,8 +255,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -304,6 +307,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -311,8 +315,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png index 2f50374..5493abb 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png index dd72c1c..d3f1076 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png index dd72c1c..d3f1076 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png index 7fbf0a8..5c498f0 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png index f996ea1..a717b93 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png index bb2935f..89a11f3 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png index bb2935f..89a11f3 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png index 21d16e5..a4c3e3e 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png index dd72c1c..d3f1076 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png index ff07545..e038ec3 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png index ff07545..e038ec3 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png index 3401fa8..4096d34 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png index adf6ba0..3ad93e2 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png index ffd0da7..4096d34 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png index 90aea7c..551e69d 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png index 2f5eafb..16fc528 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png index 89c8d04..c64d330 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png differ diff --git a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png index ef541c9..45c9bcb 100644 Binary files a/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png and b/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png differ diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png index 33ea6c9..d1e4437 100644 Binary files a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png and b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png differ diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png index 33ea6c9..d1e4437 100644 Binary files a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png and b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png differ diff --git a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png index 33ea6c9..d1e4437 100644 Binary files a/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png and b/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png differ diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist index 99de810..e993906 100644 --- a/ios/App/App/Info.plist +++ b/ios/App/App/Info.plist @@ -1,49 +1,60 @@ - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - d4sworkspace - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + d4sworkspace + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleURLName + com.getcapacitor.capacitor + CFBundleURLSchemes + + d4sworkspace + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + diff --git a/package-lock.json b/package-lock.json index 992795d..24649d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@angular/router": "^15.0.0", "@awesome-cordova-plugins/file-opener": "^6.3.0", "@capacitor/android": "4.6.3", - "@capacitor/app": "4.1.1", + "@capacitor/app": "^4.1.1", "@capacitor/core": "4.6.3", "@capacitor/haptics": "4.1.0", "@capacitor/ios": "4.6.3", @@ -32,7 +32,10 @@ "@ionic/angular": "^6.5.4", "angular-oauth2-oidc": "^15.0.1", "cordova-plugin-file-opener2": "^4.0.0", + "cordova-plugin-inappbrowser": "^5.0.0", "ionicons": "^6.1.3", + "keycloak-angular": "^13.0.0", + "keycloak-js": "^20.0.5", "material-icons": "^1.13.1", "node-angular-http-client": "^1.1.7", "rxjs": "~7.5.0", @@ -5814,7 +5817,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -6610,6 +6612,30 @@ } } }, + "node_modules/cordova-plugin-inappbrowser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cordova-plugin-inappbrowser/-/cordova-plugin-inappbrowser-5.0.0.tgz", + "integrity": "sha512-MqnpmUQ/iy6hxtIGDdlIhy8aNi1pNanLATpbnkk7uCqW9YQ4rH/dGK9zESbZ50pUi2A2D2QMjBXNV175TJK5OQ==", + "engines": { + "cordovaDependencies": { + "0.2.3": { + "cordova": ">=3.1.0" + }, + "4.0.0": { + "cordova": ">=3.1.0", + "cordova-ios": ">=4.0.0" + }, + "5.0.0": { + "cordova": ">=9.0.0", + "cordova-android": ">=9.0.0", + "cordova-ios": ">=6.0.0" + }, + "6.0.0": { + "cordova": ">100" + } + } + } + }, "node_modules/core-js-compat": { "version": "3.28.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.28.0.tgz", @@ -10009,6 +10035,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -10411,6 +10442,29 @@ "node": ">=10" } }, + "node_modules/keycloak-angular": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/keycloak-angular/-/keycloak-angular-13.0.0.tgz", + "integrity": "sha512-8IDN97GhH4ZVrpTtuqBHQfn7O4D7ZYQNqR/datpYiXViHGhgY0zbe1mGRi5lhQFH8dmwNurj8EjXKgwrX+eV8g==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^15", + "@angular/core": "^15", + "@angular/router": "^15", + "keycloak-js": "^18 || ^19 || ^20" + } + }, + "node_modules/keycloak-js": { + "version": "20.0.5", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-20.0.5.tgz", + "integrity": "sha512-7+M5Uni4oNlAmbjM/lDJzFHu2+PGqU6/bvmTBuQssE1fJ7ZyNeCRHgFoaVfFpIU3m6aAFwPUko4lVcn4kPXP5Q==", + "dependencies": { + "base64-js": "^1.5.1", + "js-sha256": "^0.9.0" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", diff --git a/package.json b/package.json index c3524f9..e60beb4 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@angular/router": "^15.0.0", "@awesome-cordova-plugins/file-opener": "^6.3.0", "@capacitor/android": "4.6.3", - "@capacitor/app": "4.1.1", + "@capacitor/app": "^4.1.1", "@capacitor/core": "4.6.3", "@capacitor/haptics": "4.1.0", "@capacitor/ios": "4.6.3", @@ -37,7 +37,10 @@ "@ionic/angular": "^6.5.4", "angular-oauth2-oidc": "^15.0.1", "cordova-plugin-file-opener2": "^4.0.0", + "cordova-plugin-inappbrowser": "^5.0.0", "ionicons": "^6.1.3", + "keycloak-angular": "^13.0.0", + "keycloak-js": "^20.0.5", "material-icons": "^1.13.1", "node-angular-http-client": "^1.1.7", "rxjs": "~7.5.0", diff --git a/resources/android/icon/drawable-hdpi-icon.png b/resources/android/icon/drawable-hdpi-icon.png new file mode 100644 index 0000000..eef7c36 Binary files /dev/null and b/resources/android/icon/drawable-hdpi-icon.png differ diff --git a/resources/android/icon/drawable-ldpi-icon.png b/resources/android/icon/drawable-ldpi-icon.png new file mode 100644 index 0000000..f854419 Binary files /dev/null and b/resources/android/icon/drawable-ldpi-icon.png differ diff --git a/resources/android/icon/drawable-mdpi-icon.png b/resources/android/icon/drawable-mdpi-icon.png new file mode 100644 index 0000000..38f70d8 Binary files /dev/null and b/resources/android/icon/drawable-mdpi-icon.png differ diff --git a/resources/android/icon/drawable-xhdpi-icon.png b/resources/android/icon/drawable-xhdpi-icon.png new file mode 100644 index 0000000..c05ffe5 Binary files /dev/null and b/resources/android/icon/drawable-xhdpi-icon.png differ diff --git a/resources/android/icon/drawable-xxhdpi-icon.png b/resources/android/icon/drawable-xxhdpi-icon.png new file mode 100644 index 0000000..d051b1c Binary files /dev/null and b/resources/android/icon/drawable-xxhdpi-icon.png differ diff --git a/resources/android/icon/drawable-xxxhdpi-icon.png b/resources/android/icon/drawable-xxxhdpi-icon.png new file mode 100644 index 0000000..e641bfc Binary files /dev/null and b/resources/android/icon/drawable-xxxhdpi-icon.png differ diff --git a/resources/android/icon/hdpi-foreground.png b/resources/android/icon/hdpi-foreground.png new file mode 100644 index 0000000..eef7c36 Binary files /dev/null and b/resources/android/icon/hdpi-foreground.png differ diff --git a/resources/android/icon/mdpi-foreground.png b/resources/android/icon/mdpi-foreground.png new file mode 100644 index 0000000..38f70d8 Binary files /dev/null and b/resources/android/icon/mdpi-foreground.png differ diff --git a/resources/android/icon/xhdpi-foreground.png b/resources/android/icon/xhdpi-foreground.png new file mode 100644 index 0000000..c05ffe5 Binary files /dev/null and b/resources/android/icon/xhdpi-foreground.png differ diff --git a/resources/android/icon/xxhdpi-foreground.png b/resources/android/icon/xxhdpi-foreground.png new file mode 100644 index 0000000..d051b1c Binary files /dev/null and b/resources/android/icon/xxhdpi-foreground.png differ diff --git a/resources/android/icon/xxxhdpi-foreground.png b/resources/android/icon/xxxhdpi-foreground.png new file mode 100644 index 0000000..e641bfc Binary files /dev/null and b/resources/android/icon/xxxhdpi-foreground.png differ diff --git a/resources/android/splash/drawable-land-hdpi-screen.png b/resources/android/splash/drawable-land-hdpi-screen.png new file mode 100644 index 0000000..1563e5d Binary files /dev/null and b/resources/android/splash/drawable-land-hdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-ldpi-screen.png b/resources/android/splash/drawable-land-ldpi-screen.png new file mode 100644 index 0000000..de9b917 Binary files /dev/null and b/resources/android/splash/drawable-land-ldpi-screen.png differ diff --git a/resources/android/splash/drawable-land-mdpi-screen.png b/resources/android/splash/drawable-land-mdpi-screen.png new file mode 100644 index 0000000..42b3dd1 Binary files /dev/null and b/resources/android/splash/drawable-land-mdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-xhdpi-screen.png b/resources/android/splash/drawable-land-xhdpi-screen.png new file mode 100644 index 0000000..f3f0e1f Binary files /dev/null and b/resources/android/splash/drawable-land-xhdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-xxhdpi-screen.png b/resources/android/splash/drawable-land-xxhdpi-screen.png new file mode 100644 index 0000000..adc9dd3 Binary files /dev/null and b/resources/android/splash/drawable-land-xxhdpi-screen.png differ diff --git a/resources/android/splash/drawable-land-xxxhdpi-screen.png b/resources/android/splash/drawable-land-xxxhdpi-screen.png new file mode 100644 index 0000000..9183251 Binary files /dev/null and b/resources/android/splash/drawable-land-xxxhdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-hdpi-screen.png b/resources/android/splash/drawable-port-hdpi-screen.png new file mode 100644 index 0000000..b71cc85 Binary files /dev/null and b/resources/android/splash/drawable-port-hdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-ldpi-screen.png b/resources/android/splash/drawable-port-ldpi-screen.png new file mode 100644 index 0000000..7e62464 Binary files /dev/null and b/resources/android/splash/drawable-port-ldpi-screen.png differ diff --git a/resources/android/splash/drawable-port-mdpi-screen.png b/resources/android/splash/drawable-port-mdpi-screen.png new file mode 100644 index 0000000..37da713 Binary files /dev/null and b/resources/android/splash/drawable-port-mdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-xhdpi-screen.png b/resources/android/splash/drawable-port-xhdpi-screen.png new file mode 100644 index 0000000..5127f91 Binary files /dev/null and b/resources/android/splash/drawable-port-xhdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-xxhdpi-screen.png b/resources/android/splash/drawable-port-xxhdpi-screen.png new file mode 100644 index 0000000..1879ea3 Binary files /dev/null and b/resources/android/splash/drawable-port-xxhdpi-screen.png differ diff --git a/resources/android/splash/drawable-port-xxxhdpi-screen.png b/resources/android/splash/drawable-port-xxxhdpi-screen.png new file mode 100644 index 0000000..9388ae8 Binary files /dev/null and b/resources/android/splash/drawable-port-xxxhdpi-screen.png differ diff --git a/resources/icon.png b/resources/icon.png index 9791ff0..48d3437 100644 Binary files a/resources/icon.png and b/resources/icon.png differ diff --git a/resources/ios/icon/icon-1024.png b/resources/ios/icon/icon-1024.png new file mode 100644 index 0000000..3ad93e2 Binary files /dev/null and b/resources/ios/icon/icon-1024.png differ diff --git a/resources/ios/icon/icon-108@2x.png b/resources/ios/icon/icon-108@2x.png new file mode 100644 index 0000000..b30010c Binary files /dev/null and b/resources/ios/icon/icon-108@2x.png differ diff --git a/resources/ios/icon/icon-20.png b/resources/ios/icon/icon-20.png new file mode 100644 index 0000000..5493abb Binary files /dev/null and b/resources/ios/icon/icon-20.png differ diff --git a/resources/ios/icon/icon-20@2x.png b/resources/ios/icon/icon-20@2x.png new file mode 100644 index 0000000..d3f1076 Binary files /dev/null and b/resources/ios/icon/icon-20@2x.png differ diff --git a/resources/ios/icon/icon-20@3x.png b/resources/ios/icon/icon-20@3x.png new file mode 100644 index 0000000..5c498f0 Binary files /dev/null and b/resources/ios/icon/icon-20@3x.png differ diff --git a/resources/ios/icon/icon-24@2x.png b/resources/ios/icon/icon-24@2x.png new file mode 100644 index 0000000..35fdaa1 Binary files /dev/null and b/resources/ios/icon/icon-24@2x.png differ diff --git a/resources/ios/icon/icon-27.5@2x.png b/resources/ios/icon/icon-27.5@2x.png new file mode 100644 index 0000000..865143a Binary files /dev/null and b/resources/ios/icon/icon-27.5@2x.png differ diff --git a/resources/ios/icon/icon-29.png b/resources/ios/icon/icon-29.png new file mode 100644 index 0000000..a717b93 Binary files /dev/null and b/resources/ios/icon/icon-29.png differ diff --git a/resources/ios/icon/icon-29@2x.png b/resources/ios/icon/icon-29@2x.png new file mode 100644 index 0000000..89a11f3 Binary files /dev/null and b/resources/ios/icon/icon-29@2x.png differ diff --git a/resources/ios/icon/icon-29@3x.png b/resources/ios/icon/icon-29@3x.png new file mode 100644 index 0000000..a4c3e3e Binary files /dev/null and b/resources/ios/icon/icon-29@3x.png differ diff --git a/resources/ios/icon/icon-40.png b/resources/ios/icon/icon-40.png new file mode 100644 index 0000000..d3f1076 Binary files /dev/null and b/resources/ios/icon/icon-40.png differ diff --git a/resources/ios/icon/icon-40@2x.png b/resources/ios/icon/icon-40@2x.png new file mode 100644 index 0000000..e038ec3 Binary files /dev/null and b/resources/ios/icon/icon-40@2x.png differ diff --git a/resources/ios/icon/icon-40@3x.png b/resources/ios/icon/icon-40@3x.png new file mode 100644 index 0000000..4096d34 Binary files /dev/null and b/resources/ios/icon/icon-40@3x.png differ diff --git a/resources/ios/icon/icon-44@2x.png b/resources/ios/icon/icon-44@2x.png new file mode 100644 index 0000000..ef01cde Binary files /dev/null and b/resources/ios/icon/icon-44@2x.png differ diff --git a/resources/ios/icon/icon-50.png b/resources/ios/icon/icon-50.png new file mode 100644 index 0000000..651299a Binary files /dev/null and b/resources/ios/icon/icon-50.png differ diff --git a/resources/ios/icon/icon-50@2x.png b/resources/ios/icon/icon-50@2x.png new file mode 100644 index 0000000..3827f67 Binary files /dev/null and b/resources/ios/icon/icon-50@2x.png differ diff --git a/resources/ios/icon/icon-60.png b/resources/ios/icon/icon-60.png new file mode 100644 index 0000000..5c498f0 Binary files /dev/null and b/resources/ios/icon/icon-60.png differ diff --git a/resources/ios/icon/icon-60@2x.png b/resources/ios/icon/icon-60@2x.png new file mode 100644 index 0000000..4096d34 Binary files /dev/null and b/resources/ios/icon/icon-60@2x.png differ diff --git a/resources/ios/icon/icon-60@3x.png b/resources/ios/icon/icon-60@3x.png new file mode 100644 index 0000000..551e69d Binary files /dev/null and b/resources/ios/icon/icon-60@3x.png differ diff --git a/resources/ios/icon/icon-72.png b/resources/ios/icon/icon-72.png new file mode 100644 index 0000000..32c65a5 Binary files /dev/null and b/resources/ios/icon/icon-72.png differ diff --git a/resources/ios/icon/icon-72@2x.png b/resources/ios/icon/icon-72@2x.png new file mode 100644 index 0000000..8ab3be6 Binary files /dev/null and b/resources/ios/icon/icon-72@2x.png differ diff --git a/resources/ios/icon/icon-76.png b/resources/ios/icon/icon-76.png new file mode 100644 index 0000000..16fc528 Binary files /dev/null and b/resources/ios/icon/icon-76.png differ diff --git a/resources/ios/icon/icon-76@2x.png b/resources/ios/icon/icon-76@2x.png new file mode 100644 index 0000000..c64d330 Binary files /dev/null and b/resources/ios/icon/icon-76@2x.png differ diff --git a/resources/ios/icon/icon-83.5@2x.png b/resources/ios/icon/icon-83.5@2x.png new file mode 100644 index 0000000..45c9bcb Binary files /dev/null and b/resources/ios/icon/icon-83.5@2x.png differ diff --git a/resources/ios/icon/icon-86@2x.png b/resources/ios/icon/icon-86@2x.png new file mode 100644 index 0000000..7534ac2 Binary files /dev/null and b/resources/ios/icon/icon-86@2x.png differ diff --git a/resources/ios/icon/icon-98@2x.png b/resources/ios/icon/icon-98@2x.png new file mode 100644 index 0000000..368316e Binary files /dev/null and b/resources/ios/icon/icon-98@2x.png differ diff --git a/resources/ios/icon/icon.png b/resources/ios/icon/icon.png new file mode 100644 index 0000000..bc02a26 Binary files /dev/null and b/resources/ios/icon/icon.png differ diff --git a/resources/ios/icon/icon@2x.png b/resources/ios/icon/icon@2x.png new file mode 100644 index 0000000..2c1403c Binary files /dev/null and b/resources/ios/icon/icon@2x.png differ diff --git a/resources/ios/splash/Default-1792h~iphone.png b/resources/ios/splash/Default-1792h~iphone.png new file mode 100644 index 0000000..91a88f4 Binary files /dev/null and b/resources/ios/splash/Default-1792h~iphone.png differ diff --git a/resources/ios/splash/Default-2436h.png b/resources/ios/splash/Default-2436h.png new file mode 100644 index 0000000..9327186 Binary files /dev/null and b/resources/ios/splash/Default-2436h.png differ diff --git a/resources/ios/splash/Default-2688h~iphone.png b/resources/ios/splash/Default-2688h~iphone.png new file mode 100644 index 0000000..1fe9cf8 Binary files /dev/null and b/resources/ios/splash/Default-2688h~iphone.png differ diff --git a/resources/ios/splash/Default-568h@2x~iphone.png b/resources/ios/splash/Default-568h@2x~iphone.png new file mode 100644 index 0000000..62b49e5 Binary files /dev/null and b/resources/ios/splash/Default-568h@2x~iphone.png differ diff --git a/resources/ios/splash/Default-667h.png b/resources/ios/splash/Default-667h.png new file mode 100644 index 0000000..6dbb9c9 Binary files /dev/null and b/resources/ios/splash/Default-667h.png differ diff --git a/resources/ios/splash/Default-736h.png b/resources/ios/splash/Default-736h.png new file mode 100644 index 0000000..dfe9f6c Binary files /dev/null and b/resources/ios/splash/Default-736h.png differ diff --git a/resources/ios/splash/Default-Landscape-1792h~iphone.png b/resources/ios/splash/Default-Landscape-1792h~iphone.png new file mode 100644 index 0000000..6d7b8a9 Binary files /dev/null and b/resources/ios/splash/Default-Landscape-1792h~iphone.png differ diff --git a/resources/ios/splash/Default-Landscape-2436h.png b/resources/ios/splash/Default-Landscape-2436h.png new file mode 100644 index 0000000..a125e4b Binary files /dev/null and b/resources/ios/splash/Default-Landscape-2436h.png differ diff --git a/resources/ios/splash/Default-Landscape-2688h~iphone.png b/resources/ios/splash/Default-Landscape-2688h~iphone.png new file mode 100644 index 0000000..4807d5e Binary files /dev/null and b/resources/ios/splash/Default-Landscape-2688h~iphone.png differ diff --git a/resources/ios/splash/Default-Landscape-736h.png b/resources/ios/splash/Default-Landscape-736h.png new file mode 100644 index 0000000..9e14349 Binary files /dev/null and b/resources/ios/splash/Default-Landscape-736h.png differ diff --git a/resources/ios/splash/Default-Landscape@2x~ipad.png b/resources/ios/splash/Default-Landscape@2x~ipad.png new file mode 100644 index 0000000..10018b0 Binary files /dev/null and b/resources/ios/splash/Default-Landscape@2x~ipad.png differ diff --git a/resources/ios/splash/Default-Landscape@~ipadpro.png b/resources/ios/splash/Default-Landscape@~ipadpro.png new file mode 100644 index 0000000..8ea7d88 Binary files /dev/null and b/resources/ios/splash/Default-Landscape@~ipadpro.png differ diff --git a/resources/ios/splash/Default-Landscape~ipad.png b/resources/ios/splash/Default-Landscape~ipad.png new file mode 100644 index 0000000..c4c1927 Binary files /dev/null and b/resources/ios/splash/Default-Landscape~ipad.png differ diff --git a/resources/ios/splash/Default-Portrait@2x~ipad.png b/resources/ios/splash/Default-Portrait@2x~ipad.png new file mode 100644 index 0000000..f5721d8 Binary files /dev/null and b/resources/ios/splash/Default-Portrait@2x~ipad.png differ diff --git a/resources/ios/splash/Default-Portrait@~ipadpro.png b/resources/ios/splash/Default-Portrait@~ipadpro.png new file mode 100644 index 0000000..4976459 Binary files /dev/null and b/resources/ios/splash/Default-Portrait@~ipadpro.png differ diff --git a/resources/ios/splash/Default-Portrait~ipad.png b/resources/ios/splash/Default-Portrait~ipad.png new file mode 100644 index 0000000..999eba8 Binary files /dev/null and b/resources/ios/splash/Default-Portrait~ipad.png differ diff --git a/resources/ios/splash/Default@2x~iphone.png b/resources/ios/splash/Default@2x~iphone.png new file mode 100644 index 0000000..592b045 Binary files /dev/null and b/resources/ios/splash/Default@2x~iphone.png differ diff --git a/resources/ios/splash/Default@2x~universal~anyany.png b/resources/ios/splash/Default@2x~universal~anyany.png new file mode 100644 index 0000000..d1e4437 Binary files /dev/null and b/resources/ios/splash/Default@2x~universal~anyany.png differ diff --git a/resources/ios/splash/Default~iphone.png b/resources/ios/splash/Default~iphone.png new file mode 100644 index 0000000..37da713 Binary files /dev/null and b/resources/ios/splash/Default~iphone.png differ diff --git a/resources/logo.png b/resources/logo.png new file mode 100644 index 0000000..611ebf6 Binary files /dev/null and b/resources/logo.png differ diff --git a/src/app/_helper/auth-guard.ts b/src/app/_helper/auth-guard.ts new file mode 100644 index 0000000..4333bd6 --- /dev/null +++ b/src/app/_helper/auth-guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router } from '@angular/router'; +import { KeycloakService } from 'keycloak-angular'; +import { D4sAuthService } from '../d4sauth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + + constructor( + private auth: D4sAuthService, + private router: Router + ) { + } + + canActivate() { + + console.log("in the authguard"); + + var logged = this.auth.isAuthorized(); + + if (!logged) { + this.router.navigate(['login']); + } + + console.log(`is logged in ? ${logged}`); + + return logged; + } +} \ No newline at end of file diff --git a/src/app/_helper/keycloak-init.factory.ts b/src/app/_helper/keycloak-init.factory.ts new file mode 100644 index 0000000..8bc2f23 --- /dev/null +++ b/src/app/_helper/keycloak-init.factory.ts @@ -0,0 +1,18 @@ +import { KeycloakService } from "keycloak-angular"; +import * as Keycloak from "keycloak-js"; +export function initializeKeycloak(keycloak: KeycloakService) { + + return () => keycloak.init({ + config: { + url: 'https://accounts.dev.d4science.org/auth', + realm: 'd4science', + clientId: 'workspaceapp', + }, + initOptions: { + redirectUri: 'http://localhost:8100', //'d4sworkspace://redirect', + checkLoginIframe: false + }, + shouldAddToken: (request) => false + }).then((res) => console.log(res)) + .catch((err) => console.error(err)); +} \ No newline at end of file diff --git a/src/app/_helper/login-guard.ts b/src/app/_helper/login-guard.ts new file mode 100644 index 0000000..3b5833c --- /dev/null +++ b/src/app/_helper/login-guard.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, Router } from '@angular/router'; +import { KeycloakService } from 'keycloak-angular'; +import { D4sAuthService } from '../d4sauth.service'; + +@Injectable({ + providedIn: 'root' +}) +export class LoginGuard implements CanActivate { + + constructor( + private auth: D4sAuthService, + private router: Router + ) { + } + + async canActivate() { + + console.log("in the authguard"); + + var logged =this.auth.isAuthorized(); + + console.log("logged? "+logged); + + if (logged) { + this.router.navigate(['tabs','ws']); + } + + return !logged; + } +} \ No newline at end of file diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index cb5b2dd..08a1c10 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,10 +1,20 @@ import { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; +import { AuthGuard } from './_helper/auth-guard'; +import { LoginGuard } from './_helper/login-guard'; + +const loginModule = () => import('./login/login.module').then(m => m.LoginPageModule); const routes: Routes = [ + { - path: '', - loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule) + path: 'login', + loadChildren: loginModule, // canActivate : [LoginGuard] + }, + { path: '', redirectTo: '/login', pathMatch: 'full'}, + { + path: 'tabs', + loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule) , canActivate: [AuthGuard] }, ]; @NgModule({ @@ -13,4 +23,4 @@ const routes: Routes = [ ], exports: [RouterModule] }) -export class AppRoutingModule {} +export class AppRoutingModule { } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3cedf0e..e0001f4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,17 +1,29 @@ import { HttpClientModule } from '@angular/common/http'; -import { NgModule } from '@angular/core'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { RouteReuseStrategy } from '@angular/router'; - +import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular'; import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; -import { OAuthModule } from 'angular-oauth2-oidc' import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import {initializeKeycloak} from './_helper/keycloak-init.factory' +import { D4sAuthService } from './d4sauth.service'; @NgModule({ declarations: [AppComponent], - imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule, HttpClientModule], - providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }], + imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule, HttpClientModule, KeycloakAngularModule], + + providers: [ + D4sAuthService, + { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, + { + provide: APP_INITIALIZER, + useFactory: initializeKeycloak, + multi: true, + deps: [KeycloakService] + }, + + ], bootstrap: [AppComponent] }) export class AppModule { diff --git a/src/app/d4sauth.service.spec.ts b/src/app/d4sauth.service.spec.ts new file mode 100644 index 0000000..cbc6621 --- /dev/null +++ b/src/app/d4sauth.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { D4sAuthService } from './d4sauth.service'; + +describe('D4sauthService', () => { + let service: D4sAuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(D4sAuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/d4sauth.service.ts b/src/app/d4sauth.service.ts new file mode 100644 index 0000000..49509b7 --- /dev/null +++ b/src/app/d4sauth.service.ts @@ -0,0 +1,82 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { resolve } from 'dns'; +import { KeycloakService } from 'keycloak-angular'; + +@Injectable({ + providedIn: 'root' +}) +export class D4sAuthService { + + redirectUrl: string = 'http://localhost:8100/'; //'d4sworkspace://org.gcube.workspace/'; + + umaUrl : string = " https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token"; + + audience: string = '%2Fgcube'; + + config: any = undefined; + + uma : UmaToken | undefined ; + + constructor(private keycloak: KeycloakService, private httpClient: HttpClient) { + } + + async login() { + if (!this.isAuthorized()) { + this.keycloak.login({ + redirectUri: this.redirectUrl + }); + } + + if (!this.isAuthorized()) + throw("error authorizing"); + + await this.entitlement(this.audience).then( res => this.uma = res); + } + + isAuthorized(): boolean { + var auth = this.keycloak.getKeycloakInstance()?.authenticated; + if (!auth) return false; + else + return auth; + } + + getSecureHeader(): string{ + return "Bearer "+this.uma?.access_token; + } + + entitlement(resourceServerId: any): Promise { + return new Promise((resolve, reject) => { + const keycloak = this.keycloak.getKeycloakInstance(); + + var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" + + const audience = encodeURIComponent(resourceServerId) + params += "&audience=" + audience; + + console.log("params "+params); + + this.httpClient.post(this.umaUrl, params , { headers: { + "Content-type": "application/x-www-form-urlencoded", + "Authorization": "Bearer " + this.keycloak.getKeycloakInstance().token + } }).subscribe( + { + error: (err) => reject("error getting uma token "+err), + next: (res:UmaToken) => resolve(res) + } + ); + }); + } + +} + +class UmaToken{ + + constructor(public upgraded :boolean, + public access_token:string, + public expires_in: number, + public refresh_expires_in: number, + public refresh_token: string, + public token_type: string, + public not_before_policy:number){} +} \ No newline at end of file diff --git a/src/app/login/login-routing.module.ts b/src/app/login/login-routing.module.ts new file mode 100644 index 0000000..29ef3a2 --- /dev/null +++ b/src/app/login/login-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { LoginPage } from './login.page'; + +const routes: Routes = [ + { + path: '', + component: LoginPage + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class LoginPageRoutingModule {} diff --git a/src/app/login/login.module.ts b/src/app/login/login.module.ts new file mode 100644 index 0000000..40e2562 --- /dev/null +++ b/src/app/login/login.module.ts @@ -0,0 +1,20 @@ +import { APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { LoginPageRoutingModule } from './login-routing.module'; + +import { LoginPage } from './login.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + LoginPageRoutingModule + ], + declarations: [LoginPage] +}) +export class LoginPageModule {} diff --git a/src/app/login/login.page.html b/src/app/login/login.page.html new file mode 100644 index 0000000..e69de29 diff --git a/src/app/login/login.page.scss b/src/app/login/login.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/login/login.page.spec.ts b/src/app/login/login.page.spec.ts new file mode 100644 index 0000000..e6f9aac --- /dev/null +++ b/src/app/login/login.page.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { LoginPage } from './login.page'; + +describe('LoginPage', () => { + let component: LoginPage; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ LoginPage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(LoginPage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/login/login.page.ts b/src/app/login/login.page.ts new file mode 100644 index 0000000..20d6452 --- /dev/null +++ b/src/app/login/login.page.ts @@ -0,0 +1,23 @@ +import { AfterViewInit, Component, } from '@angular/core'; +import { Router } from '@angular/router'; +import { D4sAuthService } from '../d4sauth.service'; + +@Component({ + selector: 'app-login', + templateUrl: './login.page.html', + styleUrls: ['./login.page.scss'] +}) +export class LoginPage implements AfterViewInit { + + constructor(private auth: D4sAuthService, private router : Router) { } + + + ngAfterViewInit(): void{ + this.auth.login().then(ret => { + console.log("logged in - trying to navigates to tabs "+ret); + this.router.navigateByUrl('/tabs/ws').then(() => console.log("navigating")).catch((e) => console.log("error "+e)); + }).catch( err => console.log(err)); + } + + +} diff --git a/src/app/model/ws-item.ts b/src/app/model/ws-item.ts index bc9fa7c..7e0bb38 100644 --- a/src/app/model/ws-item.ts +++ b/src/app/model/ws-item.ts @@ -56,9 +56,10 @@ export class WSItem { this.type === 'GenericFileItem'; } - getTitle(): string{ - var title = this.item.vreFolder? this.item.displayName: this.item.title; - return title? title : this.item.name; + getTitle(): string{ + if (this.item.displayName && this.item.vreFolder) + return this.item.displayName; + else return this.item.title; } } diff --git a/src/app/storagehub.service.ts b/src/app/storagehub.service.ts index b8b551b..85c611b 100644 --- a/src/app/storagehub.service.ts +++ b/src/app/storagehub.service.ts @@ -7,117 +7,127 @@ import { HttpErrorResponse, HttpHeaders } from 'node-angular-http-client'; import { Item } from './model/item.model'; import { ItemList } from './model/itemlist.model'; import { ItemWrapper } from './model/item-wrapper.model'; +import { D4sAuthService } from './d4sauth.service'; -const token = "b7c80297-e4ed-42ab-ab42-fdc0b8b0eabf-98187548"; -//const shURL ="https://workspace-repository.dev.d4science.org/storagehub/workspace" -const shURL = "/shub/storagehub/workspace"; - +const shURL ="https://api.dev.d4science.org/workspace" @Injectable() export class StoragehubService { - constructor(private http: HttpClient) { + constructor(private http: HttpClient, private auth : D4sAuthService) { } - getWsRoot(): Observable { - const getWsURL = `${shURL}?gcube-token=${token}&exclude=hl:accounting`; - return this.http.get(getWsURL, {observe: 'body', responseType: 'json'}).pipe( + getWsRoot() { + const getWsURL = `${shURL}?exclude=hl:accounting`; + const bearer = this.auth.getSecureHeader(); + return this.http.get(getWsURL, {headers: {"Authorization": bearer }, observe: 'body', responseType: 'json'}).pipe( map(value => value.item), catchError(this.error) ); } getTrashRoot(): Observable { - const getTrashURL = `${shURL}/trash/?gcube-token=${token}&exclude=hl:accounting`; - return this.http.get(getTrashURL, {observe: "body", responseType: "json"}).pipe( + const bearer = this.auth.getSecureHeader(); + const getTrashURL = `${shURL}/trash/?exclude=hl:accounting`; + return this.http.get(getTrashURL, {headers: {"Authorization": bearer }, observe: "body", responseType: "json"}).pipe( map(value => value.item), catchError(this.error) ); } getVres(): Observable { - const getVresURL = `${shURL}/vrefolders/?gcube-token=${token}&exclude=hl:accounting`; - return this.http.get(getVresURL, {observe: 'body', responseType: 'json'}).pipe( + const bearer = this.auth.getSecureHeader(); + const getVresURL = `${shURL}/vrefolders/?exclude=hl:accounting`; + return this.http.get(getVresURL, {headers: {"Authorization": bearer }, observe: 'body', responseType: 'json'}).pipe( map(value => value.itemlist), catchError(this.error) ); } getItem(id: string): Observable { - const getWsURL = `${shURL}/items/${id}?gcube-token=${token}&exclude=hl:accounting`; - return this.http.get(getWsURL, {observe: 'body', responseType: 'json'}).pipe( + const bearer = this.auth.getSecureHeader(); + const getWsURL = `${shURL}/items/${id}?exclude=hl:accounting`; + return this.http.get(getWsURL, {headers: {"Authorization": bearer }, observe: 'body', responseType: 'json'}).pipe( map(value => value.item), catchError(this.error) ); } getChildren(id:string, onlyFolder?:boolean): Observable{ - let getWsURL = `${shURL}/items/${id}/children?gcube-token=${token}&exclude=hl:accounting`; + const bearer = this.auth.getSecureHeader(); + let getWsURL = `${shURL}/items/${id}/children?exclude=hl:accounting`; if (onlyFolder) getWsURL += '&onlyType=nthl:workspaceItem'; - return this.http.get(getWsURL).pipe( + return this.http.get(getWsURL, {headers: {"Authorization": bearer }}).pipe( map(values => values.itemlist), catchError(this.error) ); } getAnchestor(id:string): Observable{ - let getWsURL = `${shURL}/items/${id}/ancherstors?gcube-token=${token}&exclude=hl:accounting`; + const bearer = this.auth.getSecureHeader(); + let getWsURL = `${shURL}/items/${id}/ancherstors?exclude=hl:accounting`; console.log("ws children "+getWsURL); - return this.http.get(getWsURL, {observe: 'body', responseType: 'json'}).pipe( + return this.http.get(getWsURL, {headers: {"Authorization": bearer }, observe: 'body', responseType: 'json'}).pipe( map(value => value.itemlist), catchError(this.error) ); } uploadFile(parentId: string, name: string, file: File){ - let uploadURL = `${shURL}/items/${parentId}/create/FILE?gcube-token=${token}`; + const bearer = this.auth.getSecureHeader(); + let uploadURL = `${shURL}/items/${parentId}/create/FILE`; const formData = new FormData(); formData.append("name", name); formData.append("description", ""); formData.append("file",file); - return this.http.post(uploadURL, formData, {observe: 'events', responseType: 'text', reportProgress: true}).pipe( + return this.http.post(uploadURL, formData, {headers: {"Authorization": bearer }, observe: 'events', responseType: 'text', reportProgress: true}).pipe( catchError(this.error) ); } createFolder(parentId: string, name: string, description: string) : Observable { - let createFolderURL = `${shURL}/items/${parentId}/create/FOLDER?gcube-token=${token}`; + const bearer = this.auth.getSecureHeader(); + let createFolderURL = `${shURL}/items/${parentId}/create/FOLDER`; const formData = new FormData(); let body = `name=${name}&description=${description}`; - return this.http.post(createFolderURL, body, {observe: 'body', responseType: 'text' , headers: { 'Content-Type' : 'application/x-www-form-urlencoded' }}).pipe( + return this.http.post(createFolderURL, body, {observe: 'body', responseType: 'text' , headers: { 'Content-Type' : 'application/x-www-form-urlencoded', "Authorization": bearer }}).pipe( catchError(this.error) ); } deleteItem(itemId: string) : Observable { - let deleteItemUrl = `${shURL}/items/${itemId}?gcube-token=${token}`; + const bearer = this.auth.getSecureHeader(); + let deleteItemUrl = `${shURL}/items/${itemId}`; return this.http.delete( deleteItemUrl).pipe( catchError(this.error) ); } renameItem(itemId: string, newName: string) : Observable { - let renameItemUrl = `${shURL}/items/${itemId}/rename?gcube-token=${token}`; + const bearer = this.auth.getSecureHeader(); + let renameItemUrl = `${shURL}/items/${itemId}/rename`; let body = `newName=${newName}`; - return this.http.put(renameItemUrl,body,{observe: 'body', responseType: 'text' ,headers: { 'Content-Type' : 'application/x-www-form-urlencoded' }}).pipe( + return this.http.put(renameItemUrl,body,{observe: 'body', responseType: 'text' ,headers: { 'Content-Type' : 'application/x-www-form-urlencoded', "Authorization": bearer }}).pipe( catchError(this.error) ); } copyItem(destinationId: string, itemId: string, name:string) : Observable { - let copyItemUrl = `${shURL}/items/${itemId}/copy?gcube-token=${token}`; + const bearer = this.auth.getSecureHeader(); + let copyItemUrl = `${shURL}/items/${itemId}/copy`; let body = `destinationId=${destinationId}&fileName=${name}`; - return this.http.put(copyItemUrl,body,{observe: 'body', responseType: 'text' ,headers: { 'Content-Type' : 'application/x-www-form-urlencoded' }}).pipe( + return this.http.put(copyItemUrl,body,{observe: 'body', responseType: 'text' ,headers: { 'Content-Type' : 'application/x-www-form-urlencoded', "Authorization": bearer }}).pipe( catchError(this.error) ); } moveItem(destinationId: string, itemId: string) : Observable { - let copyItemUrl = `${shURL}/items/${itemId}/move?gcube-token=${token}`; + const bearer = this.auth.getSecureHeader(); + let copyItemUrl = `${shURL}/items/${itemId}/move`; let body = `destinationId=${destinationId}`; - return this.http.put(copyItemUrl,body,{observe: 'body', responseType: 'text' ,headers: { 'Content-Type' : 'application/x-www-form-urlencoded' }}).pipe( + return this.http.put(copyItemUrl,body,{observe: 'body', responseType: 'text' ,headers: { 'Content-Type' : 'application/x-www-form-urlencoded', "Authorization": bearer }}).pipe( catchError(this.error) ); } diff --git a/src/app/tabs/tabs-routing.module.ts b/src/app/tabs/tabs-routing.module.ts index aae0477..c40f858 100644 --- a/src/app/tabs/tabs-routing.module.ts +++ b/src/app/tabs/tabs-routing.module.ts @@ -4,7 +4,7 @@ import { TabsPage } from './tabs.page'; const routes: Routes = [ { - path: 'tabs', + path: '', component: TabsPage, children: [ { @@ -32,11 +32,11 @@ const routes: Routes = [ } ] }, - { + /*{ path: '', redirectTo: '/tabs/ws', pathMatch: 'full' - } + }*/ ]; @NgModule({ diff --git a/src/app/tabs/tabs.page.ts b/src/app/tabs/tabs.page.ts index 7d64f97..af10391 100644 --- a/src/app/tabs/tabs.page.ts +++ b/src/app/tabs/tabs.page.ts @@ -3,10 +3,14 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-tabs', templateUrl: 'tabs.page.html', - styleUrls: ['tabs.page.scss'] + styleUrls: ['tabs.page.scss'], + }) export class TabsPage { + public auth : boolean = false; + constructor() {} + } diff --git a/src/app/ws/ws.page.html b/src/app/ws/ws.page.html index 5f4def9..cae9aa5 100644 --- a/src/app/ws/ws.page.html +++ b/src/app/ws/ws.page.html @@ -1,5 +1,3 @@ - - diff --git a/src/assets/js/d4s-boot-mod.js b/src/assets/js/d4s-boot-mod.js new file mode 100644 index 0000000..638dca3 --- /dev/null +++ b/src/assets/js/d4s-boot-mod.js @@ -0,0 +1,376 @@ +/** + * D4Science boot component that handles keykloak authz + * { + console.log("Keycloak loaded") + return this.initKeycloak() + + }).then(authenticated => { + if (!authenticated) { + throw "Failed to authenticate" + } + console.log("Keycloak initialized and user authenticated") + //console.log("Token exp: " + this.expirationDate(this.#keycloak.tokenParsed.exp)) + + //if an audience is provided then perform also authorization + if (this.#audience) { + return this.loadConfig() + } else { + Promise.resolve() + } + + }).then(() => { + this.#authenticated = true + this.unlock() + this.fire("authenticated") + + }).catch(err => { + console.error("Unable to initialize Keycloak", err) + }) + } + + loadKeycloak() { + return new Promise((resolve, reject) => { + if (typeof Keycloak === 'undefined') { + const script = document.createElement('script') + script.src = this.#url + '/js/keycloak.js' + script.type = 'text/javascript' + script.addEventListener('load', resolve) + document.head.appendChild(script) + } else { + resolve() + } + }) + } + + initKeycloak() { + this.#keycloak = new Keycloak({ + url: this.#url, + realm: this.#realm, + clientId: this.#clientId + }) + + return this.#keycloak.init({onLoad: 'login-required', checkLoginIframe: false }) + } + + startStateChecker() { + this.#interval = window.setInterval(() => { + if (this.#locked) { + console.log("Still locked. Currently has " + this.#queue.length + " pending requests.") + } else if (!this.authenticated) { + window.alert("Not authorized!") + } else { + if (this.#queue.length > 0) { + this.#keycloak.updateToken(30).then(() => { + if (this.#audience) { + console.log("Checking entitlement for audience", this.#audience) + const audience = encodeURIComponent(this.#audience) + return this.entitlement(audience) + } else { + return Promise.resolve(this.#keycloak.token) + } + + }).then(token => { + console.log("Authorized. Token exp: " + this.expirationDate(this.parseJwt(token).exp)) + //transform all queued requests to fetches + console.log("All pending requests to promises") + let promises = this.#queue.map(r => { + r.request.headers["Authorization"] = "Bearer " + token + return r.resolve( fetch(r.url, r.request) ) + }) + //clear queue + this.#queue = [] + console.log("Resolving all fetches") + return Promise.all(promises) + + }).catch(err => console.error("Unable to make calls: " + err)) // Sometimes throws: Unable to make calls: TypeError: Cannot read properties of undefined (reading 'split') + } + } + }, 300) + } + + parseJwt(token) { + var base64Url = token.split('.')[1] + var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/') + var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + }).join('')) + + return JSON.parse(jsonPayload); + } + + expirationDate(utc) { + let d = new Date(0) + d.setUTCSeconds(utc) + return d + } + + checkContext() { + const parseJwt = this.parseJwt + const expDt = this.expirationDate + const audience = encodeURIComponent(this.#audience) + this.entitlement(audience).then(function (rpt) { + // onGrant callback function. + // If authorization was successful you'll receive an RPT + // with the necessary permissions to access the resource server + //console.log(rpt) + //console.log("rpt expires: " + expDt(parseJwt(rpt).exp)) + }) + } + + secureFetch(url, request) { + const p = new Promise((resolve, reject) => { + let req = request ? request : {} + if ("headers" in req) { + req.headers["Authorization"] = null + } else { + req.headers = { "Authorization" : null} + } + console.log("Queued request to url ", url) + this.#queue.push({ url : url, request : req, resolve : resolve, reject : reject}) + }) + return p + } + + logout() { + if (this.#keycloak) { + if (!this.#redirectUrl) { + console.error("Missing required @redirectUrl attribute in d4s-boot") + } else { + this.#keycloak.logout({ + redirectUri: this.#redirectUrl + }) + } + } + } + + loadConfig() { + const kc = this.#keycloak + return new Promise((resolve, reject) => { + fetch(kc.authServerUrl + '/realms/' + kc.realm + '/.well-known/uma2-configuration') + .then(response => response.json()) + .then(json => { + this.#config = json + console.log("Keycloak uma2 configuration loaded") + resolve(true) + }) + .catch(err => reject("Failed to fetch uma2-configuration from server: " + err)) + }) + } + + /** + * Refactor of entitlement in '/js/keycloak-authz.js' + * (dependency no longer needed) + */ + entitlement(resourceServerId, authorizationRequest) { + return new Promise((resolve, reject) => { + const keycloak = this.#keycloak + const config = this.#config + if (!keycloak || !config) { + reject("Keycloak not initialized") + + } else { + if (!authorizationRequest) { + authorizationRequest = {} + } + var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId + if (authorizationRequest.claimToken) { + params += "&claim_token=" + authorizationRequest.claimToken + if (authorizationRequest.claimTokenFormat) { + params += "&claim_token_format=" + authorizationRequest.claimTokenFormat + } + } + + params += "&audience=" + resourceServerId + + var permissions = authorizationRequest.permissions + + if (!permissions) { + permissions = [] + } + + for (var i = 0; i < permissions.length; i++) { + var resource = permissions[i] + var permission = resource.id + + if (resource.scopes && resource.scopes.length > 0) { + permission += "#" + for (j = 0; j < resource.scopes.length; j++) { + var scope = resource.scopes[j] + if (permission.indexOf('#') != permission.length - 1) { + permission += "," + } + permission += scope + } + } + + params += "&permission=" + permission + } + + var metadata = authorizationRequest.metadata + if (metadata) { + if (metadata.responseIncludeResourceName) { + params += "&response_include_resource_name=" + metadata.responseIncludeResourceName + } + if (metadata.responsePermissionsLimit) { + params += "&response_permissions_limit=" + metadata.responsePermissionsLimit + } + } + + if (this.#rpt) { + params += "&rpt=" + this.#rpt + } + + fetch(config.token_endpoint, { + method: 'POST', + headers: { + "Content-type" : "application/x-www-form-urlencoded", + "Authorization" : "Bearer " + this.#keycloak.token + }, + body: params + }) + .then(response => response.json()) + .then(json => { + this.#rpt = json.access_token + resolve(this.#rpt) + }) + .catch(err => reject(err)) + } + }) + } + + static get observedAttributes() { + return ["url", "realm", "gateway", "redirect-url", "context"]; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue !== newValue) { + switch (name) { + case "url": + this.#url = newValue + break + case "realm": + this.#realm = newValue + break + case "gateway": + this.#clientId = newValue + break + case "redirect-url": + this.#redirectUrl = newValue + break + case "context": + this.#audience = newValue + break + } + } + } + + get authenticated(){ + return this.#authenticated + } + + get subject(){ + return this.#keycloak.subject + } + + get loginToken(){ + return this.#keycloak.tokenParsed + } + + get url() { + return this.#url + } + + set url(url) { + this.#url = url + this.setAttribute("url", url) + } + + get realm() { + return this.#realm + } + + set realm(realm) { + this.#realm = realm + this.setAttribute("realm", realm) + } + + get clientId() { + return this.#clientId + } + + set clientId(clientId) { + this.#clientId = clientId + this.setAttribute("gateway", clientId) + } + + get redirectUrl() { + return this.#redirectUrl + } + + set redirectUrl(redirectUrl) { + this.#redirectUrl = redirectUrl + this.setAttribute("redirect-url", redirectUrl) + } + + get context() { + return this.#audience + } + + set context(context) { + this.#audience = context + this.setAttribute("context", context) + } + }) + \ No newline at end of file diff --git a/src/proxy.conf.json b/src/proxy.conf.json deleted file mode 100644 index cae6154..0000000 --- a/src/proxy.conf.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "/shub": { - "target": "https://workspace-repository.dev.d4science.org", - "secure": "true", - "changeOrigin": "true", - "logLevel": "debug", - "pathRewrite": {"^/shub" : ""} - } -} \ No newline at end of file