Пример #1
0
        public async Task <InvokeResult> UpdateUserAsync(UserInfo user, EntityHeader org, EntityHeader updatedByUser)
        {
            var appUser = await _appUserRepo.FindByIdAsync(user.Id);

            appUser.FirstName       = user.FirstName;
            appUser.LastName        = user.LastName;
            appUser.ProfileImageUrl = user.ProfileImageUrl;
            appUser.LastUpdatedBy   = updatedByUser;
            appUser.LastUpdatedDate = DateTime.UtcNow.ToJSONString();

            if (appUser.IsSystemAdmin != user.IsSystemAdmin)
            {
                var updateByAppUser = await GetUserByIdAsync(updatedByUser.Id, org, updatedByUser);

                if (!updateByAppUser.IsSystemAdmin)
                {
                    _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "UserServicesController_UpdateUserAsync", UserAdminErrorCodes.AuthNotSysAdmin.Message);
                    return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthNotSysAdmin.ToErrorMessage()));
                }
                appUser.IsSystemAdmin = user.IsSystemAdmin;
            }

            ValidationCheck(appUser, Actions.Update);

            await AuthorizeAsync(appUser, AuthorizeResult.AuthorizeActions.Update, updatedByUser, org);

            await _appUserRepo.UpdateAsync(appUser);

            return(InvokeResult.Success);
        }
Пример #2
0
        protected async Task <ResourceResponse <Document> > CreateDocumentAsync(TEntity item)
        {
            if (item is IValidateable)
            {
                var result = Validator.Validate(item as IValidateable);
                if (!result.Successful)
                {
                    throw new ValidationException("Invalid Data.", result.Errors);
                }
            }

            item.DatabaseName = _dbName;
            item.EntityType   = typeof(TEntity).Name;

            var response = await Client.CreateDocumentAsync(await GetCollectionDocumentsLinkAsync(), item);

            if (response.StatusCode != System.Net.HttpStatusCode.Created)
            {
                _logger.AddCustomEvent(LogLevel.Error, $"DocuementDbRepo<{_dbName}>_CreateDocumentAsync", "Error return code: " + response.StatusCode,
                                       new KeyValuePair <string, string>("EntityType", typeof(TEntity).Name),
                                       new KeyValuePair <string, string>("Id", item.Id)
                                       );
                throw new Exception("Could not insert entity");
            }

            if (_cacheProvider != null)
            {
                await _cacheProvider.AddAsync(GetCacheKey(item.Id), JsonConvert.SerializeObject(item));
            }

            return(response);
        }
Пример #3
0
        /// <summary>
        /// Validate the core properties of the request.
        /// </summary>
        /// <param name="authRequest"></param>
        /// <returns></returns>
        public async Task <InvokeResult <AuthResponse> > AccessTokenGrantAsync(AuthRequest authRequest)
        {
            var requestValidationResult = _authRequestValidators.ValidateAuthRequest(authRequest);

            if (!requestValidationResult.Successful)
            {
                return(InvokeResult <AuthResponse> .FromInvokeResult(requestValidationResult));
            }

            var accessTokenRequestValidationResult = _authRequestValidators.ValidateAccessTokenGrant(authRequest);

            if (!accessTokenRequestValidationResult.Successful)
            {
                return(InvokeResult <AuthResponse> .FromInvokeResult(accessTokenRequestValidationResult));
            }

            var signInRequest = await _signInManager.PasswordSignInAsync(authRequest.UserName, authRequest.Password, true, false);

            if (!signInRequest.Successful)
            {
                return(InvokeResult <AuthResponse> .FromInvokeResult(signInRequest));
            }

            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Message, "AuthTokenManager_AccessTokenGrantAsync", "UserLoggedIn", new KeyValuePair <string, string>("email", authRequest.UserName));

            var appUser = await _userManager.FindByNameAsync(authRequest.UserName);

            if (appUser == null)
            {
                /* Should really never, ever happen, but well...let's track it */
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthTokenManager_AccessTokenGrantAsync", UserAdminErrorCodes.AuthCouldNotFindUserAccount.Message, new KeyValuePair <string, string>("email", authRequest.UserName));
                return(InvokeResult <AuthResponse> .FromErrors(UserAdminErrorCodes.AuthCouldNotFindUserAccount.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.AppInstanceId))
            {
                /* This generally happens for the first time the app is logged in on a new device, if it is logged in again future times it will resend the app id */
                var appInstanceResult = await _appInstanceManager.CreateForUserAsync(appUser.Id, authRequest);

                authRequest.AppInstanceId = appInstanceResult.Result.RowKey;
            }
            else
            {
                var updateLastLoginResult = (await _appInstanceManager.UpdateLastLoginAsync(appUser.Id, authRequest));
                if (updateLastLoginResult.Successful)
                {
                    authRequest.AppInstanceId = updateLastLoginResult.Result.RowKey;
                }
                else
                {
                    return(InvokeResult <AuthResponse> .FromInvokeResult(updateLastLoginResult.ToInvokeResult()));
                }
            }

            var refreshTokenResponse = await _refreshTokenManager.GenerateRefreshTokenAsync(authRequest.AppId, authRequest.AppInstanceId, appUser.Id);

            return(_tokenHelper.GenerateAuthResponse(appUser, authRequest, refreshTokenResponse));
        }
