Files
MConnectSDK/MConnectSDK/MConnectClient.cs
T
Samuele E. Locatelli d82b58246d fix metodi configure await
2020-11-04 10:56:42 +01:00

2067 lines
77 KiB
C#

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using YamlDotNet.RepresentationModel;
namespace MConnectSDK
{
/// <summary>
/// Client per connessione a MaestroConnect
/// <summary>
public class MConnectClient
{
#region Protected Fields
/// <summary>
/// Parametri di comunicazione attivi
/// <summary>
protected confParam _currParam;
/// <summary>
/// URL per test HEALT del cloud
/// <summary>
protected string AliveTarget = @"https://stg.api.maestroconnect.scmgroup.com/health";
/// <summary>
/// URL BASE per le chiamate
/// <summary>
protected string BaseUrl = @"https://stg.api.maestroconnect.scmgroup.com/";
/// <summary>
/// ID applicativo/client per piattaforma MaestroConnect (differente per UTE)
/// <summary>
protected string ClientID;
/// <summary>
/// Semaforo controllo CLOUD PRELIMINARE
/// </summary>
protected bool cloudOk = false;
/// <summary>
/// DataOra ultimo controllo stato server REDIS
/// <summary>
protected DateTime lastRedSrvCheck;
/// <summary>
/// TTL lungo in REDIS (10 min)
/// <summary>
protected int longTTL = 600;
/// <summary>
/// Oggetto accesso memoria...
/// <summary>
protected memLayer ML;
/// <summary>
/// Semaforo ping PRELIMINARE
/// </summary>
protected bool pingOk = false;
/// <summary>
/// URL BASE per le chiamate
/// <summary>
protected string PingTarget = @"stg.maestroconnect.scmgroup.com";
/// <summary>
/// URL BASE per le chiamate upload
/// <summary>
protected string UploadUrl = @"https://stg.maestroconnect.scmgroup.com/storage-api/backups";
/// <summary>
/// Intervallo di default x recall successive
/// <summary>
protected int waitRecall = 1000;
/// <summary>
/// URL x chiamata auth WebApp / QR-code da YAML...
/// <summary>
protected string WebAppUrl = @"https://smart.maestroconnect.com/deviceauth?user_code={0}&machineID={1}";
#endregion Protected Fields
#region Public Fields
/// <summary>
/// ID univoco dell'HMI per piattaforma MaestroConnect
/// <summary>
public string MachineID;
/// <summary>
/// Determina se il server redis sia attivo e connesso (check periodico...)
/// <summary>
public bool redServAlive;
#endregion Public Fields
#region Public Constructors
/// <summary>
/// Inizializzazione classe specificando il file di conf con i parametri
/// <summary>
public MConnectClient()
{
confParam param = new confParam();
ML = new memLayer(param);
}
#endregion Public Constructors
#region Protected Properties
/// <summary>
/// AccessToken
/// <summary>
protected string access_token
{
get
{
string answ = "";
answ = verifResp.access_token;
return answ;
}
}
/// <summary>
/// Risposta della chiamata di clientInfo
/// <summary>
protected clientInfoResponse clientInfoResp
{
get
{
clientInfoResponse answ = new clientInfoResponse();
try
{
answ = JsonConvert.DeserializeObject<clientInfoResponse>(ML.getRSV(ML.redHash("ClientInfoResponse")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("ClientInfoResponse"), serVal);
}
}
/// <summary>
/// Utente corrente (SE login OK)
/// <summary>
protected UserData currUser
{
get
{
UserData answ = new UserData();
try
{
answ = JsonConvert.DeserializeObject<UserData>(ML.getRSV(ML.redHash("CurrUser")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
// salvo MAX 2h
ML.setRSV(ML.redHash("CurrUser"), serVal, 60 * 60 * 2);
}
}
/// <summary>
/// DateTime di enroll (se futuro = NON enrolled)
/// <summary>
protected DateTime enrollDate
{
get
{
DateTime answ = DateTime.Now.AddYears(100);
try
{
answ = JsonConvert.DeserializeObject<DateTime>(ML.getRSV(ML.redHash("EnrollDate")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("EnrollDate"), serVal);
ML.forceDbDump();
}
}
/// <summary>
/// Ultima response controllo enroll
/// <summary>
protected enrollResponse enrollResp
{
get
{
enrollResponse answ = new enrollResponse();
try
{
answ = JsonConvert.DeserializeObject<enrollResponse>(ML.getRSV(ML.redHash("enrollResp")));
}
catch
{ }
return answ;
}
set
{
if (value != null)
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("enrollResp"), serVal);
}
}
}
/// <summary>
/// Dati Licenza (in cache locale)
/// <summary>
protected LicenseData LicenseDataCache
{
get
{
LicenseData answ = new LicenseData();
try
{
string rawResult = ML.getRSV(ML.redHash("LicenseDataCache"));
answ = JsonConvert.DeserializeObject<LicenseData>(rawResult);
}
catch
{ }
return answ;
}
set
{
string license = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("LicenseDataCache"), license);
ML.forceDbDump();
}
}
/// <summary>
/// URL pagina per check enroll (parametrico)
/// <summary>
protected string pageUrlCheckEnroll
{
get
{
string answ = "";
//string shortBaseUrl = BaseUrl.Replace("/api", "");
string shortBaseUrl = RemoveFromEnd(BaseUrl, "/api");
answ = $"{shortBaseUrl}/clients/{organizationCode}/machines/{MachineID}";
return answ;
}
}
/// <summary>
/// URL pagina per Download Immagini ( https://firebasestorage.googleapis.com/v0/b/scm-mconnect.appspot.com/o/ )
/// <summary>
protected string pageUrlImgDownload
{
get
{
return "https://firebasestorage.googleapis.com/v0/b/scm-mconnect.appspot.com/o/";
}
}
/// <summary>
/// RefreshToken
/// <summary>
protected string refresh_token
{
get
{
string answ = "";
answ = verifResp.refresh_token;
return answ;
}
}
/// <summary>
/// Stato della richiesta attuale tornato
/// <summary>
protected Result reqStatus
{
get
{
Result answ = new Result();
try
{
answ = JsonConvert.DeserializeObject<Result>(ML.getRSV(ML.redHash("RequestStatus")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("RequestStatus"), serVal, longTTL);
}
}
/// <summary>
/// Verifica se sia necessario rigenerare il token...
/// <summary>
protected bool tokenDone
{
get
{
bool answ = false;
if (redServAlive)
{
string currObj = ML.getRSV(ML.redHash("TokenResponse"));
answ = !string.IsNullOrEmpty(currObj);
}
return answ;
}
}
/// <summary>
/// Ultimo token response ottenuto (e salvato in REDIS)
/// <summary>
protected tokenResponse tokResp
{
get
{
tokenResponse answ = new tokenResponse();
try
{
answ = JsonConvert.DeserializeObject<tokenResponse>(ML.getRSV(ML.redHash("TokenResponse")));
}
catch
{ }
return answ;
}
set
{
if (value != null)
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("TokenResponse"), serVal, value.expires_in);
ML.forceDbDump();
}
}
}
/// <summary>
/// Elenco utenti (in cache locale)
/// <summary>
protected List<UserData> userListCache
{
get
{
List<UserData> answ = new List<UserData>();
try
{
string rawResult = ML.getRSV(ML.redHash("UserListCache"));
answ = JsonConvert.DeserializeObject<List<UserData>>(rawResult);
}
catch
{ }
// bonifico eventuali null...
answ.RemoveAll(item => item == null);
return answ;
}
set
{
value.RemoveAll(item => item == null);
value.Sort();
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("UserListCache"), serVal);
ML.forceDbDump();
}
}
/// <summary>
/// Elenco utenti (in cache locale)
/// <summary>
protected List<userPwdData> userPwdLocalCache
{
get
{
List<userPwdData> answ = new List<userPwdData>();
try
{
answ = JsonConvert.DeserializeObject<List<userPwdData>>(ML.getRSV(ML.redHash("UserPwdLocalCache")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("UserPwdLocalCache"), serVal);
ML.forceDbDump();
}
}
/// <summary>
/// Ultima response elenco utenti
/// <summary>
protected userListResponse usrListResp
{
get
{
userListResponse answ = new userListResponse();
try
{
answ = JsonConvert.DeserializeObject<userListResponse>(ML.getRSV(ML.redHash("usrListResp")));
}
catch
{ }
return answ;
}
set
{
if (value != null)
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("usrListResp"), serVal);
}
}
}
/// <summary>
/// Verifica se sia necessario rifare la fase di verifica x i token di auth...
/// <summary>
protected bool verificationDone
{
get
{
bool answ = true;
if (redServAlive)
{
string currObj = ML.getRSV(ML.redHash("VerificationResponse"));
answ = !string.IsNullOrEmpty(currObj);
}
return answ;
}
}
/// <summary>
/// Risposta della chiamata di verifica
/// <summary>
protected verificationSuccessResponse verifResp
{
get
{
verificationSuccessResponse answ = new verificationSuccessResponse();
try
{
answ = JsonConvert.DeserializeObject<verificationSuccessResponse>(ML.getRSV(ML.redHash("VerificationResponse")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("VerificationResponse"), serVal);
ML.forceDbDump();
}
}
/// <summary>
/// DateTime di watchdog
/// <summary>
protected DateTime watchDog
{
get
{
DateTime answ = DateTime.Now.AddDays(-1);
try
{
answ = JsonConvert.DeserializeObject<DateTime>(ML.getRSV(ML.redHash("WatchDog")));
}
catch
{ }
return answ;
}
set
{
string serVal = JsonConvert.SerializeObject(value);
ML.setRSV(ML.redHash("WatchDog"), serVal, longTTL);
}
}
#endregion Protected Properties
#region Public Properties
/// <summary>
/// Restituisce codice organizzazione
/// <summary>
public string organizationCode
{
get
{
string answ = "";
try
{
answ = clientInfoResp.organization_code;
}
catch
{ }
return answ;
}
}
/// <summary>
/// Metodo x ottenere update richiesta
/// <summary>
/// <returns>{ext}<returns>
public Result reqStatusUpd
{
get
{
// !!!TBD!!! Fare vera chaimata
return reqStatus;
}
}
#endregion Public Properties
#region Private Methods
/// <summary>
/// Fa un controllo sulla risposta, eventualmente rinnova il token di accesso restituendo SE HA FATTO refresh (quindi serve nuova chiamata)
/// <summary>
/// <param name="rawResult">{ext}<param>
private bool checkFixToken(string rawResult)
{
bool answ = false;
try
{
// verifico eventuali errori
var errResp = JsonConvert.DeserializeObject<errorResponse>(rawResult);
// se trovo errore aggiungo nella reqStatus...
if (errResp.error != "")
{
if (errResp.error == "Access Token not Valid")
{
// effettuo una chiamata al refresh e aggiorno!
refreshAuthToken();
answ = true;
}
}
}
catch
{ }
return answ;
}
/// <summary>
/// Controlla stato server alive
/// <summary>
private bool checkRedisAlive()
{
bool newStatus = ML.connRedis.IsConnected;
// controllo se cambia lo stato...
string message = "";
if (redServAlive != newStatus)
{
message = newStatus ? "Server REDIS ONLINE!" : "Server REDIS offline";
Log.Instance.Info(message);
redServAlive = newStatus;
if (newStatus)
{
setWatchdog();
}
}
return newStatus;
}
/// <summary>
/// Effettua lo step 1 di chiamata x ottenere il TOKEN iniziale
/// <summary>
private void doTokenStep()
{
// parametri chiamata
Uri callUri = new Uri(BaseUrl + "/oauth2/token");
// encapsulo clientID
var payload = "{\"client_id\": \"" + ClientID + "\"}";
HttpContent callCont = new StringContent(payload, Encoding.UTF8, "application/json");
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// effettuo call effettiva
var taskRes = Task.Run(() => Utils.postUriAsync(callUri, callCont));
taskRes.Wait();
// decodifico risposta come tokResp...
var resp = JsonConvert.DeserializeObject<tokenResponse>(taskRes.Result);
waitRecall = 1000 * resp.interval;
tokResp = resp;
}
}
/// <summary>
/// Effetuo step 3: /verification
/// <summary>
private bool doVerificationStep()
{
bool answ = false;
if (redServAlive)
{
// SOLO SE ho un device code...
if (tokResp != null && tokResp.device_code != "")
{
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// parametri chiamata
Uri callUri = new Uri(BaseUrl + "/oauth2/verification");
// !!! FARE fix hard coded !!!
var payload = "{\"client_id\": \"" + ClientID + "\", \"device_code\": \"" + tokResp.device_code + "\"}";
HttpContent callCont = new StringContent(payload, Encoding.UTF8, "application/json");
// effettuo call effettiva
var taskRes = Task.Run(() => Utils.postUriAsync(callUri, callCont));
taskRes.Wait();
// leggo la rispsota in var temporanea...
var resp = JsonConvert.DeserializeObject<tokenResponse>(taskRes.Result);
// verifico se esista un errore come risposta...
if (resp != null)
{
// se è stato autorizzato...
if (resp.error == "")
{
var authResp = JsonConvert.DeserializeObject<verificationSuccessResponse>(taskRes.Result);
verifResp = authResp;
// blocco richiesta verifica
var currReq = reqStatus;
currReq.IsAuth = true;
answ = true;
reqStatus = currReq;
}
else
{
// resp.error == "slow_down"
// resp.error == "authorization_pending"
Thread.Sleep(waitRecall);
}
}
}
}
}
return answ;
}
/// <summary>
/// Ottiene i dati licenza da MaestroConnect
/// <summary>
/// <returns>Dati licenza<returns>
private LicenseData GetLicenseMConnect()
{
LicenseData result = null;
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
//controllo con la chiamata della clientInfo...
var taskCliInfo = Task.Run(() => GetClientStatusAsync());
taskCliInfo.Wait();
// parametri chiamata
string shortBaseUrl = BaseUrl.Replace(".com/api", ".com/clients");
string pageUrl = $"{shortBaseUrl}/{organizationCode}/machines/{MachineID}/license";
// effettuo call
Console.WriteLine("-> " + verifResp.access_token);
var taskRes = Task.Run(() => Utils.getPageAsync(pageUrl, verifResp.access_token));
taskRes.Wait();
try
{
var resp = JsonConvert.DeserializeObject<LicenseResponse>(taskRes.Result);
result = resp.result;
}
catch
{
result = null;
}
finally
{
Console.WriteLine(taskRes.Result.ToString());
}
}
return result;
}
/// <summary>
/// Parsing file di configurazione
/// <summary>
/// <param name="confFilePath">{ext}<param>
private void parseConfFile(string confFilePath)
{
confParam param = new confParam();
// deserializzazione brutale...
try
{
// Load the stream
using (var reader = new StreamReader(confFilePath))
{
// Load the stream
var yaml = new YamlStream();
yaml.Load(reader);
// Examine the stream
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
var config = mapping.Children["conf"];
// imposto val da file YAML...
MachineID = config["MachineID"].ToString();
ClientID = config["Client_ID"].ToString();
// URL base x le chiamate REALI
BaseUrl = config["BaseUrl"].ToString();
WebAppUrl = config["WebAppUrl"].ToString();
UploadUrl = config["UploadUrl"].ToString();
PingTarget = config["PingTarget"].ToString();
AliveTarget = config["AliveTarget"].ToString();
var redisConf = config["MemoryLayer"]["Redis"];
int currDb = 0;
int.TryParse(redisConf["DbIndex"].ToString(), out currDb);
string baseConf = redisConf["ConnectionString"].ToString();
string _module = redisConf["Module"].ToString();
param = new confParam
{
redisConn = baseConf,
redisConnAdmin = baseConf + ",allowAdmin=true",
module = _module,
defaultDb = currDb
};
}
}
catch
{
throw;
}
// registro parametri...
_currParam = param;
ML = new memLayer(_currParam);
}
/// <summary>
/// Effettua test preliminare stato...
/// <summary>
/// <returns>{ext}<returns>
private Result preCheckStatus()
{
Result answ = new Result()
{
// init in stato ND...
IsHmiEnrolled = false,
CloudStatusOk = false,
LocalStatusOk = false,
Source = SourceType.OFFLINE,
CallResultOk = false
};
// verifico REDIS in primis
answ.LocalStatusOk = checkRedisAlive();
if (answ.LocalStatusOk)
{
// cerco i TOKEN in REDIS...
answ.IsAuth = !string.IsNullOrEmpty(refresh_token);
answ.Source = SourceType.LOCAL;
}
// ORA verifica STATO sistema A VALLE...
if (checkConnStatus())
{
answ.CloudStatusOk = true;
answ.Source = SourceType.CLOUD;
}
answ.CallResultOk = (answ.CloudStatusOk || answ.LocalStatusOk);
// x capire SE sia enrolled --> faccio chiamata di verifica...
answ.IsHmiEnrolled = checkEnroll();
// rispondo!
return answ;
}
/// <summary>
/// Effettua una chaimata alla funzione di refresh del token di comunicazione
/// <summary>
private void refreshAuthToken()
{
// parametri chiamata
Uri callUri = new Uri(BaseUrl + "/oauth2/refresh");
var payload = "{\"refresh_token\": \"" + verifResp.refresh_token + "\"}";
HttpContent callCont = new StringContent(payload, Encoding.UTF8, "application/json");
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// effettuo call effettiva
var taskRes = Task.Run(() => Utils.postUriAsync(callUri, callCont));
taskRes.Wait();
// decodifico risposta come authResp...
var authResp = JsonConvert.DeserializeObject<verificationSuccessResponse>(taskRes.Result);
// se OK salvo e sovrascrivo...
verifResp = authResp;
}
}
/// <summary>
/// Aggiorna stato redis server + invio dati pending
/// <summary>
private void setWatchdog()
{
// verifico eventuali invii pending... SE connesso...
if (redServAlive)
{
// salvo in REDIS che sto trasmettendo...
watchDog = DateTime.Now;
}
}
/// <summary>
/// Aggiorna i dati licenza dal cloud
///
/// <summary>
private void UpdateLicenseDataCacheFromCloud()
{
try
{
// se NON modalità testing LEGGO davvero da cloud i nuovi dati
LicenseData answ = GetLicenseMConnect();
if (answ != null)
{
// salvo in cache locale (comunque...)
LicenseDataCache = answ;
}
}
catch
{ }
}
/// <summary>
/// Effettua update userList
/// <param name="getImages">Indica se scaricare ANCHE TUTTE le immagini degli utenti (default = false){ext}<param>
/// <summary>
private void updateUserListCacheFromCloud(bool getImages = false)
{
try
{
// se NON modalità testing LEGGO davvero da cloud la NUOVA lista
List<UserData> answ = userListMConnect(getImages);
if (answ != null && answ.Count > 0)
{
// salvo in cache locale (comunque...)
userListCache = answ;
}
}
catch
{ }
}
/// <summary>
/// Elenco utenti da MaestroConnect
/// <summary>
/// <param name="getImages">Indica se scaricare ANCHE TUTTE le immagini degli utenti (default = false){ext}<param>
/// <returns>{ext}<returns>
private List<UserData> userListMConnect(bool getImages = false)
{
// continuo da remoto
List<UserData> answ = new List<UserData>();
List<UserData> resFilt = new List<UserData>();
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// salvo VECCHIO elenco utenti...
List<UserData> userListOld = userListCache;
// gestisco numero utenti precedentemente importati... e trovati nella nuova call da cloud
var elImported = userListOld.FindAll(X => X.LoginStatus == UserStatus.IMPORTED);
int numImp = elImported.Count;
int numFound = 0;
// controllo con la chiamata della clientInfo...
var taskCliInfo = Task.Run(() => GetClientStatusAsync());
taskCliInfo.Wait();
// parametri chiamata...SENZA /api...
string shortBaseUrl = BaseUrl.Replace("/api", "");
string pageUrl = $"{shortBaseUrl}/clients/{organizationCode}/users?skip=0&limit=5";
// effettuo call PRELIMINARE
var taskRes = Task.Run(() => Utils.getPageAsync(pageUrl, verifResp.access_token));
taskRes.Wait();
var rawResult = taskRes.Result;
// leggo la risposta in var temporanea...
var resp = JsonConvert.DeserializeObject<userListResponse>(rawResult);
// ora che so quanti utenti ho --> chiedo TUTTI!
pageUrl = $"{shortBaseUrl}/clients/{organizationCode}/users?skip=0&limit={resp.total}";
// effettuo call EFFETTIVA
taskRes = Task.Run(() => Utils.getPageAsync(pageUrl, verifResp.access_token));
taskRes.Wait();
rawResult = taskRes.Result;
// leggo la risposta in var temporanea...
resp = JsonConvert.DeserializeObject<userListResponse>(rawResult);
// salvo in cache REDIS
usrListResp = resp;
UserData _newUser;
Image userImg = null;
UserStatus currStatus = UserStatus.NOT_IMPORTED;
// recupero effettivi dati utente e restituisco...
foreach (var item in resp.result)
{
// verifica SE FOSSE GIA' esistente --> tengo STATUS...
resFilt = userListOld.FindAll(X => X.Username == item.Username);
if (resFilt.Count > 0)
{
currStatus = resFilt[0].LoginStatus;
if (currStatus == UserStatus.IMPORTED)
{
numFound++;
}
}
else
{
currStatus = UserStatus.NOT_IMPORTED;
}
// creo utente
_newUser = new UserData
{
UserId = item.Id,
IsAdmin = item.IsAdmin,
Username = item.Username,
Email = item.Email,
Cognome = item.Lastname,
Nome = item.Firstname,
LoginStatus = currStatus,
UserImage = userImg
};
answ.Add(_newUser);
}
// se ne avessi ricevuti (da cloud) meno di quanti ne avevo --> cerco DELETED...
if (numFound != numImp)
{
// cerco nei "vecchi" imported chi sia stato eliminato...
foreach (var item in elImported)
{
resFilt = answ.FindAll(X => X.Username == item.Username);
if (resFilt.Count == 0)
{
// creo utente
_newUser = new UserData
{
UserId = item.UserId,
IsAdmin = item.IsAdmin,
Username = item.Username,
Email = item.Email,
Cognome = item.Cognome,
Nome = item.Nome,
LoginStatus = UserStatus.DELETED,
UserImage = userImg
};
answ.Add(_newUser);
}
}
}
// se richiesto aggiungo immagine...
if (getImages)
{
// in modalità parallela TENTO scaricamento imagini...
Parallel.ForEach(resp.result, item =>
{
userImg = null;
try
{
// creo obj userData... SE HO immagine...
if (item.Image_Url != "")
{
var imgRes = Task.Run(() => Utils.getImageAsync(pageUrlImgDownload, item.Image_Url));
imgRes.Wait();
if (imgRes.Result != null)
{
userImg = imgRes.Result;
}
}
}
catch
{
}
resFilt = answ.FindAll(X => X.Username == item.Username);
if (resFilt.Count == 1)
{
resFilt[0].UserImage = userImg;
}
});
}
}
return answ;
}
#endregion Private Methods
#region Protected Methods
/// <summary>
/// Verifica stato connessione (ping + cloudOK)
/// </summary>
/// <returns></returns>
protected bool checkConnStatus()
{
// STEP 1: PING a server (locale/remoto)
pingOk = false;
cloudOk = false;
if (PingTarget != "")
{
pingOk = Utils.pingAddress(PingTarget);
}
// STEP 2: test HEALTH page...
if (pingOk)
{
// verifica chiamata pagina HEALT con reply = "HELLO" | https://stg.api.maestroconnect.scmgroup.com/health
var pageRes = Utils.getPageAsync(AliveTarget);
pageRes.Wait();
// 2019.09.26 versione x evitare deadlock sezionatura, by F.Sodano
//var pageRes = Task.Run(async () => await Utils.getPageAsync(AliveTarget));
if (pageRes.Result.IndexOf("HELLO") >= 0)
{
cloudOk = true;
}
else
{
var _cReqStatus = reqStatus;
_cReqStatus.CloudStatusOk = false;
_cReqStatus.Source = SourceType.LOCAL;
reqStatus = _cReqStatus;
}
}
else
{
var _cReqStatus = reqStatus;
_cReqStatus.CloudStatusOk = false;
_cReqStatus.Source = SourceType.OFFLINE;
reqStatus = _cReqStatus;
}
return pingOk && cloudOk;
}
/// <summary>
/// Effettua verifica enroll (chiamando organization + machineID)
/// <summary>
protected bool checkEnroll()
{
bool answ = false;
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// controllo con la chiamata della clientInfo...
var taskCliInfo = Task.Run(() => GetClientStatusAsync());
taskCliInfo.Wait();
// effettuo lettura verifica...
var taskRes = Task.Run(() => Utils.getPageAsync(pageUrlCheckEnroll, verifResp.access_token));
taskRes.Wait();
var rawResult = taskRes.Result;
// leggo la risposta...
var resp = JsonConvert.DeserializeObject<enrollResponse>(taskRes.Result);
if (resp != null)
{
enrollResp = resp;
var currStatus = reqStatus;
if (resp.statusCode != 404)
{
if (resp.result != null && (resp.result.OrganizationCode.ToUpper() == organizationCode.ToUpper()))
{
// registro che è OK x AUTH
currStatus.IsHmiEnrolled = true;
answ = true;
reqStatus = currStatus;
}
}
else
{
Log.Instance.Error($"Errore in fase di checkEnroll: {Environment.NewLine}{taskRes.Result}");
}
}
}
return answ;
}
/// <summary>
/// Generazione HASH password salted
/// <summary>
/// <param name="_plainText">{ext}<param>
/// <param name="_salt">{ext}<param>
/// <returns>{ext}<returns>
protected string GenerateSaltedHash(string _plainText, string _salt)
{
byte[] plainText = Encoding.ASCII.GetBytes(_plainText);
byte[] salt = Encoding.ASCII.GetBytes(_salt);
HashAlgorithm algorithm = new SHA256Managed();
byte[] plainTextWithSaltBytes =
new byte[plainText.Length + salt.Length];
for (int i = 0; i < plainText.Length; i++)
{
plainTextWithSaltBytes[i] = plainText[i];
}
for (int i = 0; i < salt.Length; i++)
{
plainTextWithSaltBytes[plainText.Length + i] = salt[i];
}
string answ = ASCIIEncoding.ASCII.GetString(algorithm.ComputeHash(plainTextWithSaltBytes));
return answ;
}
#endregion Protected Methods
#region Public Methods
/// <summary>
/// Effettua vera chiamata enroll
/// <summary>
/// <param name="pageUrl">{ext}<param>
/// <returns>{ext}<returns>
public string doEnroll()
{
string answ = "";
// effettuo call DI ENROLL
Uri callUri = new Uri(pageUrlCheckEnroll);
var payload = "";
HttpContent callCont = new StringContent(payload, Encoding.UTF8, "application/json");
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
var taskRes = Task.Run(() => Utils.putAsync(callUri, callCont, verifResp.access_token));
taskRes.Wait();
answ = taskRes.Result;
}
return answ;
}
/// <summary>
/// Task x AUTH
/// <summary>
/// <param name="token">{ext}<param>
/// <param name="progress">{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> EnrollMachineAsync(CancellationToken token, IProgress<Result> progress)
{
// predispongo il payload da allegare alla result...
ActivationPayload authData = new ActivationPayload();
StringBuilder sb = new StringBuilder();
bool processRequest = true;
var _currReq = reqStatus;
// fino a quanod NON è auth --> cicla...
while (!_currReq.IsHmiEnrolled && processRequest)
{
await Task.Run(() =>
{
// SOLOS E precedentemente autorizzato...
if (_currReq.IsAuth)
{
authData = tryEnroll();
// recupero nuova reqStatus...
_currReq = reqStatusUpd;
// verifico SE sia stato registrato...
if (_currReq.IsHmiEnrolled || authData.IsEnrolled)
{
sb.AppendLine("");
sb.AppendLine(string.Format("{0:HH.mm.ss.fff} | ENROLL DONE", DateTime.Now));
authData.Message += sb.ToString();
processRequest = false;
_currReq.IsAuth = true;
_currReq.IsHmiEnrolled = true;
}
// verifico se richiesta cancellazione... CHIUDO!
if (token.IsCancellationRequested)
{
// riporto richiesta cancellazione... ed esco
authData = new ActivationPayload();
processRequest = false;
}
// aggiorno il payload...
_currReq.Payload = authData;
reqStatus = _currReq;
// riporto progress...
if (progress != null)
{
progress.Report(_currReq);
}
}
// attendo...
Thread.Sleep(500);
}).ConfigureAwait(false);
}
// return finale dello status della chiamata...
return _currReq;
}
/// <summary>
/// Forza svuotamento di TUTTI i dati in REDIS
/// <summary>
public void forceRedisFlush()
{
// svuoto TUTTI i dati x INIT...
ML.redFlushKey(_currParam.module + "*", _currParam.defaultDb);
}
/// <summary>
/// Recupera status del client
/// <summary>
/// <returns><returns>
public async Task<Result> GetClientStatusAsync()
{
checkConnStatus();
var currReq = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
string rawResult = "";
bool tokenRefreshed = false;
// verifico il token della risposta...
if (verifResp != null)
{
// se ho il TOKEN di accesso procedo
if (verifResp.access_token != "")
{
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// provo a chiamare clientInfo...
string pageUrl = $"{BaseUrl}/oauth2/clientinfo?access_token={verifResp.access_token}";
var taskRes = Task.Run(() => Utils.getPageAsync(pageUrl));
taskRes.Wait();
rawResult = taskRes.Result;
tokenRefreshed = checkFixToken(rawResult);
if (tokenRefreshed)
{
taskRes = Task.Run(() => Utils.getPageAsync(pageUrl));
taskRes.Wait();
rawResult = taskRes.Result;
tokenRefreshed = checkFixToken(rawResult);
currReq.CloudStatusOk = true;
}
// ORA se NON HA + fatto refresh --> procedo!
if (!tokenRefreshed)
{
// salvo il client info...
var cliResp = JsonConvert.DeserializeObject<clientInfoResponse>(taskRes.Result);
if (cliResp != null)
{
clientInfoResp = cliResp;
currReq.CloudStatusOk = true;
}
}
}
}
reqStatus = currReq;
}
}).ConfigureAwait(false);
return currReq;
}
/// <summary>
/// Recupera status del client
/// <summary>
/// <param name="token">Token, che può benissimo essere CancellationToken.None{ext}<param>
/// <param name="progress">{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> GetClientStatusAsync(CancellationToken token, IProgress<Result> progress = null)
{
await Task.Run(() =>
{
// iniziod a watchdog...
setWatchdog();
// faccio VERA chiamata x ClientInfo
checkConnStatus();
// verifico il token della risposta...
if (verifResp != null)
{
// se ho il TOKEN di accesso procedo
if (verifResp.access_token != "")
{
// controllo preliminare ping/cloud..
if (pingOk && cloudOk)
{
// provo a chiamare clientInfo...
string pageUrl = $"{BaseUrl}/oauth2/clientinfo?access_token={verifResp.access_token}";
var taskRes = Task.Run(() => Utils.getPageAsync(pageUrl));
taskRes.Wait();
string outVal = taskRes.Result;
}
}
}
}).ConfigureAwait(false);
return reqStatus;
}
/// <summary>
/// Recupera i dati licenza per la macchina corrente
/// </summary>
/// <returns>{ext}<returns>
public async Task<Result> GetLicenseAsync()
{
var _currStatus = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
var license = new LicensePayload();
UpdateLicenseDataCacheFromCloud();
LicenseData answ = LicenseDataCache;
// aggiungo nella risposta...
license.Data = answ;
if (answ != null && string.IsNullOrWhiteSpace(answ.error) && answ.currentPeriod != null)
{
string isActive = answ.IsActive ? "Active" : "Inactive";
license.Message = $"License {isActive}! \nStart date: {answ.currentPeriod.StartDate} \nEndDate: {answ.currentPeriod.EndDate}.";
}
else license.Message = $"License Not Found!";
_currStatus.Payload = license;
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
/// <summary>
/// Recupera elenco user di Maestro connect registrati come ELIMINATI (erano importati ma NON SONO PIU' su MConnect)
/// <summary>
/// <returns>{ext}<returns>
public async Task<Result> getUserdeletedListAsync()
{
var _currStatus = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
var userList = new UserPayload();
List<UserData> answ = userListCache;
// filtro SOLO per stato (improted)
var resFilt = answ.FindAll(X => X.LoginStatus == UserStatus.DELETED);
if (resFilt.Count > 0)
{
answ = resFilt;
}
else
{
answ = new List<UserData>();
}
// aggiungo nella risposta...
userList.UserList = answ;
userList.Message = $"{answ.Count} Users DELETED Found!";
_currStatus.Payload = userList;
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
/// <summary>
/// Recupera elenco user di Maestro connect registrati come CREATI sul client/HMI x organizzazione corrente
/// <summary>
/// <returns>{ext}<returns>
public async Task<Result> getUserImportedListAsync()
{
var _currStatus = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
var userList = new UserPayload();
List<UserData> answ = userListCache;
// filtro SOLO per stato (improted)
var resFilt = answ.FindAll(X => X.LoginStatus == UserStatus.IMPORTED);
if (resFilt.Count > 0)
{
answ = resFilt;
}
else
{
answ = new List<UserData>();
}
// aggiungo nella risposta...
userList.UserList = answ;
userList.Message = $"{answ.Count} Users Imported Found!";
_currStatus.Payload = userList;
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
/// <summary>
/// Recupera elenco user x organizzazione corrente
/// <summary>
/// <param name="getImages">Indica se scaricare ANCHE TUTTE le immagini degli utenti (default = false){ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> getUserListAsync(bool getImages = false)
{
var _currStatus = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
var userList = new UserPayload();
updateUserListCacheFromCloud(getImages);
List<UserData> answ = userListCache;
// aggiungo nella risposta...
userList.UserList = answ;
userList.Message = $"{answ.Count} Users Found!";
_currStatus.Payload = userList;
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
/// <summary>
/// Metodo di INIT della classe di comunicazione
/// <summary>
/// <param name="confFilePath">{ext}<param>
/// <param name="token">Token, che può benissimo essere CancellationToken.None{ext}<param>
/// <param name="progress">{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> InitSDKAsync(string confFilePath, CancellationToken token, IProgress<Result> progress = null)
{
if (confFilePath != "")
{
// controlloare file di conf se valido... = "mconnect.conf.yaml"
parseConfFile(confFilePath);
Result _currStatus = new Result();
await Task.Run(() =>
{
// inizio con invio watchdog...
setWatchdog();
// init output
_currStatus = preCheckStatus();
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
else
{
throw new ArgumentException(confFilePath, "File yaml (confFilePath) deve essere valorizzato");
}
}
public string RemoveFromEnd(string fullString, string suffix)
{
string answ = fullString;
if (fullString.EndsWith(suffix))
{
answ = fullString.Substring(0, fullString.Length - suffix.Length);
}
return answ;
}
/// <summary>
/// Imposta UN SINGOLO utente come importato (<see langword="true"/>) o eliminato(false)
/// <summary>
/// <param name="sender">CHI invia la richeista (x decidere come impostare status), HMI = LOCAL{ext}<param>
/// <param name="username">{ext}<param>
/// <param name="imported">true=importato / false = cancellato/non + importato{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> setUserImported(SourceType sender, string username, bool imported = true)
{
var _currStatus = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
var userList = new UserPayload();
List<UserData> answ = userListCache;
// CERCO utente x settare importato...
if (answ.Exists(x => x.Username == username))
{
// cerco ed aggiorno
var obj = answ.FirstOrDefault(x => x.Username == username);
if (obj != null)
{
// se mi chiede imported --> setto sempre...
if (imported)
{
obj.LoginStatus = UserStatus.IMPORTED;
}
// altrimenti controllo
else
{
// se chiede HMI/LOCAL --> NOT_IMPORTED
if (sender == SourceType.LOCAL)
{
obj.LoginStatus = UserStatus.NOT_IMPORTED;
}
else
{
// SE CHIEDE CLOUD e NON E' importato --> ELIMINO
if (obj.LoginStatus != UserStatus.IMPORTED)
{
// elimino ANCHE da cache pwd (se c'era)
if (userPwdLocalCache.Exists(x => x.Username == obj.Username))
{
var tmpData = userPwdLocalCache;
tmpData.RemoveAll(x => x.Username == obj.Username);
userPwdLocalCache = tmpData;
}
// elimino...
answ.Remove(obj);
}
// SE CHIEDE CLOUD ed E' importato --> imposto DELETED
else
{
obj.LoginStatus = UserStatus.DELETED;
}
}
}
//obj.LoginStatus = imported ? UserStatus.IMPORTED : UserStatus.DELETED;
}
// salvo
userListCache = answ;
}
// aggiungo nella risposta...
userList.UserList = answ;
userList.Message = "User Updated!";
_currStatus.Payload = userList;
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
/// <summary>
/// Task x AUTH
/// <summary>
/// <param name="token">{ext}<param>
/// <param name="progress">{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> TryActivationAsync(CancellationToken token, IProgress<Result> progress)
{
// predispongo il payload da allegare alla result...
ActivationPayload authData = new ActivationPayload();
StringBuilder sb = new StringBuilder();
bool processRequest = true;
var _currReq = reqStatus;
// fino a quanod NON è auth --> cicla...
while (!reqStatus.IsAuth && processRequest)
{
await Task.Run(() =>
{
authData = tryAuthorize();
// aggiorno che ho finito...
sb = new StringBuilder();
sb.AppendLine(string.Format("{0:HH.mm.ss.fff} >> keep trying", DateTime.Now));
sb.AppendLine("--------------------------------------------------");
authData.Message += sb.ToString();
// verifico SE sia stato x autorizzato...
if (reqStatus.IsAuth || authData.HasAuth)
{
sb.AppendLine("");
sb.AppendLine("SUCCESS 200: AUTH RECEIVED!");
authData.Message += sb.ToString();
processRequest = false;
}
// verifico se richiesta cancellazione... CHIUDO!
if (token.IsCancellationRequested)
{
// riporto richiesta cancellazione... ed esco
authData = new ActivationPayload();
processRequest = false;
}
_currReq = reqStatus;
// aggiorno il payload...
_currReq.Payload = authData;
reqStatus = _currReq;
// riporto progress...
if (progress != null)
{
progress.Report(_currReq);
}
// mi fermo...
Thread.Sleep(500);
}).ConfigureAwait(false);
}
// return finale dello status della chiamata...
return _currReq;
}
/// <summary>
/// Tentativo AUTH
/// <summary>
/// <returns>{ext}<returns>
public ActivationPayload tryAuthorize()
{
// init output
ActivationPayload answ = new ActivationPayload();
StringBuilder sb = new StringBuilder();
bool hasAuth = false;
// verifico autorizzazione... se non c'è TENTO auth...
if (!reqStatus.IsAuth)
{
// attesa come richiesto da call precedente...
Thread.Sleep(tokResp.interval);
// verifico SE HO il valore in REDIS del primo token (quindi passo a /verification) oppure devo rifare step 1 (/token)...
if (redServAlive)
{
setWatchdog();
// verifico SE ho un token valido...
if (!tokenDone)
{
doTokenStep();
}
else
{
hasAuth = doVerificationStep();
}
}
// riporto in risposta i dati...
answ.DeviceCode = tokResp.device_code;
answ.UserCode = tokResp.user_code;
string userCode = (tokResp != null && tokResp.user_code != null) ? tokResp.user_code : "";
answ.QrCode = string.Format(WebAppUrl, userCode, MachineID);
sb.AppendLine("--------------------------------------------------");
sb.AppendLine(string.Format("{0:HH.mm.ss.fff} | Try OAuth process", DateTime.Now));
sb.AppendLine("--------------------------------------------------");
answ.Message = sb.ToString();
// controllo SE abbia avuto auth...
answ.HasAuth = hasAuth;
}
// ritorno!
return answ;
}
/// <summary>
/// Tentativo ENROLL
/// <summary>
/// <returns>{ext}<returns>
public ActivationPayload tryEnroll()
{
// init output
ActivationPayload answ = new ActivationPayload();
StringBuilder sb = new StringBuilder();
// verifico autorizzazione... se non c'è TENTO auth...
var currStatus = reqStatus;
// inizio da watchdog...
setWatchdog();
// controllo con la chiamata della clientInfo...
var taskCliInfo = Task.Run(() => GetClientStatusAsync());
taskCliInfo.Wait();
if (!currStatus.IsHmiEnrolled && currStatus.IsAuth)
{
// verifico il token della risposta...
if (verifResp != null)
{
// se ho il TOKEN di accesso procedo
if (verifResp.access_token != "")
{
// provo a chiamare ENROLL...
string callEnroll = doEnroll();
checkEnroll();
}
}
// controllo SE sia stato autorizzato
if (enrollResp != null && enrollResp.statusCode == 200)
{
if (enrollResp.result.OrganizationCode.ToUpper() == organizationCode.ToUpper())
{
sb.AppendLine("--------------------------------------------------");
sb.AppendLine(string.Format("{0:HH.mm.ss.fff} | ENROLL DONE", DateTime.Now));
sb.AppendLine("--------------------------------------------------");
answ.Message = sb.ToString();
// riporto in risposta i dati VUOTI...
answ.DeviceCode = "";
answ.UserCode = "";
answ.QrCode = "";
// registro che è OK x AUTH
answ.HasAuth = true;
answ.IsEnrolled = true;
}
}
}
// ritorno!
return answ;
}
/// <summary>
/// Classe x caricamento files
/// <summary>
/// <param name="token">{ext}<param>
/// <param name="progress">{ext}<param>
/// <param name="fileName">Nem file (PATH COMPLETO) da caricare{ext}<param>
/// <param name="uploadName">Nome da dare al file per il caricamento (con cui sarà nominato al download), se nullo/empty --> impostato BACKUP.{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> TryUpLoadFile(CancellationToken token, IProgress<Result> progress, string fileName, string uploadName = "")
{
Result result = new Result();
await Task.Run(() =>
{
// setup valori
result.Payload = new FilePayload();
result.CallResultOk = false;
GatewayPayload resp = new GatewayPayload();
bool forceChange = false;
if (uploadName == null || uploadName == "")
{
if (forceChange)
{
string fileExt = Path.GetExtension(fileName);
uploadName = string.Format("Backup{0}", fileExt);
}
else
{
uploadName = Path.GetFileName(fileName);
}
}
Stopwatch stopwatch = new Stopwatch();
try
{
stopwatch.Restart();
double fileSize = 0;
Task<string> taskRes;
//open file from the disk (file path is the path to the file to be opened)
using (FileStream fileStream = File.OpenRead(fileName))
{
fileSize = ((double)fileStream.Length) / 1024 / 1024;
Log.Instance.Info($"Inizio TryUpLoadFile, effettuata lettura del file {fileName} per {fileSize:N3}Mb");
// effettuo call EFFETTIVA
taskRes = Task.Run(() => Utils.uploadAsync(UploadUrl, uploadName, fileName));
}
//// test call FTP
//taskRes = Task.Run(() => Utils.ftpUploadAsync(MachineID, uploadName, fileName));
var rawResult = taskRes.Result;
if (rawResult != null && rawResult != "")
{
double velMbps = fileSize * 1000 / stopwatch.ElapsedMilliseconds;
Log.Instance.Info($"Completato invio del file: durata: {stopwatch.ElapsedMilliseconds} msec | vel media upload: {velMbps:N3} Mb/sec | risultato json: {rawResult}");
}
// leggo la risposta in var temporanea...
resp = JsonConvert.DeserializeObject<GatewayPayload>(rawResult);
}
catch (Exception exc)
{
// la loggo al posto di "embeddarla"
Log.Instance.Error(exc, $"Sollevata eccezione in TryUpLoadFile dopo {stopwatch.ElapsedMilliseconds} msec:");
// rilancio eccezione a monte...
throw;
}
if (resp != null)
{
// creo un nuovo result x embeddare il payload del risultato del caricamento file...
Result currReq = reqStatus;
// controllo se ho result...
string messaggio = resp.success ? "Upload Done!" : "Upload failed";
var risultato = new uploadResponse();
if (resp.success)
{
risultato.result = new FileUpLoadPayload()
{
key = resp.key,
link = resp.link,
expiry = resp.expiry,
filename = uploadName
};
}
else
{
risultato.error = new ErrorResult()
{
code = 000,
message = resp.error
};
}
FilePayload newPayload = new FilePayload()
{
Message = messaggio,
uploadResults = risultato
};
currReq.Payload = newPayload;
// salvo risultato!
reqStatus = currReq;
result = currReq;
}
else
{ }
}).ConfigureAwait(false);
return result;
}
/// <summary>
/// Effettua login e restituisce dati utente
/// <summary>
/// <param name="username">{ext}<param>
/// <param name="password">{ext}<param>
/// <returns>{ext}<returns>
public async Task<Result> TryUserLogin(string username, string password)
{
checkConnStatus();
var _currStatus = reqStatus;
await Task.Run(() =>
{
// inizio da watchdog...
setWatchdog();
// controllo con la chiamata della clientInfo...
var taskCliInfo = Task.Run(() => GetClientStatusAsync());
taskCliInfo.Wait();
_currStatus = taskCliInfo.Result;
// parto pessimista...
bool userOk = false;
// variabili accessorie
var userList = new UserPayload();
UserData answ = new UserData();
LoginErrorResult errore = new LoginErrorResult();
// controllo base: USER E PWD != ""
if (username != "" && password != "")
{
// recupero ed aggiorno userListcache
updateUserListCacheFromCloud();
UserData userFound = new UserData();
UserStatus currLoginStatus = UserStatus.NOT_IMPORTED;
string callResult = "";
// cerco in utenti LOCALI x trovare dati (di un precedente accesso/importazione) e quindi stato login...
userFound = userListCache.Find(x => x.Username == username);
if (userFound != null)
{
try
{
currLoginStatus = userFound.LoginStatus;
}
catch
{
currLoginStatus = UserStatus.NOT_IMPORTED;
}
}
// se sono online --> login
if (reqStatus.CloudStatusOk)
{
//Uri callUri = new Uri(BaseUrl + $"/FAKE/auth/login");
Uri callUri = new Uri(BaseUrl + $"/{organizationCode}/auth/login");
var payload = "{\"Username\": \"" + username + "\",\"Password\": \"" + password + "\"}";
HttpContent callCont = new StringContent(payload, Encoding.UTF8, "application/json");
// effettuo call effettiva
var taskRes = Task.Run(() => Utils.postUriAsync(callUri, callCont));
taskRes.Wait();
callResult = taskRes.Result;
if (callResult != "")
{
// deserializzo
var resp = JsonConvert.DeserializeObject<userLoginResponse>(callResult);
// controllo risultato: se 200 / status OK --> loggato!
if (resp.status == "Ok" || resp.statusCode == 200)
{
userData item = resp.result.user;
Image userImg = null;
try
{
// creo obj userData... SE HO immagine...
if (item.Image_Url != "")
{
var imgRes = Task.Run(() => Utils.getImageAsync(pageUrlImgDownload, item.Image_Url));
imgRes.Wait();
if (imgRes.Result != null)
{
userImg = imgRes.Result;
}
}
}
catch
{ }
userFound = new UserData()
{
UserId = item.Id,
IsAdmin = item.IsAdmin,
Username = item.Username,
Email = item.Email,
Cognome = item.Lastname,
Nome = item.Firstname,
LoginStatus = currLoginStatus,
UserImage = userImg
};
}
else if (resp.statusCode == 400 && callResult.IndexOf("Invalid password") >= 0)
{
errore = new LoginErrorResult
{
Reason = UserReason.PwdMismatch,
message = "Username Ok, wrong Password"
};
_currStatus.Error = errore;
// elimino...
userFound = null;
}
else
{
userFound = null;
}
}
else
{ }
}
else
{
userFound = userListCache.Find(x => x.Username == username);
}
// se trovato user (locale/cloud) procedo
if (userFound != null)
{
// controllo SE deleted...
if (userFound.LoginStatus == UserStatus.DELETED)
{
errore = new LoginErrorResult
{
Reason = UserReason.UserDeleted,
message = "User deleted from CLOUD"
};
}
// SE VALIDO
else
{
var _usrPwdList = userPwdLocalCache;
// cifro la pwd locale... cifrata
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);
string pwdCyph = GenerateSaltedHash(password, salt.ToString());
// cerco nelle pwd salvate: se lo trovo e NON HO una pwd --> accetto questa e salvo!
var usrId_Found = _usrPwdList.Find(x => x.Username == userFound.Username);
// --> a seconda che sia locale o meno decido come procedere...
if (reqStatus.CloudStatusOk)
{
if (usrId_Found != null)
{
// elimino vecchio record locale
_usrPwdList.Remove(usrId_Found);
}
// aggiungo nuova pwd
var _userPwd = new userPwdData()
{
Username = userFound.Username,
passwordCyph = pwdCyph
};
_usrPwdList.Add(_userPwd);
// salvo pwd
userPwdLocalCache = _usrPwdList;
// OK! trovato
answ = userFound;
userOk = true;
}
else
{
if (usrId_Found != null)
{
// verifico la pwd locale...
var pwdDataFound = _usrPwdList.Find(x => x.passwordCyph == pwdCyph);
if (pwdDataFound != null)
{
// OK! trovato
answ = userFound;
userOk = true;
}
else
{
errore = new LoginErrorResult
{
Reason = UserReason.PwdMismatch,
message = "Username Ok, wrong Password"
};
_currStatus.Error = errore;
}
}
}
}
}
else
{
var respErr = JsonConvert.DeserializeObject<userLoginFailedResponse>(callResult);
if (_currStatus.Error == null)
{
string msgErr = "Username not found";
if (respErr != null)
{
msgErr = "Username not found | " + respErr.message + " | " + respErr.result.error;
}
errore = new LoginErrorResult
{
Reason = UserReason.UserNotFound,
message = msgErr
};
_currStatus.Error = errore;
}
}
// controllo SE sia stato trovato
if (userOk)
{
// salvo in cache locale (comunque...)
currUser = answ;
// aggiungo nella risposta...
List<UserData> _answ = new List<UserData>();
_answ.Add(answ);
userList.UserList = _answ;
userList.Message = "User Found!";
_currStatus.Payload = userList;
_currStatus.Error = errore;
}
}
else
{
if (username == "")
{
errore = new LoginErrorResult
{
Reason = UserReason.UserNotFound,
message = "Username is empty"
};
}
else
{
errore = new LoginErrorResult
{
Reason = UserReason.PwdMismatch,
message = "Password is empty"
};
}
_currStatus.Error = errore;
}
reqStatus = _currStatus;
}).ConfigureAwait(false);
return _currStatus;
}
#endregion Public Methods
}
}