TF-571 Check OIDC is available

This commit is contained in:
dab246
2022-05-20 12:38:31 +07:00
committed by Dat H. Pham
parent c5aa9f7d69
commit 239234f836
29 changed files with 625 additions and 55 deletions
BIN
View File
Binary file not shown.
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+90
View File
@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+2 -2
View File
@@ -77,7 +77,6 @@ export 'data/constants/constant.dart';
// Network // Network
export 'data/network/config/authorization_interceptors.dart'; export 'data/network/config/authorization_interceptors.dart';
export 'data/network/config/dynamic_url_interceptors.dart'; export 'data/network/config/dynamic_url_interceptors.dart';
export 'data/network/config/endpoint.dart';
export 'data/network/config/service_path.dart'; export 'data/network/config/service_path.dart';
export 'data/network/dio_client.dart'; export 'data/network/dio_client.dart';
export 'data/network/download/download_client.dart'; export 'data/network/download/download_client.dart';
@@ -95,4 +94,5 @@ export 'data/local/database_client.dart';
export 'data/local/database_manager.dart'; export 'data/local/database_manager.dart';
// Model // Model
export 'data/model/data_source_type.dart'; export 'data/model/source_type/data_source_type.dart';
export 'data/model/query/query_parameter.dart';
@@ -0,0 +1,19 @@
abstract class QueryParameter<T> {
final String queryName;
final T queryValue;
QueryParameter(this.queryName, this.queryValue);
}
class BooleanQueryParameter extends QueryParameter<bool> {
BooleanQueryParameter(String queryName, bool queryValue) : super(queryName, queryValue);
}
class StringQueryParameter extends QueryParameter<String> {
StringQueryParameter(String queryName, String queryValue) : super(queryName, queryValue);
}
class IntQueryParameter extends QueryParameter<int> {
IntQueryParameter(String queryName, int queryValue) : super(queryName, queryValue);
}
@@ -1,5 +0,0 @@
import 'package:core/data/network/config/service_path.dart';
class Endpoint {
static final ServicePath authentication = ServicePath('/api/login');
}
+1 -14
View File
@@ -1,19 +1,6 @@
class ServicePath { class ServicePath {
final String path; final String path;
ServicePath(this.path); ServicePath(this.path);
}
extension ServicePathExtension on ServicePath {
String generateEndpointPath() {
return '$path';
}
String generateAuthenticationUrl(Uri? baseUrl) {
if (baseUrl == null) {
return generateEndpointPath();
} else {
return baseUrl.origin + generateEndpointPath();
}
}
} }
@@ -5,13 +5,16 @@ extension URLExtension on String {
static final String prefixUrlHttp = 'http://'; static final String prefixUrlHttp = 'http://';
String formatURLValid() { String formatURLValid() {
if (startsWith(prefixUrlHttps)) { if (isNotEmpty) {
return this; if (startsWith(prefixUrlHttps)) {
} else if (startsWith(prefixUrlHttp)) { return this;
return kReleaseMode ? replaceAll(prefixUrlHttp, prefixUrlHttps) : this; } else if (startsWith(prefixUrlHttp)) {
} else { return kReleaseMode ? replaceAll(prefixUrlHttp, prefixUrlHttps) : this;
return '$prefixUrlHttps${this}'; } else {
return '$prefixUrlHttps${this}';
}
} }
return '';
} }
String removePrefix() { String removePrefix() {
@@ -0,0 +1,6 @@
import 'package:model/model.dart';
abstract class AuthenticationOIDCDataSource {
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest);
}
@@ -0,0 +1,20 @@
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart';
class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource {
final OIDCHttpClient _oidcHttpClient;
AuthenticationOIDCDataSourceImpl(this._oidcHttpClient);
@override
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
return Future.sync(() async {
final oidcResponse = await _oidcHttpClient.checkOIDCIsAvailable(oidcRequest);
return oidcResponse != null;
}).catchError((error) {
throw error;
});
}
}
@@ -0,0 +1,29 @@
import 'package:core/core.dart';
import 'package:tmail_ui_user/features/login/data/network/endpoint.dart';
extension ServicePathExtension on ServicePath {
String generateEndpointPath() {
return path;
}
ServicePath generateOIDCPath(Uri baseUrl) {
return ServicePath(baseUrl.origin + Endpoint.oidc + path);
}
ServicePath withQueryParameters(List<QueryParameter> queryParameters) {
if (queryParameters.isEmpty) {
return this;
}
return ServicePath('$path?${queryParameters
.map((query) => '${query.queryName}=${query.queryValue}').join('&')}');
}
ServicePath withPathParameter([String? pathParameter]) {
if (pathParameter == null || pathParameter.isEmpty) {
return this;
}
return ServicePath('$path/$pathParameter');
}
}
@@ -0,0 +1,7 @@
import 'package:core/data/network/config/service_path.dart';
class Endpoint {
static const String oidc = '/oidc';
static final ServicePath webFinger = ServicePath('/.well-known/webfinger');
}
@@ -0,0 +1,32 @@
import 'dart:convert';
import 'package:core/core.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/login/data/network/endpoint.dart';
import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart';
class OIDCHttpClient {
final DioClient _dioClient;
OIDCHttpClient(this._dioClient);
Future<OIDCResponse?> checkOIDCIsAvailable(OIDCRequest oidcRequest) async {
final result = await _dioClient.get(
Endpoint.webFinger
.generateOIDCPath(Uri.parse(oidcRequest.baseUrl))
.withQueryParameters([
StringQueryParameter('resource', oidcRequest.resourceUrl),
StringQueryParameter('rel', oidcRequest.relUrl),
])
.generateEndpointPath()
);
log('OIDCHttpClient::checkOIDCIsAvailable(): RESULT: $result');
if (result is Map<String, dynamic>) {
return OIDCResponse.fromJson(result);
} else {
return OIDCResponse.fromJson(jsonDecode(result));
}
}
}
@@ -0,0 +1,14 @@
import 'package:model/oidc/request/oidc_request.dart';
import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository {
final AuthenticationOIDCDataSource _oidcDataSource;
AuthenticationOIDCRepositoryImpl(this._oidcDataSource);
@override
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest) {
return _oidcDataSource.checkOIDCIsAvailable(oidcRequest);
}
}
@@ -0,0 +1,6 @@
import 'package:model/oidc/request/oidc_request.dart';
abstract class AuthenticationOIDCRepository {
Future<bool> checkOIDCIsAvailable(OIDCRequest oidcRequest);
}
@@ -0,0 +1,18 @@
import 'package:core/core.dart';
class CheckOIDCIsAvailableSuccess extends UIState {
CheckOIDCIsAvailableSuccess();
@override
List<Object> get props => [];
}
class CheckOIDCIsAvailableFailure extends FeatureFailure {
final dynamic exception;
CheckOIDCIsAvailableFailure(this.exception);
@override
List<Object> get props => [exception];
}
@@ -0,0 +1,24 @@
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart';
class CheckOIDCIsAvailableInteractor {
final AuthenticationOIDCRepository _oidcRepository;
CheckOIDCIsAvailableInteractor(this._oidcRepository);
Future<Either<Failure, Success>> execute(OIDCRequest oidcRequest) async {
try {
final result = await _oidcRepository.checkOIDCIsAvailable(oidcRequest);
log('CheckOIDCIsAvailableInteractor::execute(): result: $result');
return result
? Right<Failure, Success>(CheckOIDCIsAvailableSuccess())
: Left<Failure, Success>(CheckOIDCIsAvailableFailure(null));
} catch (e) {
log('CheckOIDCIsAvailableInteractor::execute(): ERROR: $e');
return Left<Failure, Success>(CheckOIDCIsAvailableFailure(e));
}
}
}
@@ -42,6 +42,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
(success) { (success) {
if (loginController.loginFormType.value == LoginFormType.credentialForm || kIsWeb) { if (loginController.loginFormType.value == LoginFormType.credentialForm || kIsWeb) {
return AppLocalizations.of(context).loginInputCredentialMessage; return AppLocalizations.of(context).loginInputCredentialMessage;
} else if (loginController.loginFormType.value == LoginFormType.ssoForm) {
return AppLocalizations.of(context).loginInputSSOMessage;
} }
return AppLocalizations.of(context).loginInputUrlMessage; return AppLocalizations.of(context).loginInputUrlMessage;
})) }))
@@ -3,33 +3,48 @@ import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart';
import 'package:tmail_ui_user/features/login/data/datasource/authentication_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_datasource.dart';
import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart';
import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication_datasource_impl.dart';
import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart';
import 'package:tmail_ui_user/features/login/data/repository/authentication_oidc_repository_impl.dart';
import 'package:tmail_ui_user/features/login/data/repository/authentication_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/authentication_repository_impl.dart';
import 'package:tmail_ui_user/features/login/data/repository/credential_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/credential_repository_impl.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart'; import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
class LoginBindings extends BaseBindings { class LoginBindings extends BaseBindings {
@override
void dependencies() {
Get.lazyPut(() => OIDCHttpClient(Get.find<DioClient>()));
super.dependencies();
}
@override @override
void bindingsController() { void bindingsController() {
Get.lazyPut(() => LoginController( Get.lazyPut(() => LoginController(
Get.find<AuthenticationInteractor>(), Get.find<AuthenticationInteractor>(),
Get.find<DynamicUrlInterceptors>(), Get.find<DynamicUrlInterceptors>(),
Get.find<AuthorizationInterceptors>(), Get.find<AuthorizationInterceptors>(),
Get.find<CheckOIDCIsAvailableInteractor>(),
)); ));
} }
@override @override
void bindingsDataSource() { void bindingsDataSource() {
Get.lazyPut<AuthenticationDataSource>(() => Get.find<AuthenticationDataSourceImpl>()); Get.lazyPut<AuthenticationDataSource>(() => Get.find<AuthenticationDataSourceImpl>());
Get.lazyPut<AuthenticationOIDCDataSource>(() => Get.find<AuthenticationOIDCDataSourceImpl>());
} }
@override @override
void bindingsDataSourceImpl() { void bindingsDataSourceImpl() {
Get.lazyPut(() => AuthenticationDataSourceImpl()); Get.lazyPut(() => AuthenticationDataSourceImpl());
Get.lazyPut(() => AuthenticationOIDCDataSourceImpl(Get.find<OIDCHttpClient>()));
} }
@override @override
@@ -38,17 +53,22 @@ class LoginBindings extends BaseBindings {
Get.find<AuthenticationRepository>(), Get.find<AuthenticationRepository>(),
Get.find<CredentialRepository>(), Get.find<CredentialRepository>(),
)); ));
Get.lazyPut(() => CheckOIDCIsAvailableInteractor(
Get.find<AuthenticationOIDCRepository>(),
));
} }
@override @override
void bindingsRepository() { void bindingsRepository() {
Get.lazyPut<CredentialRepository>(() => Get.find<CredentialRepositoryImpl>()); Get.lazyPut<CredentialRepository>(() => Get.find<CredentialRepositoryImpl>());
Get.lazyPut<AuthenticationRepository>(() => Get.find<AuthenticationRepositoryImpl>()); Get.lazyPut<AuthenticationRepository>(() => Get.find<AuthenticationRepositoryImpl>());
Get.lazyPut<AuthenticationOIDCRepository>(() => Get.find<AuthenticationOIDCRepositoryImpl>());
} }
@override @override
void bindingsRepositoryImpl() { void bindingsRepositoryImpl() {
Get.lazyPut(() => CredentialRepositoryImpl(Get.find<SharedPreferences>())); Get.lazyPut(() => CredentialRepositoryImpl(Get.find<SharedPreferences>()));
Get.lazyPut(() => AuthenticationRepositoryImpl(Get.find<AuthenticationDataSource>())); Get.lazyPut(() => AuthenticationRepositoryImpl(Get.find<AuthenticationDataSource>()));
Get.lazyPut(() => AuthenticationOIDCRepositoryImpl(Get.find<AuthenticationOIDCDataSource>()));
} }
} }
@@ -5,7 +5,9 @@ import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart';
import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart';
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart'; import 'package:tmail_ui_user/features/login/presentation/state/login_state.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart';
@@ -17,6 +19,7 @@ class LoginController extends GetxController {
final AuthenticationInteractor _authenticationInteractor; final AuthenticationInteractor _authenticationInteractor;
final DynamicUrlInterceptors _dynamicUrlInterceptors; final DynamicUrlInterceptors _dynamicUrlInterceptors;
final AuthorizationInterceptors _authorizationInterceptors; final AuthorizationInterceptors _authorizationInterceptors;
final CheckOIDCIsAvailableInteractor _checkOIDCIsAvailableInteractor;
final TextEditingController urlInputController = TextEditingController(); final TextEditingController urlInputController = TextEditingController();
@@ -24,6 +27,7 @@ class LoginController extends GetxController {
this._authenticationInteractor, this._authenticationInteractor,
this._dynamicUrlInterceptors, this._dynamicUrlInterceptors,
this._authorizationInterceptors, this._authorizationInterceptors,
this._checkOIDCIsAvailableInteractor,
); );
var loginState = LoginState(Right(LoginInitAction())).obs; var loginState = LoginState(Right(LoginInitAction())).obs;
@@ -33,20 +37,48 @@ class LoginController extends GetxController {
String? _userNameText; String? _userNameText;
String? _passwordText; String? _passwordText;
void setUrlText(String url) => _urlText = url.formatURLValid(); void setUrlText(String url) => _urlText = url.trim().formatURLValid();
void setUserNameText(String userName) => _userNameText = userName; void setUserNameText(String userName) => _userNameText = userName;
void setPasswordText(String password) => _passwordText = password; void setPasswordText(String password) => _passwordText = password;
Uri? _parseUri(String? url) => url != null ? Uri.parse(url.trim()) : null; Uri? _parseUri(String? url) => url != null && url.trim().isNotEmpty
? Uri.parse(url.trim())
: null;
UserName? _parseUserName(String? userName) => userName != null ? UserName(userName.trim()) : null; UserName? _parseUserName(String? userName) => userName != null && userName.trim().isNotEmpty
? UserName(userName.trim())
: null;
Password? _parsePassword(String? password) => password != null ? Password(password.trim()) : null; Password? _parsePassword(String? password) => password != null && password.trim().isNotEmpty
? Password(password.trim())
: null;
void handleNextInUrlInputFormPress() { void handleNextInUrlInputFormPress() {
completeInputUrlAction(); _checkOIDCIsAvailable();
}
void _checkOIDCIsAvailable() async {
final baseUri = BuildUtils.isWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
if (baseUri == null) {
loginState.value = LoginState(Left(LoginMissUrlAction()));
} else {
loginState.value = LoginState(Right(LoginLoadingAction()));
await _checkOIDCIsAvailableInteractor
.execute(OIDCRequest(baseUrl: baseUri.path))
.then((response) => response.fold(
(failure) => _showFormLoginWithCredentialAction(),
(success) => success is CheckOIDCIsAvailableSuccess
? _showFormLoginWithSSOAction(success)
: _showFormLoginWithCredentialAction()));
}
}
void _showFormLoginWithSSOAction(CheckOIDCIsAvailableSuccess success) {
loginState.value = LoginState(Right(success));
loginFormType.value = LoginFormType.ssoForm;
} }
void handleBackInCredentialForm() { void handleBackInCredentialForm() {
@@ -54,23 +86,27 @@ class LoginController extends GetxController {
loginFormType.value = LoginFormType.baseUrlForm; loginFormType.value = LoginFormType.baseUrlForm;
} }
void completeInputUrlAction() { void _showFormLoginWithCredentialAction() {
loginState.value = LoginState(Right(InputUrlCompletion())); loginState.value = LoginState(Right(InputUrlCompletion()));
loginFormType.value = LoginFormType.credentialForm; loginFormType.value = LoginFormType.credentialForm;
} }
void handleLoginPressed() { void handleLoginPressed() {
final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText); if (loginFormType.value == LoginFormType.ssoForm) {
final userName = _parseUserName(_userNameText);
final password = _parsePassword(_passwordText); } else {
if (baseUri != null && userName != null && password != null) { final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText);
_loginAction(baseUri, userName, password); final userName = _parseUserName(_userNameText);
} else if (baseUri == null) { final password = _parsePassword(_passwordText);
loginState.value = LoginState(Left(LoginMissUrlAction())); if (baseUri != null && userName != null && password != null) {
} else if (userName == null) { _loginAction(baseUri, userName, password);
loginState.value = LoginState(Left(LoginMissUsernameAction())); } else if (baseUri == null) {
} else if (password == null) { loginState.value = LoginState(Left(LoginMissUrlAction()));
loginState.value = LoginState(Left(LoginMissPasswordAction())); } else if (userName == null) {
loginState.value = LoginState(Left(LoginMissUsernameAction()));
} else if (password == null) {
loginState.value = LoginState(Left(LoginMissPasswordAction()));
}
} }
} }
@@ -1,3 +1,3 @@
enum LoginFormType { enum LoginFormType {
none, baseUrlForm, credentialForm none, baseUrlForm, credentialForm, ssoForm
} }
@@ -26,9 +26,11 @@ class LoginView extends BaseLoginView {
children: [ children: [
Center(child: SingleChildScrollView(child: _buildCenterForm(context), scrollDirection: Axis.vertical)), Center(child: SingleChildScrollView(child: _buildCenterForm(context), scrollDirection: Axis.vertical)),
Obx(() { Obx(() {
return loginController.loginFormType.value == LoginFormType.credentialForm if (loginController.loginFormType.value == LoginFormType.credentialForm
? _buildBackButton(context) || loginController.loginFormType.value == LoginFormType.ssoForm) {
: const SizedBox.shrink(); return _buildBackButton(context);
}
return const SizedBox.shrink();
}) })
] ]
) )
@@ -36,9 +38,11 @@ class LoginView extends BaseLoginView {
children: [ children: [
_buildCenterForm(context), _buildCenterForm(context),
Obx(() { Obx(() {
return loginController.loginFormType.value == LoginFormType.credentialForm if (loginController.loginFormType.value == LoginFormType.credentialForm
? _buildBackButton(context) || loginController.loginFormType.value == LoginFormType.ssoForm) {
: const SizedBox.shrink(); return _buildBackButton(context);
}
return const SizedBox.shrink();
}) })
] ]
), ),
@@ -78,15 +82,23 @@ class LoginView extends BaseLoginView {
Obx(() { Obx(() {
switch (controller.loginFormType.value) { switch (controller.loginFormType.value) {
case LoginFormType.baseUrlForm: case LoginFormType.baseUrlForm:
return _supportScrollForm(context) return Obx(() => loginController.loginState.value.viewState.fold(
? _buildNextButton(context) (failure) => _buildNextButtonInContext(context),
: _buildExpandedButton(context, _buildNextButton(context)); (success) => success is LoginLoadingAction
? _buildLoadingCircularProgress()
: _buildNextButtonInContext(context)));
case LoginFormType.credentialForm: case LoginFormType.credentialForm:
return Obx(() => loginController.loginState.value.viewState.fold( return Obx(() => loginController.loginState.value.viewState.fold(
(failure) => _buildLoginButtonInContext(context), (failure) => _buildLoginButtonInContext(context),
(success) => success is LoginLoadingAction (success) => success is LoginLoadingAction
? _buildLoadingCircularProgress() ? _buildLoadingCircularProgress()
: _buildLoginButtonInContext(context))); : _buildLoginButtonInContext(context)));
case LoginFormType.ssoForm:
return Obx(() => loginController.loginState.value.viewState.fold(
(failure) => _buildLoginButtonInContext(context),
(success) => success is LoginLoadingAction
? _buildLoadingCircularProgress()
: _buildLoginButtonInContext(context)));
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -166,6 +178,12 @@ class LoginView extends BaseLoginView {
); );
} }
Widget _buildNextButtonInContext(BuildContext context) {
return _supportScrollForm(context)
? _buildNextButton(context)
: _buildExpandedButton(context, _buildNextButton(context));
}
Widget _buildLoginButtonInContext(BuildContext context) { Widget _buildLoginButtonInContext(BuildContext context) {
return _supportScrollForm(context) return _supportScrollForm(context)
? buildLoginButton(context) ? buildLoginButton(context)
@@ -1387,4 +1387,10 @@ class AppLocalizations {
name: 'thisEmailAddressInvalid', name: 'thisEmailAddressInvalid',
); );
} }
String get loginInputSSOMessage {
return Intl.message(
'Sign-in with my SSO account',
name: 'loginInputSSOMessage');
}
} }
+6 -1
View File
@@ -71,4 +71,9 @@ export 'upload/upload_response.dart';
export 'autocomplete/auto_complete_pattern.dart'; export 'autocomplete/auto_complete_pattern.dart';
// Identity // Identity
export 'identity/identity_request_dto.dart'; export 'identity/identity_request_dto.dart';
// OIDC
export 'oidc/response/oidc_response.dart';
export 'oidc/response/oidc_link_dto.dart';
export 'oidc/request/oidc_request.dart';
@@ -0,0 +1,12 @@
import 'package:json_annotation/json_annotation.dart';
class UriConverter implements JsonConverter<Uri, String> {
const UriConverter();
@override
Uri fromJson(String json) => Uri.parse(json);
@override
String toJson(Uri uri) => uri.path;
}
+14
View File
@@ -0,0 +1,14 @@
import 'package:equatable/equatable.dart';
class OIDCRequest with EquatableMixin {
final String baseUrl;
final String resourceUrl = 'https://gateway.upn.integration-open-paas.org';
final String relUrl = 'http://openid.net/specs/connect/1.0/issuer';
OIDCRequest({required this.baseUrl});
@override
List<Object?> get props => [baseUrl, resourceUrl, relUrl];
}
@@ -0,0 +1,23 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:model/oidc/converter/uri_converter.dart';
part 'oidc_link_dto.g.dart';
@UriConverter()
@JsonSerializable()
class OIDCLinkDto with EquatableMixin {
final Uri rel;
final Uri href;
OIDCLinkDto(this.rel, this.href);
factory OIDCLinkDto.fromJson(Map<String, dynamic> json) => _$OIDCLinkDtoFromJson(json);
Map<String, dynamic> toJson() => _$OIDCLinkDtoToJson(this);
@override
List<Object?> get props => [rel, href];
}
@@ -0,0 +1,24 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:model/oidc/converter/uri_converter.dart';
import 'package:model/oidc/response/oidc_link_dto.dart';
part 'oidc_response.g.dart';
@UriConverter()
@JsonSerializable()
class OIDCResponse with EquatableMixin {
final String subject;
final List<OIDCLinkDto> links;
OIDCResponse(this.subject, this.links);
factory OIDCResponse.fromJson(Map<String, dynamic> json) => _$OIDCResponseFromJson(json);
Map<String, dynamic> toJson() => _$OIDCResponseToJson(this);
@override
List<Object?> get props => [subject, links];
}