Пример #4
0
        public async Task <InvokeResult> UpdateUserAsync(UserInfo user, EntityHeader org, EntityHeader updatedByUser)
        {
            var appUser = await _appUserRepo.FindByIdAsync(user.Id);

            if (!String.IsNullOrEmpty(user.FirstName))
            {
                appUser.FirstName = user.FirstName;
            }
            if (!String.IsNullOrEmpty(user.LastName))
            {
                appUser.LastName = user.LastName;
            }
            if (!String.IsNullOrEmpty(user.PhoneNumber))
            {
                appUser.PhoneNumber          = user.PhoneNumber;
                appUser.PhoneNumberConfirmed = true;
            }

            if ((user.ProfileImageUrl != null))
            {
                appUser.ProfileImageUrl = user.ProfileImageUrl;
            }

            appUser.LastUpdatedBy   = updatedByUser;
            appUser.LastUpdatedDate = DateTime.UtcNow.ToJSONString();

            if (appUser.IsSystemAdmin != user.IsSystemAdmin)
            {
                var updateByAppUser = await GetUserByIdAsync(updatedByUser.Id, org, updatedByUser);

                if (updateByAppUser == null)
                {
                    return(InvokeResult.FromError($"Could not find updating user with id: {updateByAppUser.Id}."));
                }

                if (!updateByAppUser.IsSystemAdmin)
                {
                    _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "UserServicesController_UpdateUserAsync", UserAdminErrorCodes.AuthNotSysAdmin.Message);
                    return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthNotSysAdmin.ToErrorMessage()));
                }
                appUser.IsSystemAdmin = user.IsSystemAdmin;
                appUser.IsAppBuilder  = user.IsAppBuilder;
                appUser.IsOrgAdmin    = user.IsOrgAdmin;
                appUser.IsRuntimeuser = user.IsRuntimeUser;
                appUser.IsUserDevice  = user.IsUserDevice;
            }

            ValidationCheck(appUser, Actions.Update);

            await AuthorizeAsync(appUser, AuthorizeResult.AuthorizeActions.Update, updatedByUser, org);

            await _appUserRepo.UpdateAsync(appUser);

            return(InvokeResult.Success);
        }
Пример #5
0
        public async Task <InvokeResult> SendResetPasswordLinkAsync(SendResetPasswordLink sendResetPasswordLink)
        {
            var validationResult = _authRequestValidators.ValidateSendPasswordLinkRequest(sendResetPasswordLink);

            if (!validationResult.Successful)
            {
                return(validationResult);
            }

            var appUser = await _userManager.FindByEmailAsync(sendResetPasswordLink.Email);

            if (appUser == null)
            {
                _adminLogger.AddError("PasswordManager_SendResetPasswordLinkAsync", "CouldNotFindUser", new System.Collections.Generic.KeyValuePair <string, string>("email", sendResetPasswordLink.Email));
                return(InvokeResult.FromErrors(new ErrorMessage(UserAdminResources.Err_ResetPwd_CouldNotFindUser)));
            }

            var token = await _userManager.GeneratePasswordResetTokenAsync(appUser);

            var encodedToken      = System.Net.WebUtility.UrlEncode(token);
            var callbackUrl       = $"{_appConfig.WebAddress}{ACTION_RESET_PASSWORD}?code={encodedToken}";
            var mobileCallbackUrl = $"nuviot://resetpassword?code={token}";

#if DIAG
            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Message, "PasswordManager_SendResetPasswordLinkAsync", "SentToken",
                                        token.ToKVP("token"),
                                        appUser.Id.ToKVP("appUserId"),
                                        encodedToken.ToKVP("encodedToken"),
                                        appUser.Email.ToKVP("toEmailAddress"));
#endif

            var subject = UserAdminResources.Email_ResetPassword_Subject.Replace("[APP_NAME]", _appConfig.AppName);
            var body    = UserAdminResources.Email_ResetPassword_Body.Replace("[CALLBACK_URL]", callbackUrl).Replace("[MOBILE_CALLBACK_URL]", mobileCallbackUrl);

            var result = await _emailSender.SendAsync(sendResetPasswordLink.Email, subject, body);

            if (result.Successful)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Message, "PasswordManager_SendResetPasswordLinkAsync", "SentLink",
                                            appUser.Id.ToKVP("appUserId"),
                                            appUser.Email.ToKVP("toEmailAddress"));

                var org = appUser.CurrentOrganization == null?EntityHeader.Create(Guid.Empty.ToId(), "????") : appUser.CurrentOrganization;

                await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "SentResetPasswordLink", org, appUser.ToEntityHeader());
            }
            else
            {
                _adminLogger.AddError("PasswordManager_SendResetPasswordLinkAsync", "Could Not Send Password Link", result.ErrorsToKVPArray());
            }

            return(result);
        }
Пример #6
0
        public async Task DeleteAsync <TEntity>(string id, IEntityHeader org) where TEntity : class, IIDEntity, INoSQLEntity, IKeyedEntity, IOwnedEntity
        {
            var documentLink = await GetCollectionDocumentsLinkAsync();

            var docClient = GetDocumentClient();

            var collectionName = _dbName + "_Collections";
            var docUri         = UriFactory.CreateDocumentUri(_dbName, collectionName, id);
            var response       = await docClient.ReadDocumentAsync(docUri);

            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                var json = response.Resource.ToString();

                if (String.IsNullOrEmpty(json))
                {
                    _logger.AddCustomEvent(LogLevel.Error, "DocumentDBRepoBase_GetDocumentAsync", $"Empty Response Content", new KeyValuePair <string, string>("entityType", typeof(TEntity).Name), new KeyValuePair <string, string>("id", id));
                    throw new RecordNotFoundException(typeof(TEntity).Name, id);
                }

                var entity = JsonConvert.DeserializeObject <TEntity>(json);
                if (org.Id != entity.OwnerOrganization.Id)
                {
                    throw new NotAuthorizedException($"Attempt to delete record of type {typeof(TEntity).Name} owned by org {entity.OwnerOrganization.Text} by org {org.Text}");
                }

                await docClient.DeleteDocumentAsync(docUri);
            }
        }
