Merge pull request #3790 from crazyserver/MOBILE-4201

Mobile 4201
main
Dani Palou 2023-09-21 12:58:06 +02:00 committed by GitHub
commit 3dd5acc411
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 72 additions and 38 deletions

View File

@ -336,7 +336,7 @@ class behat_app extends behat_app_helper {
// Note there are two 'Log in' texts visible (the title and the button) so we have to use // Note there are two 'Log in' texts visible (the title and the button) so we have to use
// a 'near' value here. // a 'near' value here.
$this->i_press_in_the_app('"Log in" near "Forgotten"'); $this->i_press_in_the_app('"Log in" "ion-button"');
// Wait until the main page appears. // Wait until the main page appears.
$this->spin( $this->spin(

View File

@ -2074,7 +2074,7 @@
"core.login.findyoursite": "local_moodlemobileapp", "core.login.findyoursite": "local_moodlemobileapp",
"core.login.firsttime": "moodle", "core.login.firsttime": "moodle",
"core.login.forcepasswordchangenotice": "moodle", "core.login.forcepasswordchangenotice": "moodle",
"core.login.forgotten": "moodle", "core.login.forgotaccount": "moodle",
"core.login.help": "moodle", "core.login.help": "moodle",
"core.login.instructions": "auth", "core.login.instructions": "auth",
"core.login.invalidaccount": "local_moodlemobileapp", "core.login.invalidaccount": "local_moodlemobileapp",

View File

@ -2771,7 +2771,7 @@ export type CoreSitePublicConfigResponse = {
maintenancemessage: string; // Maintenance message. maintenancemessage: string; // Maintenance message.
logourl?: string; // The site logo URL. logourl?: string; // The site logo URL.
compactlogourl?: string; // The site compact logo URL. compactlogourl?: string; // The site compact logo URL.
typeoflogin: number; // The type of login. 1 for app, 2 for browser, 3 for embedded. typeoflogin: TypeOfLogin; // The type of login. 1 for app, 2 for browser, 3 for embedded.
launchurl?: string; // SSO login launch URL. launchurl?: string; // SSO login launch URL.
mobilecssurl?: string; // Mobile custom CSS theme. mobilecssurl?: string; // Mobile custom CSS theme.
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -2869,3 +2869,12 @@ enum OngoingRequestType {
STANDARD = 0, STANDARD = 0,
UPDATE_IN_BACKGROUND = 1, UPDATE_IN_BACKGROUND = 1,
} }
/**
* The type of login. 1 for app, 2 for browser, 3 for embedded.
*/
export enum TypeOfLogin {
APP = 1,
BROWSER = 2, // SSO in browser window is required.
EMBEDDED = 3, // SSO in embedded browser is required.
}

View File

@ -82,7 +82,13 @@ export class CoreConstants {
static readonly WS_PREFIX = 'local_mobile_'; // @deprecated since app 4.0. static readonly WS_PREFIX = 'local_mobile_'; // @deprecated since app 4.0.
// Login constants. // Login constants.
/**
* @deprecated since 4.3 Use TypeOfLogin.BROWSER instead.
*/
static readonly LOGIN_SSO_CODE = 2; // SSO in browser window is required. static readonly LOGIN_SSO_CODE = 2; // SSO in browser window is required.
/**
* @deprecated since 4.3 Use TypeOfLogin.EMBEDDED instead.
*/
static readonly LOGIN_SSO_INAPP_CODE = 3; // SSO in embedded browser is required. static readonly LOGIN_SSO_INAPP_CODE = 3; // SSO in embedded browser is required.
static readonly LOGIN_LAUNCH_DATA = 'CoreLoginLaunchData'; static readonly LOGIN_LAUNCH_DATA = 'CoreLoginLaunchData';

View File

@ -31,6 +31,7 @@ export class CoreLoginMethodsComponent implements OnInit {
@Input() siteConfig?: CoreSitePublicConfigResponse; @Input() siteConfig?: CoreSitePublicConfigResponse;
@Input() redirectData?: CoreRedirectPayload; @Input() redirectData?: CoreRedirectPayload;
isBrowserSSO = false;
showScanQR = false; showScanQR = false;
loginMethods: CoreLoginMethod[] = []; loginMethods: CoreLoginMethod[] = [];
identityProviders: CoreSiteIdentityProvider[] = []; identityProviders: CoreSiteIdentityProvider[] = [];
@ -50,9 +51,14 @@ export class CoreLoginMethodsComponent implements OnInit {
} }
if (this.siteConfig) { if (this.siteConfig) {
this.isBrowserSSO = CoreLoginHelper.isSSOLoginNeeded(this.siteConfig.typeoflogin);
if (!this.isBrowserSSO) {
// Identity providers won't be shown if login on browser.
const disabledFeatures = CoreLoginHelper.getDisabledFeatures(this.siteConfig); const disabledFeatures = CoreLoginHelper.getDisabledFeatures(this.siteConfig);
this.identityProviders = CoreLoginHelper.getValidIdentityProviders(this.siteConfig, disabledFeatures); this.identityProviders = CoreLoginHelper.getValidIdentityProviders(this.siteConfig, disabledFeatures);
}
if (this.reconnect) { if (this.reconnect) {
this.showScanQR = CoreLoginHelper.displayQRInSiteScreen(); this.showScanQR = CoreLoginHelper.displayQRInSiteScreen();

View File

@ -37,7 +37,7 @@
"exceededpasswordresetattemptssupportsubject": "I can't reset my password", "exceededpasswordresetattemptssupportsubject": "I can't reset my password",
"faqcannotfindmysiteanswer": "If you tried searching by URL address and still can't find your Moodle site, please get in touch with the person who takes care of Moodle in your school or learning organisation.", "faqcannotfindmysiteanswer": "If you tried searching by URL address and still can't find your Moodle site, please get in touch with the person who takes care of Moodle in your school or learning organisation.",
"faqcannotfindmysitequestion": "I can't find my site by URL address.", "faqcannotfindmysitequestion": "I can't find my site by URL address.",
"faqcantloginanswer": "<p>Once you've connected to your Moodle site, you should be able to log in with your usual username and password.</p><br><p>If you forgot your username or password, select the option <strong>Forgotten your username or password?</strong>. If you still have trouble logging in or can't see any options for retrieving your username or password, please get in touch with the person who takes care of Moodle in your school or learning organisation.</p>", "faqcantloginanswer": "<p>Once you've connected to your Moodle site, you should be able to log in with your usual username and password.</p><br><p>If you forgot your username or password, select the option <strong>Lost password?</strong>. If you still have trouble logging in or can't see any options for retrieving your username or password, please get in touch with the person who takes care of Moodle in your school or learning organisation.</p>",
"faqcantloginquestion": "I can't log in.", "faqcantloginquestion": "I can't log in.",
"faqmore": "Check out <a href=\"https://docs.moodle.org/en/Moodle_app_FAQ#toc\" target=\"_blank\">our FAQ</a> for more answers.", "faqmore": "Check out <a href=\"https://docs.moodle.org/en/Moodle_app_FAQ#toc\" target=\"_blank\">our FAQ</a> for more answers.",
"faqsetupsiteanswer": "Visit {{$link}} to check out the different options you have to create your own Moodle site.", "faqsetupsiteanswer": "Visit {{$link}} to check out the different options you have to create your own Moodle site.",
@ -52,7 +52,7 @@
"findyoursite": "Find your site", "findyoursite": "Find your site",
"firsttime": "Is this your first time here?", "firsttime": "Is this your first time here?",
"forcepasswordchangenotice": "You must change your password to proceed.", "forcepasswordchangenotice": "You must change your password to proceed.",
"forgotten": "Forgotten your username or password?", "forgotaccount": "Lost password?",
"help": "Help", "help": "Help",
"instructions": "Instructions", "instructions": "Instructions",
"invalidaccount": "Please check your login details and try again.", "invalidaccount": "Please check your login details and try again.",

View File

@ -69,7 +69,7 @@
<!-- Forgotten password option. --> <!-- Forgotten password option. -->
<ion-button *ngIf="showForgottenPassword" expand="block" fill="clear" <ion-button *ngIf="showForgottenPassword" expand="block" fill="clear"
class="core-login-forgotten-password core-button-as-link ion-text-wrap" (click)="forgottenPassword()"> class="core-login-forgotten-password core-button-as-link ion-text-wrap" (click)="forgottenPassword()">
{{ 'core.login.forgotten' | translate }} {{ 'core.login.forgotaccount' | translate }}
</ion-button> </ion-button>
</form> </form>
@ -86,7 +86,7 @@
<core-login-methods *ngIf="siteConfig" [siteConfig]="siteConfig" [siteUrl]="siteUrl"></core-login-methods> <core-login-methods *ngIf="siteConfig" [siteConfig]="siteConfig" [siteUrl]="siteUrl"></core-login-methods>
</div> </div>
<div class="core-login-sign-up" *ngIf="canSignup || authInstructions"> <div class="core-login-sign-up" *ngIf="!isBrowserSSO && (canSignup || authInstructions)">
<h2>{{ 'core.login.firsttime' | translate }}</h2> <h2>{{ 'core.login.firsttime' | translate }}</h2>
<ion-item class="ion-text-wrap ion-no-padding core-login-instructions"> <ion-item class="ion-text-wrap ion-no-padding core-login-instructions">
@ -95,12 +95,12 @@
</ion-label> </ion-label>
</ion-item> </ion-item>
</div>
<ion-button *ngIf="canSignup" expand="block" class="ion-margin ion-text-wrap" fill="outline" (click)="openEmailSignup()"> <ion-button *ngIf="canSignup" expand="block" class="ion-margin ion-text-wrap" fill="outline" (click)="openEmailSignup()">
{{ 'core.login.startsignup' | translate }} {{ 'core.login.startsignup' | translate }}
</ion-button> </ion-button>
</div>
</ng-container> </ng-container>
<core-empty-box *ngIf="siteCheckError" icon="fas-circle-exclamation" [message]="siteCheckError"> <core-empty-box *ngIf="siteCheckError" icon="fas-circle-exclamation" [message]="siteCheckError">
<ion-button expand="block" (click)="checkSite()" fill="outline"> <ion-button expand="block" (click)="checkSite()" fill="outline">
{{ 'core.tryagain' | translate }} {{ 'core.tryagain' | translate }}

View File

@ -106,6 +106,10 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
await this.checkSite(); await this.checkSite();
if (this.isBrowserSSO && CoreLoginHelper.shouldSkipCredentialsScreenOnSSO()) {
this.openBrowserSSO();
}
if (CorePlatform.isIOS() && !this.isBrowserSSO) { if (CorePlatform.isIOS() && !this.isBrowserSSO) {
// Make iOS auto-fill work. The field that isn't focused doesn't get updated, do it manually. // Make iOS auto-fill work. The field that isn't focused doesn't get updated, do it manually.
// Debounce it to prevent triggering this function too often when the user is typing. // Debounce it to prevent triggering this function too often when the user is typing.

View File

@ -76,7 +76,7 @@
<!-- Forgotten password option. --> <!-- Forgotten password option. -->
<ion-button *ngIf="showForgottenPassword" expand="block" fill="clear" <ion-button *ngIf="showForgottenPassword" expand="block" fill="clear"
class="core-login-forgotten-password core-button-as-link ion-text-wrap" (click)="forgottenPassword()"> class="core-login-forgotten-password core-button-as-link ion-text-wrap" (click)="forgottenPassword()">
{{ 'core.login.forgotten' | translate }} {{ 'core.login.forgotaccount' | translate }}
</ion-button> </ion-button>
</form> </form>

View File

@ -26,7 +26,7 @@ import { CoreTextUtils } from '@services/utils/text';
import { CoreUrlParams, CoreUrlUtils } from '@services/utils/url'; import { CoreUrlParams, CoreUrlUtils } from '@services/utils/url';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { CoreSite, CoreSiteIdentityProvider, CoreSitePublicConfigResponse, CoreSiteQRCodeType } from '@classes/site'; import { CoreSite, CoreSiteIdentityProvider, CoreSitePublicConfigResponse, CoreSiteQRCodeType, TypeOfLogin } from '@classes/site';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreWSError } from '@classes/errors/wserror'; import { CoreWSError } from '@classes/errors/wserror';
import { DomSanitizer, makeSingleton, Translate } from '@singletons'; import { DomSanitizer, makeSingleton, Translate } from '@singletons';
@ -139,7 +139,7 @@ export class CoreLoginHelperProvider {
* Open a browser to perform SSO login. * Open a browser to perform SSO login.
* *
* @param siteUrl URL of the site where the SSO login will be performed. * @param siteUrl URL of the site where the SSO login will be performed.
* @param typeOfLogin CoreConstants.LOGIN_SSO_CODE or CoreConstants.LOGIN_SSO_INAPP_CODE. * @param typeOfLogin TypeOfLogin.BROWSER or TypeOfLogin.EMBEDDED.
* @param service The service to use. If not defined, core service will be used. * @param service The service to use. If not defined, core service will be used.
* @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used. * @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used.
* @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page. * @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page.
@ -148,7 +148,7 @@ export class CoreLoginHelperProvider {
*/ */
async confirmAndOpenBrowserForSSOLogin( async confirmAndOpenBrowserForSSOLogin(
siteUrl: string, siteUrl: string,
typeOfLogin: number, typeOfLogin: TypeOfLogin,
service?: string, service?: string,
launchUrl?: string, launchUrl?: string,
redirectData?: CoreRedirectPayload, redirectData?: CoreRedirectPayload,
@ -601,8 +601,8 @@ export class CoreLoginHelperProvider {
* @param code Code to check. * @param code Code to check.
* @returns True if embedded browser, false othwerise. * @returns True if embedded browser, false othwerise.
*/ */
isSSOEmbeddedBrowser(code: number): boolean { isSSOEmbeddedBrowser(code: TypeOfLogin): boolean {
return code == CoreConstants.LOGIN_SSO_INAPP_CODE; return code == TypeOfLogin.EMBEDDED;
} }
/** /**
@ -611,8 +611,8 @@ export class CoreLoginHelperProvider {
* @param code Code to check. * @param code Code to check.
* @returns True if SSO login is needed, false othwerise. * @returns True if SSO login is needed, false othwerise.
*/ */
isSSOLoginNeeded(code: number): boolean { isSSOLoginNeeded(code: TypeOfLogin): boolean {
return code == CoreConstants.LOGIN_SSO_CODE || code == CoreConstants.LOGIN_SSO_INAPP_CODE; return code == TypeOfLogin.BROWSER || code == TypeOfLogin.EMBEDDED;
} }
/** /**
@ -656,14 +656,14 @@ export class CoreLoginHelperProvider {
* Open a browser to perform SSO login. * Open a browser to perform SSO login.
* *
* @param siteUrl URL of the site where the SSO login will be performed. * @param siteUrl URL of the site where the SSO login will be performed.
* @param typeOfLogin CoreConstants.LOGIN_SSO_CODE or CoreConstants.LOGIN_SSO_INAPP_CODE. * @param typeOfLogin TypeOfLogin.BROWSER or TypeOfLogin.EMBEDDED.
* @param service The service to use. If not defined, core service will be used. * @param service The service to use. If not defined, core service will be used.
* @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used. * @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used.
* @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page. * @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page.
*/ */
openBrowserForSSOLogin( openBrowserForSSOLogin(
siteUrl: string, siteUrl: string,
typeOfLogin: number, typeOfLogin: TypeOfLogin,
service?: string, service?: string,
launchUrl?: string, launchUrl?: string,
redirectData?: CoreRedirectPayload, redirectData?: CoreRedirectPayload,
@ -901,13 +901,21 @@ export class CoreLoginHelperProvider {
/** /**
* Check if a confirm should be shown to open a SSO authentication. * Check if a confirm should be shown to open a SSO authentication.
* *
* @param typeOfLogin CoreConstants.LOGIN_SSO_CODE or CoreConstants.LOGIN_SSO_INAPP_CODE. * @param typeOfLogin TypeOfLogin.BROWSER or TypeOfLogin.EMBEDDED.
* @returns True if confirm modal should be shown, false otherwise. * @returns True if confirm modal should be shown, false otherwise.
* @deprecated since 4.3 Not used anymore. * @deprecated since 4.3 Not used anymore. See shouldSkipCredentialsScreenOnSSO.
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars shouldShowSSOConfirm(typeOfLogin: TypeOfLogin): boolean {
shouldShowSSOConfirm(typeOfLogin: number): boolean { return !this.isSSOEmbeddedBrowser(typeOfLogin) && !this.shouldSkipCredentialsScreenOnSSO();
return false; }
/**
* Check if we can skip credentials page.
*
* @returns If true, the browser should be opened without the user prompt.
*/
shouldSkipCredentialsScreenOnSSO(): boolean {
return String(CoreConstants.CONFIG.skipssoconfirmation) === 'true';
} }
/** /**

View File

@ -9,11 +9,11 @@ Feature: Test basic usage of login in app
Scenario: Forgot password Scenario: Forgot password
When I enter the app When I enter the app
And I press "Forgotten your username or password?" in the app And I press "Lost password?" in the app
And I set the field "Enter either username or email address" to "student1" And I set the field "Enter either username or email address" to "student1"
And I press "Search" in the app And I press "Search" in the app
Then I should find "Success" in the app Then I should find "Success" in the app
When I press "OK" in the app When I press "OK" in the app
And I press "Forgotten your username or password?" in the app And I press "Lost password?" in the app
Then I should find "Contact support" in the app Then I should find "Contact support" in the app

View File

@ -9,11 +9,11 @@ Feature: Test basic usage of login in app
Scenario: Forgot password Scenario: Forgot password
When I enter the app When I enter the app
And I press "Forgotten your username or password?" in the app And I press "Lost password?" in the app
And I set the field "Enter either username or email address" to "student1" And I set the field "Enter either username or email address" to "student1"
And I press "Search" in the app And I press "Search" in the app
Then I should find "Success" in the app Then I should find "Success" in the app
When I press "OK" in the app When I press "OK" in the app
And I press "Forgotten your username or password?" in the app And I press "Lost password?" in the app
Then I should not find "Contact support" in the app Then I should not find "Contact support" in the app

View File

@ -36,7 +36,7 @@ Feature: Test basic usage of login in app
When I set the following fields to these values in the app: When I set the following fields to these values in the app:
| Username | student1 | | Username | student1 |
| Password | student1 | | Password | student1 |
And I press "Log in" near "Forgotten your username or password?" in the app And I press "Log in" near "Lost password?" in the app
Then I should find "Acceptance test site" in the app Then I should find "Acceptance test site" in the app
And the UI should match the snapshot And the UI should match the snapshot
But I should not find "Log in" in the app But I should not find "Log in" in the app
@ -143,11 +143,11 @@ Feature: Test basic usage of login in app
Given the following config values are set as admin: Given the following config values are set as admin:
| supportavailability | 2 | | supportavailability | 2 |
When I enter the app When I enter the app
And I press "Forgotten your username or password?" in the app And I press "Lost password?" in the app
And I set the field "Enter either username or email address" to "student1" And I set the field "Enter either username or email address" to "student1"
And I press "Search" in the app And I press "Search" in the app
Then I should find "Success" in the app Then I should find "Success" in the app
When I press "OK" in the app When I press "OK" in the app
And I press "Forgotten your username or password?" in the app And I press "Lost password?" in the app
Then I should find "Contact support" in the app Then I should find "Contact support" in the app

View File

@ -62,14 +62,14 @@ Feature: Test signup in app
And I set the following fields to these values in the app: And I set the following fields to these values in the app:
| Username | u1 | | Username | u1 |
| Password | pu1 | | Password | pu1 |
And I press "Log in" near "Forgotten your username or password?" in the app And I press "Log in" near "Lost password?" in the app
Then I should find "You need to confirm your account" in the app Then I should find "You need to confirm your account" in the app
When I open a browser tab with url "$WWWROOT" When I open a browser tab with url "$WWWROOT"
And I confirm email for "u1" And I confirm email for "u1"
And I close the browser tab opened by the app And I close the browser tab opened by the app
And I press "Close" in the app And I press "Close" in the app
And I press "Log in" near "Forgotten your username or password?" in the app And I press "Log in" near "Lost password?" in the app
Then I should find "Acceptance test site" in the app Then I should find "Acceptance test site" in the app
But I should not find "You need to confirm your account" in the app But I should not find "You need to confirm your account" in the app
@ -173,7 +173,7 @@ Feature: Test signup in app
And I set the following fields to these values in the app: And I set the following fields to these values in the app:
| Username | u1 | | Username | u1 |
| Password | pu1 | | Password | pu1 |
And I press "Log in" near "Forgotten your username or password?" in the app And I press "Log in" near "Lost password?" in the app
And I press the user menu button in the app And I press the user menu button in the app
And I press "User Test" in the app And I press "User Test" in the app
Then I should find "No" near "Are you a developer?" in the app Then I should find "No" near "Are you a developer?" in the app

View File

@ -46,6 +46,7 @@ export interface EnvironmentConfig {
multisitesdisplay: CoreLoginSiteSelectorListMethod; multisitesdisplay: CoreLoginSiteSelectorListMethod;
sitefindersettings: Partial<CoreLoginSiteFinderSettings>; sitefindersettings: Partial<CoreLoginSiteFinderSettings>;
onlyallowlistedsites: boolean; onlyallowlistedsites: boolean;
skipssoconfirmation: boolean;
forcedefaultlanguage: boolean; forcedefaultlanguage: boolean;
privacypolicy: string; privacypolicy: string;
notificoncolor: string; notificoncolor: string;