Пример #7
0
        public async Task <InvokeResult> AddBinAsync(byte[] data, string fileName)
        {
            var result = await GetStorageContainerAsync("firmware");

            if (!result.Successful)
            {
                return(result.ToInvokeResult());
            }

            var container = result.Result;

            var blob = container.GetBlockBlobReference(fileName);

            blob.Properties.ContentType = "application/octet-stream";

            //TODO: Should really encapsulate the idea of retry of an action w/ error reporting
            var numberRetries = 5;
            var retryCount    = 0;
            var completed     = false;
            var stream        = new MemoryStream(data);

            while (retryCount++ < numberRetries && !completed)
            {
                try
                {
                    stream.Seek(0, SeekOrigin.Begin);
                    await blob.UploadFromStreamAsync(stream);
                }
                catch (Exception ex)
                {
                    if (retryCount == numberRetries)
                    {
                        _logger.AddException("FirmwareBinRepo_AddItemAsync", ex);
                        return(InvokeResult.FromException("FirmwareBinRepo_AddItemAsync", ex));
                    }
                    else
                    {
                        _logger.AddCustomEvent(LagoVista.Core.PlatformSupport.LogLevel.Warning, "FirmwareBinRepo_AddItemAsync", "", ex.Message.ToKVP("exceptionMessage"), ex.GetType().Name.ToKVP("exceptionType"), retryCount.ToString().ToKVP("retryCount"));
                    }
                    await Task.Delay(retryCount * 250);
                }
            }

            return(InvokeResult.Success);
        }
Пример #8
0
        public async Task <InvokeResult <RefreshToken> > GenerateRefreshTokenAsync(string appId, string appInstanceId, string userId)
        {
            var refreshToken = new RefreshToken(userId);

            refreshToken.RowKey        = DateTime.UtcNow.ToInverseTicksRowKey();
            refreshToken.AppId         = appId;
            refreshToken.AppInstanceId = appInstanceId;
            refreshToken.IssuedUtc     = DateTime.UtcNow.ToJSONString();
            refreshToken.ExpiresUtc    = (DateTime.UtcNow + _tokenOptions.RefreshExpiration).ToJSONString();
            await _refreshTokenRepo.SaveRefreshTokenAsync(refreshToken);

            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Verbose, "RefreshTokenManager_GenerateRefreshTokenAsync", "RefreshTokenGenerated",
                                        new KeyValuePair <string, string>("authAppId", appId),
                                        new KeyValuePair <string, string>("authAppInstanceId", appInstanceId),
                                        new KeyValuePair <string, string>("authUserId", userId));

            return(InvokeResult <RefreshToken> .Create(refreshToken));
        }
Пример #9
0
        public async Task <InvokeResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)
        {
            var signInResult = await _signinManager.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);

            var appUser = await _userManager.FindByEmailAsync(userName);

            if (appUser != null && appUser.CurrentOrganization != null)
            {
                var isOrgAdmin = await _orgManager.IsUserOrgAdminAsync(appUser.CurrentOrganization.Id, appUser.Id);

                if (isOrgAdmin != appUser.IsOrgAdmin)
                {
                    appUser.IsOrgAdmin = isOrgAdmin;
                    await _userManager.UpdateAsync(appUser);
                }
            }

            if (signInResult.Succeeded)
            {
                await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin", appUser.CurrentOrganization, appUser.ToEntityHeader());

                return(InvokeResult.Success);
            }

            if (signInResult.IsLockedOut)
            {
                if (appUser != null)
                {
                    await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin Failed - Locked Out", appUser.CurrentOrganization, appUser.ToEntityHeader());
                }

                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthTokenManager_AccessTokenGrantAsync", UserAdminErrorCodes.AuthUserLockedOut.Message, new KeyValuePair <string, string>("email", userName));
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthUserLockedOut.ToErrorMessage()));
            }

            if (appUser != null)
            {
                await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin Failed", appUser.CurrentOrganization, appUser.ToEntityHeader());
            }

            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthTokenManager_AccessTokenGrantAsync", UserAdminErrorCodes.AuthInvalidCredentials.Message, new KeyValuePair <string, string>("email", userName));
            return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthInvalidCredentials.ToErrorMessage()));
        }
Пример #10
0
        protected virtual async Task <TableResult> Execute(TableOperation op)
        {
            var result = await _table.ExecuteAsync(op);

            if (result == null)
            {
                _logger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "TableStorageBase_Execute", "Null Response Code");
                throw new Exception($"Null response code from table operation");
            }
            else if (result.HttpStatusCode < 200 || result.HttpStatusCode > 299)
            {
                _logger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "TableStorageBase_Execute", "Non-Success Status Code", new System.Collections.Generic.KeyValuePair <string, string>("StatusCode", result.HttpStatusCode.ToString()));
                throw new Exception($"Error response code from table operation");
            }
            else
            {
                return(result);
            }
        }
Пример #11
0
        public async Task <InvokeResult> CheckConfirmedAsync(EntityHeader orgHeader, EntityHeader userHeader)
        {
            /* This will only take the current user id so we don't have to do any security checks, not really confidential info anyways */
            var user = await _userManager.FindByIdAsync(userHeader.Id);

            if (user == null)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "UserVerifyController_SendConfirmationEmailAsync", "Could not get current user.");
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthCouldNotFindUserAccount.ToErrorMessage()));
            }

            if (user.EmailConfirmed)
            {
                return(InvokeResult.Success);
            }
            else
            {
                return(InvokeResult.FromErrors(new ErrorMessage()
                {
                    Message = "Email Not Confirmed"
                }));
            }
        }
Пример #12
0
        public async Task <InvokeResult> SendAsync(string email, string subject, string body)
        {
            try
            {
                body = $@"<body>
<img src=""{_appConfig.AppLogo}"" />
<h1>{_appConfig.AppName}</h1>
<h2>{subject}</h2>
{body}
<img src=""{_appConfig.CompanyLogo}"" />
<p>Please do not reply to this email as it is an unmonitored address</p>
<a href=""mailto:[email protected]"">Contact Support</a>
</body>";

                var msg = new MimeMessage()
                {
                    Subject = subject,
                    Body    = new TextPart("html", body),
                };

                msg.To.Add(new MailboxAddress(email));
                msg.From.Add(new MailboxAddress(_settings.SmtpFrom));

                using (var client = new SmtpClient())
                {
                    await client.ConnectAsync(_settings.SmtpServer.Uri.ToString(), 587, false);

                    await client.AuthenticateAsync(_settings.SmtpServer.UserName, _settings.SmtpServer.Password);

                    await client.SendAsync(msg);

                    await client.DisconnectAsync(true);
                }

                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Verbose, "SendGridEmailServices_SendAsync", "EmailSent",
                                            new System.Collections.Generic.KeyValuePair <string, string>("Subject", subject),
                                            new System.Collections.Generic.KeyValuePair <string, string>("to", email));

                return(InvokeResult.Success);
            }
            catch (Exception ex)
            {
                _adminLogger.AddException("SendGridEmailServices_SendAsync", ex,
                                          new System.Collections.Generic.KeyValuePair <string, string>("Subject", subject),
                                          new System.Collections.Generic.KeyValuePair <string, string>("to", email));

                return(InvokeResult.FromException("SendGridEmailServices_SendAsync", ex));
            }
        }
Пример #13
0
        public async Task <InvokeResult <AppInstance> > UpdateLastLoginAsync(string appUserId, AuthRequest existingAppInstance)
        {
            var appInstance = await _appInstanceRepo.GetAppInstanceAsync(appUserId, existingAppInstance.AppInstanceId);

            if (appInstance == null)
            {
                _adminLogger.AddError("AppInstanceManager_UpdateLastLoginAsync", "Could not load, possible user id change",
                                      new KeyValuePair <string, string>("appUserId", appUserId),
                                      new KeyValuePair <string, string>("appInstanceId", existingAppInstance.AppInstanceId));

                return(await CreateForUserAsync(appUserId, existingAppInstance));
            }
            else
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Message, "AppInstanceManager_UpdateLastLoginAsync", "Update Last Login Information",
                                            new KeyValuePair <string, string>("appUserId", appUserId),
                                            new KeyValuePair <string, string>("appInstanceId", existingAppInstance.AppInstanceId));

                appInstance.LastLogin = DateTime.UtcNow.ToJSONString();
                await _appInstanceRepo.UpdateAppInstanceAsync(appInstance);

                return(InvokeResult <AppInstance> .Create(appInstance));
            }
        }
Пример #14
0
        public async Task <InvokeResult> SendAsync(string number, string contents)
        {
            try
            {
                TwilioClient.Init(_settings.SmsServer.AccountId, _settings.SmsServer.AccessKey);
                var restClient = new TwilioRestClient(_settings.SmsServer.AccountId, _settings.SmsServer.AccessKey);
                await MessageResource.CreateAsync(to : new PhoneNumber(number), from : new PhoneNumber(_settings.FromPhoneNumber), body : contents);

                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Verbose, "TwilioSMSSender_SendAsync", "EmailSent",
                                            new System.Collections.Generic.KeyValuePair <string, string>("Subject", number),
                                            new System.Collections.Generic.KeyValuePair <string, string>("to", contents));

                return(InvokeResult.Success);
            }
            catch (Exception ex)
            {
                _adminLogger.AddException("SendGridEmailServices_SendAsync", ex,
                                          new System.Collections.Generic.KeyValuePair <string, string>("number", number),
                                          new System.Collections.Generic.KeyValuePair <string, string>("contents", contents));

                return(InvokeResult.FromException("TwilioSMSSender_SendAsync", ex));
            }
        }
        private async Task LoadAsync()
        {
            var rootUri = "https://api.nuviot.com/api/simulator/network/runtime";

            switch (_environment)
            {
            case Environments.Development:
                rootUri = "https://dev.nuviot.com/api/simulator/network/runtime";
                break;

            case Environments.Testing:
                rootUri = "https://test.nuviot.com/api/simulator/network/runtime";
                break;

            case Environments.LocalDevelopment:
                rootUri = "http://localhost:5001/api/simulator/network/runtime";
                break;

            default:
                break;
            }

            var requestId = Guid.NewGuid().ToId();
            var dateStamp = DateTime.UtcNow.ToJSONString();
            var version   = "1.0.0";

            var bldr = new StringBuilder();

            //Adding the \r\n manualy ensures that the we don't have any
            //platform specific code messing with our signature.
            bldr.Append($"{requestId}\r\n");
            bldr.Append($"{dateStamp}\r\n");
            bldr.Append($"{version}\r\n");
            bldr.Append($"{_org.Id}\r\n");
            bldr.Append($"{_user.Id}\r\n");
            bldr.Append($"{_simulatorNetworkId}\r\n");

            var sasKey = GetSignature(requestId, _simAccessKey, bldr.ToString());

            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Message, "SimulatorRuntimeManager_InitAsync", $"Requesting configuration from: {rootUri} ");
            Console.WriteLine($"Requesting configuration from: {rootUri}");


            var client = new HttpClient();

            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("SAS", sasKey);
            client.DefaultRequestHeaders.Add(REQUEST_ID, requestId);
            client.DefaultRequestHeaders.Add(ORG_ID, _org.Id);
            client.DefaultRequestHeaders.Add(ORG, _org.Text);
            client.DefaultRequestHeaders.Add(USER_ID, _user.Id);
            client.DefaultRequestHeaders.Add(USER, _user.Text);
            client.DefaultRequestHeaders.Add(NETWORK_ID, _simulatorNetworkId);
            client.DefaultRequestHeaders.Add(DATE, dateStamp);
            client.DefaultRequestHeaders.Add(VERSION, version);

            try
            {
                var json = await client.GetStringAsync(rootUri);

                Runtimes.Clear();

                var network = JsonConvert.DeserializeObject <SimulatorNetwork>(json);
                Console.WriteLine($"Loaded simulator network {network.Name}");

                foreach (var sim in network.Simulators)
                {
                    var services = _factory.GetServices();
                    var runtime  = new SimulatorRuntime(services, Publisher, _adminLogger, sim);
                    await runtime.StartAsync();

                    Runtimes.Add(runtime);
                }
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Error loading runtime.");
                Console.WriteLine(ex.Message);
                Console.ResetColor();
            }
        }
Пример #16
0
        public async Task <InvokeResult> SetUserOrgAsync(AuthRequest authRequest, AppUser appUser)
        {
            // Synthesize the org and user from request and app user
            var org = new EntityHeader()
            {
                Id = authRequest.OrgId, Text = authRequest.OrgName
            };
            var user = new EntityHeader()
            {
                Id = appUser.Id, Text = $"{appUser.FirstName} {appUser.LastName}"
            };

            authRequest.OrgId   = org.Id;
            authRequest.OrgName = org.Text;

            // 1) Ensure user has access to the requested org.
            var orgs = await _orgManager.GetOrganizationsForUserAsync(appUser.Id, org, user);

            var switchToOrg = orgs.Where(o => o.OrgId == authRequest.OrgId).FirstOrDefault();

            if (switchToOrg == null)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthTokenManager_SetOrg", UserAdminErrorCodes.AuthOrgNotAuthorized.Message,
                                            new KeyValuePair <string, string>("userid", appUser.Id),
                                            new KeyValuePair <string, string>("requestedOrgId", authRequest.OrgId));
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthOrgNotAuthorized.ToErrorMessage()));
            }

            var oldOrgId   = EntityHeader.IsNullOrEmpty(appUser.CurrentOrganization) ? "none" : appUser.CurrentOrganization.Id;
            var oldOrgName = EntityHeader.IsNullOrEmpty(appUser.CurrentOrganization) ? "none" : appUser.CurrentOrganization.Text;

            // 2) Change the org on the user object
            appUser.CurrentOrganization = new EntityHeader()
            {
                Id   = authRequest.OrgId,
                Text = switchToOrg.OrganizationName,
            };

            appUser.IsOrgAdmin = switchToOrg.IsOrgAdmin;

            // 3) Add the roles to the user for the org.
            var orgRoles = await _orgManager.GetUsersRolesInOrgAsync(authRequest.OrgId, appUser.Id, appUser.CurrentOrganization, appUser.ToEntityHeader());

            appUser.CurrentOrganizationRoles = new List <EntityHeader>();
            foreach (var orgRole in orgRoles)
            {
                appUser.CurrentOrganizationRoles.Add(orgRole.ToEntityHeader());
            }

            // 4) Write the updated user back to storage.
            var updateResult = await _userManager.UpdateAsync(appUser);

            if (!updateResult.Successful)
            {
                var invokeResult = updateResult.ToInvokeResult();

                _adminLogger.LogInvokeResult("OrgHelper_SetUserOrgAsync", invokeResult,
                                             new KeyValuePair <string, string>("userId", appUser.Id),
                                             new KeyValuePair <string, string>("userName", appUser.UserName));

                return(invokeResult);
            }

            // 5) Write this change to logger.
            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Message, "AuthTokenManager_SetOrg", "UserSwitchedOrg",
                                        new KeyValuePair <string, string>("userId", appUser.Id),
                                        new KeyValuePair <string, string>("userName", appUser.UserName),
                                        new KeyValuePair <string, string>("oldOrgId", oldOrgId),
                                        new KeyValuePair <string, string>("oldOrgName", oldOrgName),
                                        new KeyValuePair <string, string>("newOrgId", appUser.CurrentOrganization.Id),
                                        new KeyValuePair <string, string>("newOrgName", appUser.CurrentOrganization.Text));

            // 6) Return success, no response data necessary, app user is by reference so it should already be updated.
            return(InvokeResult.Success);
        }
Пример #17
0
        public InvokeResult ValidateAuthRequest(AuthRequest authRequest)
        {
            if (authRequest == null)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthRequestNull.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthRequestNull.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.AppId))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthMissingAppId.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingAppId.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.DeviceId))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthMissingDeviceId.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingDeviceId.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.ClientType))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthMissingClientType.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingClientType.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.Email))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAccessTokenGrant", UserAdminErrorCodes.AuthMissingEmail.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingEmail.ToErrorMessage()));
            }

            return(InvokeResult.Success);
        }
Пример #18
0
        public async Task <InvokeResult <Invitation> > InviteUserAsync(Models.DTOs.InviteUser inviteViewModel, EntityHeader org, EntityHeader user)
        {
            ValidateAuthParams(org, user);


            if (inviteViewModel == null)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "OrgManager_InviteUserAsync", UserAdminErrorCodes.InviteIsNull.Message);
                return(InvokeResult <Invitation> .FromErrors(UserAdminErrorCodes.InviteIsNull.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(inviteViewModel.Email))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "OrgManager_InviteUserAsync", UserAdminErrorCodes.InviteEmailIsEmpty.Message);
                return(InvokeResult <Invitation> .FromErrors(UserAdminErrorCodes.InviteEmailIsEmpty.ToErrorMessage()));
            }


            var emailRegEx = new Regex(@"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");

            if (!emailRegEx.Match(inviteViewModel.Email).Success)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "OrgManager_InviteUserAsync", UserAdminErrorCodes.InviteEmailIsInvalid.Message);
                return(InvokeResult <Invitation> .FromErrors(UserAdminErrorCodes.InviteEmailIsInvalid.ToErrorMessage()));
            }


            if (String.IsNullOrEmpty(inviteViewModel.Name))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "OrgManager_InviteUserAsync", UserAdminErrorCodes.InviteNameIsEmpty.Message);
                return(InvokeResult <Invitation> .FromErrors(UserAdminErrorCodes.InviteNameIsEmpty.ToErrorMessage()));
            }

            if (await _orgUserRepo.QueryOrgHasUserByEmailAsync(org.Id, inviteViewModel.Email))
            {
                var existingUser = await _appUserRepo.FindByEmailAsync(inviteViewModel.Email);

                if (existingUser != null)
                {
                    var msg = UserAdminResources.InviteUser_AlreadyPartOfOrg.Replace(Tokens.USERS_FULL_NAME, existingUser.Name).Replace(Tokens.EMAIL_ADDR, inviteViewModel.Email);
                    return(InvokeResult <Invitation> .FromErrors(new ErrorMessage(msg)));
                }
                else
                {
                    _adminLogger.AddError("OrgManager_InviteUserAsync", "User Found in Org Unit XRef Table Storage, but not in User, bad data", new KeyValuePair <string, string>("OrgId", org.Id), new KeyValuePair <string, string>("Email", inviteViewModel.Email));
                }
            }

            var existingInvite = await _inviteUserRepo.GetInviteByOrgIdAndEmailAsync(org.Id, inviteViewModel.Email);

            if (existingInvite != null)
            {
                existingInvite.Status = Invitation.StatusTypes.Replaced;
                await _inviteUserRepo.UpdateInvitationAsync(existingInvite);
            }

            Organization organization;

            organization = await _organizationRepo.GetOrganizationAsync(org.Id);

            if (organization == null)
            {
                /* Quick and Dirty Error Checking, should Never Happen */
                return(InvokeResult <Invitation> .FromError("Could not Load Org"));
            }

            var inviteModel = new Invitation()
            {
                RowKey           = Guid.NewGuid().ToId(),
                PartitionKey     = org.Id,
                OrganizationId   = org.Id,
                OrganizationName = org.Text,
                InvitedById      = user.Id,
                InvitedByName    = user.Text,
                Message          = inviteViewModel.Message,
                Name             = inviteViewModel.Name,
                Email            = inviteViewModel.Email,
                DateSent         = DateTime.Now.ToJSONString(),
                Status           = Invitation.StatusTypes.New,
            };

            await AuthorizeAsync(user, org, "InviteUser", inviteModel.RowKey);

            inviteModel.OrganizationName = organization.Name;

            await _inviteUserRepo.InsertInvitationAsync(inviteModel);

            inviteModel = await _inviteUserRepo.GetInvitationAsync(inviteModel.RowKey);
            await SendInvitationAsync(inviteModel, organization.Name, user);

            inviteModel.DateSent = DateTime.Now.ToJSONString();
            inviteModel.Status   = Invitation.StatusTypes.Sent;
            await _inviteUserRepo.UpdateInvitationAsync(inviteModel);

            return(InvokeResult <Invitation> .Create(inviteModel));
        }
Пример #19
0
        public async Task <InvokeResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)
        {
            if (string.IsNullOrEmpty(userName))
            {
                return(InvokeResult.FromError($"User name is a required field [{userName}]."));
            }
            if (string.IsNullOrEmpty(password))
            {
                return(InvokeResult.FromError($"Password is a required field [{userName}]."));
            }

            var signInResult = await _signinManager.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);

            var appUser = await _userManager.FindByEmailAsync(userName);

            if (appUser == null)
            {
                await LogEntityActionAsync(userName, typeof(AppUser).Name, $"Could not find user with account [{userName}].", EntityHeader.Create("unkonwn", "unknown"), EntityHeader.Create(userName, userName));

                return(InvokeResult.FromError($"Could not find user [{userName}]."));
            }

            if (appUser.IsAccountDisabled)
            {
                await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin Failed - Account Disabled", appUser.CurrentOrganization, appUser.ToEntityHeader());

                return(InvokeResult.FromError($"Account [{userName}] is disabled."));
            }

            if (appUser.CurrentOrganization != null)
            {
                var isOrgAdmin = await _orgManager.IsUserOrgAdminAsync(appUser.CurrentOrganization.Id, appUser.Id);

                if (isOrgAdmin != appUser.IsOrgAdmin)
                {
                    appUser.IsOrgAdmin = isOrgAdmin;
                    await _userManager.UpdateAsync(appUser);
                }
            }

            if (signInResult.Succeeded)
            {
                await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin", appUser.CurrentOrganization, appUser.ToEntityHeader());

                return(InvokeResult.Success);
            }

            if (signInResult.IsLockedOut)
            {
                await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin Failed - Locked Out", appUser.CurrentOrganization, appUser.ToEntityHeader());

                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthTokenManager_AccessTokenGrantAsync", UserAdminErrorCodes.AuthUserLockedOut.Message, new KeyValuePair <string, string>("email", userName));
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthUserLockedOut.ToErrorMessage()));
            }

            await LogEntityActionAsync(appUser.Id, typeof(AppUser).Name, "UserLogin Failed", appUser.CurrentOrganization, appUser.ToEntityHeader());


            _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthTokenManager_AccessTokenGrantAsync", UserAdminErrorCodes.AuthInvalidCredentials.Message, new KeyValuePair <string, string>("email", userName));
            return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthInvalidCredentials.ToErrorMessage()));
        }
Пример #20
0
        public async Task <InvokeResult <Uri> > AddFileAsync(string fileName, byte[] data, string contentType = "application/octet-stream")
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException(nameof(fileName));
            }

            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            var result = await GetStorageContainerAsync(_containerName);

            if (!result.Successful)
            {
                return(InvokeResult <Uri> .FromInvokeResult(result.ToInvokeResult()));
            }

            var container = result.Result;

            if (fileName.StartsWith("/"))
            {
                fileName = fileName.TrimStart('/');
            }

            var blob = container.GetBlockBlobReference(fileName);

            blob.Properties.ContentType = contentType;

            //TODO: Should really encapsulate the idea of retry of an action w/ error reporting
            var numberRetries = 5;
            var retryCount    = 0;
            var completed     = false;
            var stream        = new MemoryStream(data);

            while (retryCount++ < numberRetries && !completed)
            {
                try
                {
                    stream.Seek(0, SeekOrigin.Begin);
                    await blob.UploadFromStreamAsync(stream);
                }
                catch (Exception ex)
                {
                    if (retryCount == numberRetries)
                    {
                        _logger.AddException("CloudFileStorage_AddFileAsync", ex, _containerName.ToKVP("containerName"));
                        var exceptionResult = InvokeResult.FromException("CloudFileStorage_AddFileAsync", ex);
                        return(InvokeResult <Uri> .FromInvokeResult(exceptionResult));
                    }
                    else
                    {
                        _logger.AddCustomEvent(LagoVista.Core.PlatformSupport.LogLevel.Warning, "CloudFileStorage_AddFileAsync", "", ex.Message.ToKVP("exceptionMessage"), ex.GetType().Name.ToKVP("exceptionType"), retryCount.ToString().ToKVP("retryCount"));
                    }
                    await Task.Delay(retryCount * 250);
                }
            }

            return(InvokeResult <Uri> .Create(blob.Uri));
        }
Пример #21
0
        public InvokeResult ValidateAuthRequest(AuthRequest authRequest)
        {
            if (authRequest == null)
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthRequestNull.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthRequestNull.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.AppId))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthMissingAppId.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingAppId.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.DeviceId))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthMissingDeviceId.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingDeviceId.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.ClientType))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAuthRequest", UserAdminErrorCodes.AuthMissingClientType.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingClientType.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.Email))
            {
                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAccessTokenGrant", UserAdminErrorCodes.AuthMissingEmail.Message);
                return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingEmail.ToErrorMessage()));
            }

            if (String.IsNullOrEmpty(authRequest.UserName))
            {
                authRequest.UserName = authRequest.Email;
            }

            switch (authRequest.AuthType)
            {
            case AuthTypes.ClientApp:
                break;

            case AuthTypes.DeviceUser:
                if (String.IsNullOrEmpty(authRequest.DeviceRepoId))
                {
                    _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "AuthRequestValidators_ValidateAccessTokenGrant", UserAdminErrorCodes.AuthMissingRepoIdForDeviceUser.Message);
                    return(InvokeResult.FromErrors(UserAdminErrorCodes.AuthMissingRepoIdForDeviceUser.ToErrorMessage()));
                }
                break;

            case AuthTypes.Runtime:
                break;

            case AuthTypes.User:
                break;
            }

            return(InvokeResult.Success);
        }
        protected async Task <InvokeResult <T> > GetAsync <T>(string path, DeploymentHost host, EntityHeader org, EntityHeader user)
        {
            using (var request = GetHttpClient(host, org, user, "GET", path))
            {
                try
                {
                    var uri      = new Uri($"{host.AdminAPIUri}{path}");
                    var response = await request.GetAsync(uri);

                    if (response.IsSuccessStatusCode)
                    {
                        var json = await response.Content.ReadAsStringAsync();

                        return(JsonConvert.DeserializeObject <InvokeResult <T> >(json));
                    }
                    else
                    {
                        _logger.AddCustomEvent(LogLevel.Error, "DeploymentConnectorService", $"{response.StatusCode} - {response.ReasonPhrase}",
                                               new KeyValuePair <string, string>("hostid", host.Id),
                                               new KeyValuePair <string, string>("path", path),
                                               new KeyValuePair <string, string>("orgid", org.Id),
                                               new KeyValuePair <string, string>("userid", user.Id));

                        return(DeploymentErrorCodes.ErrorCommunicatingWithhost.ToFailedInvocation <T>($"{response.StatusCode} - {response.ReasonPhrase}"));
                    }
                }
                catch (Exception ex)
                {
                    _logger.AddCustomEvent(LogLevel.Error, "DeploymentConnectorService", ex.Message,
                                           new KeyValuePair <string, string>("hostid", host.Id),
                                           new KeyValuePair <string, string>("path", path),
                                           new KeyValuePair <string, string>("orgid", org.Id),
                                           new KeyValuePair <string, string>("userid", user.Id));

                    return(DeploymentErrorCodes.ErrorCommunicatingWithhost.ToFailedInvocation <T>(ex.Message));
                }
            }
        }
Пример #23
0
        public async Task <InvokeResult> SendAsync(string email, string subject, string body)
        {
            try
            {
                body = $@"
<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd""><html xmlns=""http://www.w3.org/1999/xhtml""><head>
<meta name=""viewport"" content=""width=device-width, initial-scale=1, minium-scale=1, maxium-scale=1"">
	<title>NuvIoT - IoT Eneablement Platform</title>
	<style type=""text/css"">

    body
	{{
        width: 100% !important;
		-webkit-text-size-adjust: 100%;
		-ms-text-size-adjust: 100%;
		margin: 0;
		padding: 0;
	}}

.ExternalClass
	{{
        width: 100%;
	}}

#backgroundTable
		{{
            margin: 0;
			padding: 0;
			width: 100% !important;
			line-height: 100% !important;
		}}

	img
		{{
            outline: none;
			text-decoration: none;
			-ms-interpolation-mode: bicubic;
		}}

		a img
		{{
            border: none;
		}}

		.image-fix
		{{
            display: block;
		}}

			Bring inline: Yes. */
			p
			{{
                margin: 1em 0;
			}}

			/* Hotmail header color reset
			Bring inline: Yes. */
			h1, h2, h3, h4, h5
			{{
                color: #333333 !important;
				font-weight:400;
			}}

			h6
			{{
                color: #666666 !important;
			}}

				h1 a, h2 a, h3 a, h4 a, h5 a, h6 a
				{{
                    color: #2172ba !important;
				}}

					h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active
					{{
                        color: #2172ba !important;
					}}

					h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited
					{{
                        color: #2172ba !important;
					}}

			/* Outlook 07, 10 Padding issue fix */
			table td
			{{
                border - collapse: collapse;
			}}
            .ssrContent{{
                font - size: 12px;
            }}
			/* Remove spacing around Outlook 07, 10 tables */
			table
			{{ /*border-collapse:collapse;*/
                mso - table - lspace: 0pt;
				mso-table-rspace: 0pt;
			}}

			a
			{{
                color: #2172ba;
				text-decoration: none;
			}}

			a:hover {{
                color: #2172ba;
				text-decoration: underline;
			}}

			.ad-width{{
                width: 275px !important;
			}}
			/*.non-mobile-image, .non-mobile-email{{
                display:block !important;
			}}*/
			td[class=""mobile-email""], td[class=""mobile-image""]

            {{
                    display: none !important;
                    max - height: 0 !important;
                    width: 0 !important;
                    font - size: 0 !important;
                    line - height: 0 !important;
                    padding: 0 !important;
                    mso - hide: all; /* hide elements in Outlook 2007-2013 */
                }}
                /*div[class=""mobile-mp-card-info""] {{

                        margin:0 !important;
                        padding:90px 0 0 0 !important;                
                    }}*/
                /* Apple Mail to limit that table's size on desktop*/
                @media screen and(min - width: 600px) {{

                .container {{
                        width: 601px!important;
                    }}
                }}
                @media only screen and(max-width: 525px)
			{{
                    div[class= ""mobile - mp - card - section""] {{
                    padding:0 !important;
                    margin:-25px 0 0 -150px !important;
                }}
}}
/* More Specific Targeting */
/* MOBILE TARGETING 480px */
@media only screen and(min-width: 480px)
{{

    /*table[class=""table""], td[class=""cell""]
    {{
        width: 304px !important;
    }}*/

    td[class= ""mobile - ad - spacing""]
			{{
				padding-bottom: 15px !important;
			}}
                td[class=""mobile-image""]
			{{
				display: inline-block !important;
			}}
             
			td[class=""non-mobile-image""] /*, .non-mobile-email*/
			{{
				display: none !important;
				max-height: 0 !important;
				width: 0 !important;
				font-size: 0 !important;
				line-height: 0 !important;
				padding: 0 !important;
				/*mso-hide: all;  hide elements in Outlook 2007-2013 */
			}}
			/*.mobile-email{{
				display:block !important;
			}}*/
			tr[class=""mobile-footer""]
			{{
				background-color: #ffffff !important;
			}}

				body[class=""mobile-body""]
				{{
					background-color: #ffffff !important;
				}}
				div[class=""navigation""] {{
					width:100% !important;
				}}
				div[class=""navigation""] a {{
					display:block !important;
					text-decoration:none !important;
					text-align:center !important;
					font-size:16px !important;
				}}
				/*Controlling phone number linking for mobile. */
				a[href ^= ""tel""], a[href ^= ""sms""]
				{{
					text-decoration: none;
					color: #2172ba;
					pointer-events: none;
					cursor: default;
				}}

				.mobile_link a[href ^= ""telephone""], .mobile_link a[href ^= ""sms""]
				{{
					text-decoration: none;
					color: #2172ba !important;
					pointer-events: auto;
					cursor: default;
				}}

				a[class=""mobile-show""], a[class=""mobile-hide""]
				{{
					display: block !important;
					color: #2172ba !important;
					border-radius: 20px;
					padding: 0 8px;
					text-decoration: none;
					font-weight: bold;
					font-family: ""Helvetica Neue"", Helvetica, sans-serif;
					font-size: 11px;
					position: absolute;
					top: 25px;
					right: 10px;
					text-align: center;
					width: 40px;
				}}
				div[class=""ad-width""]{{
					width:300px !important;
				}}
            }}
			@media only screen and(min-device-width: 768px) and(max-device-width: 1024px)
{{
    /*ipad (tablets, smaller screens, etc) */
    a[href ^= ""tel""],a[href ^= ""sms""]

                {{
        text-decoration: none;
        color: #2172ba;
					pointer-events: none;
        cursor: default;
    }}

				.mobile_link a[href ^= ""tel""], .mobile_link a[href ^= ""sms""]
                {{
        text - decoration: default;
        color: #2172ba !important;
					pointer - events: auto;
        cursor: default;
    }}
}}

@media only screen and(-webkit-min-device-pixel-ratio: 2)
{{
    /*iPhone 4g styles */
    tr[class= ""non-mobile-image""],img[class= ""non - mobile - image""], table[class=""non-mobile-email""]
				{{
					display: none !important;
					max-height: 0;
					width: 0;
					font-size: 0;
					line-height: 0;
					padding: 0;
					mso-hide: all; /* hide elements in Outlook 2007-2013 */
				}}

    </style>

<body style=""padding: 0; margin: 0; -webkit-text-size-adjust: none; -ms-text-size-adjust: 100%; background-color: #e6e6e6;"" class=""mobile-body"">
		<!--[if (gte mso 9)|(IE)]>
                
		<![endif]-->
		<table width=""100%"" cellpadding=""20"" cellspacing=""0"" border=""0"" style=""max-width:600px; background-color:white""><div>
            <tr>
                <td>
                    <img src=""{_appConfig.AppLogo}"" />
                    <h1>{_appConfig.AppName}</h1>
                    <h2>{subject}</h2>
                    <div style=""width:550px"">
                    {body}
                    </div>
                </td>
            </tr>
        </table>
    <p>Please do not reply to this email as it is an unmonitored address.</p>
    <a href=""mailto:[email protected]"">Contact Support</a>

</div>
</body>
</html>";

                var msg = new MimeMessage()
                {
                    Subject = subject,
                    Body    = new TextPart("html", body),
                };

                msg.To.Add(new MailboxAddress(email));
                msg.From.Add(new MailboxAddress(_settings.SmtpFrom));

                using (var client = new SmtpClient())
                {
                    await client.ConnectAsync(_settings.SmtpServer.Uri.ToString(), 587, false);

                    await client.AuthenticateAsync(_settings.SmtpServer.UserName, _settings.SmtpServer.Password);

                    await client.SendAsync(msg);

                    await client.DisconnectAsync(true);
                }

                _adminLogger.AddCustomEvent(Core.PlatformSupport.LogLevel.Verbose, "SendGridEmailServices_SendAsync", "EmailSent",
                                            new System.Collections.Generic.KeyValuePair <string, string>("Subject", subject),
                                            new System.Collections.Generic.KeyValuePair <string, string>("to", email));

                return(InvokeResult.Success);
            }
            catch (Exception ex)
            {
                _adminLogger.AddException("SendGridEmailServices_SendAsync", ex,
                                          new System.Collections.Generic.KeyValuePair <string, string>("Subject", subject),
                                          new System.Collections.Generic.KeyValuePair <string, string>("to", email));

                return(InvokeResult.FromException("SendGridEmailServices_SendAsync", ex));
            }
        }