Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
I have downloaded the Release candidate and I see the Azure Keyvault configuration has changed :
Configuration": { "AzureKeyVault": { "IsEnabled": "false", "KeyVaultName": "" }
Where are we going to put the clientid and clientsecret ?
Previous configuration :
"Configuration": { "AzureKeyVault": { "IsEnabled": "false", "KeyVaultName": "xxx", "AzureADApplicationId": "", "AzureADCertThumbprint": "", "ClientId": "", "ClientSecret": "" }
Google is migrating to the Google Identity Services. Can you please suggest what changes we need to do by following this document :
https://developers.google.com/identity/oauth2/web/guides/migration-to-gis#gapi-callback
You guys have to make these changes as the latest code will not work with a Google App which has been created newly. They are kind of forcing to move to Google Identity Services and will deprecate completely on 2023.
Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
Problem Trying to Solve : I have configured a Tenant with its own Openid connect information i.e ClientId LoginUrl AuthorityURl
Now I want to consume my API from a different Application who uses the same OpenID info as above.
When I uses Postman and call the ExternalAuthenticate API and passes the following :
Abp.TenantID - Header
Body :
{"authProvider":"OpenIdConnect","providerKey":"xxxxxxxxF8Z356","providerAccessCode":"eyJraWQiOiJaUHpRQlBEOGRSemZLM0FzWFU2ZFZHsUmVnaW9uIjoiVVMsQ2FuYWRhIn0.lrRRCAI3yhZ5aR66Qs5RZLLsjSoknJpuvfiwzb0vvwR35FIZ4Lj_MvaTGdXG9giDWDx2QxR5_LeMkMbeXqjcBZ6wF1UUXkqqa7aOzO13G-OYM_X3ftpvwKoiwXAjmUewsM5pZWjo4s5fsxj6ms6XWy5J9Xri-yI9lGvcFPCdXUPa5hoWmprW3G_kd17ApLkvg4SzFsSQCU7h7dP5MYcj1fi79AiKLJjFZG9hK8h21Gw8GZeyJHzTHoiYxq5No24NT5_urjkiKM8tYhNggLWggexEFIVs7wvBT_MWOV9kQA8CTzGFBvnyRfjxHsD6CM4l7K1-cUkycVnED0GYIAg9gA","returnUrl":null,"singleSignIn":false}
When I pass the id_token from the other app to the APi endpoint my system is able to validate the Token.
However if I pass the accesstoken from the other app to the API endpoint my system is throwing Signature mismatch error.
I was under the assumption the system could validate the id_token and access token, but it doesn't work that way.
Anyways to solve this issue where i can validate the access token.
Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
I am trying to link users based on Provider Key and Login provider. Below is my code :
private async Task LinkUsers(ExternalAuthUserInfo externalUserInfo, User user)
{
//Check if the user account has been linked
if (!_userAccounts.GetAll().Any(p =>
p.UserLinkId.HasValue && p.UserId == user.Id &&
p.TenantId == user.TenantId))
{
//if not linked then try to link
using (_unitOfWorkProvider.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
//This needs to be joined so that we don't pick the deleted record in User Account tables
var useraccountstobeConnected = from useraccounts in _userAccounts.GetAll()
join userlogin in _userLogins.GetAll() on
new { A = useraccounts.UserId, B = useraccounts.TenantId } equals new
{ A = userlogin.UserId, B = userlogin.TenantId }
where (userlogin.LoginProvider == externalUserInfo.Provider &&
userlogin.ProviderKey == externalUserInfo.ProviderKey &&
userlogin.TenantId != user.TenantId)
select new
{
useraccounts.TenantId,
useraccounts.UserId
};
using var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew);
foreach (var useraccount in useraccountstobeConnected)
{
using (_unitOfWorkProvider.Current.SetTenantId(useraccount.TenantId))
{
var seconduser = await _userManager.FindByIdAsync(useraccount.UserId.ToString());
if (seconduser != null && seconduser.IsActive)
await _userLinkManager.Link(user, seconduser);
}
}
await uow.CompleteAsync();
}
}
}
I am calling this in 2 places :
case AbpLoginResultType.Success: // Link the users if not linked await LinkUsers(externalUser, loginResult.User) case AbpLoginResultType.UnknownExternalLogin: In this case await LinkUsers(externalUser, loginResult.User) doesnt work as the record is not available in the user account table.
Then I tried something like this :
_unitOfWorkManager.Current.Completed += (sender, args) => { LinkUsers(externalUser, loginResult.User) }
This doesn't work either. Can you suggest a way to link users right after registration.
Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
What is ABP Framework version? Whatever comes with 10.3
I am trying to use AWS Parameter store as an alternative to store my appsettings. Below is the change I made in Program.cs :
return new WebHostBuilder()
.UseKestrel(opt =>
{
opt.AddServerHeader = false;
opt.Limits.MaxRequestLineSize = 16 * 1024;
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIIS()
.UseIISIntegration()
.ConfigureAppConfiguration(builder =>
{
builder.AddJsonFile("appsettings.json");
builder.AddEnvironmentVariables();
var environmentName = (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development").ToLower();
var platformName = "capspayone";
var awsParameterProcessor = new AWSParameterProcessor();
builder.AddSystemsManager((source) =>
{
source.Path = $"/cnc/{platformName}/{environmentName}";
//source.AwsOptions = awsOptions;
source.ParameterProcessor = awsParameterProcessor;
});
})
.UseStartup<Startup>();
}
public class AWSParameterProcessor : IParameterProcessor
{
public virtual bool IncludeParameter(Parameter parameter, string path) => true;
public virtual string GetKey(Parameter parameter, string path)
{
var res = parameter.Name.Substring(path.Length).TrimStart('/').Replace("--", ConfigurationPath.KeyDelimiter);
return res;
}
public virtual string GetValue(Parameter parameter, string path) => parameter.Value;
}
I am unable to get the settings from AWS. Am I doing anything wrong ?
Also I tried[https://aws.amazon.com/blogs/developer/net-core-configuration-provider-for-aws-systems-manager/]
Please help.
Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
I am trying to consume a web api and below is what I have implemented :
Startup.cs
services.AddTransient<HtgAuthenticationDelegatingHandler>();
services.AddHttpClient<IMyServiceManager,MyServiceManager>(client =>
{
client.BaseAddress = new Uri(_appConfiguration["cccccccc"]);
client.DefaultRequestHeaders.Add("User-Agent", "myPortal");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache");
})
//.AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler<MyAuthenticationDelegatingHandler>(); // This handler is on the inside, closest to the request.
public class MyAuthenticationDelegatingHandler : DelegatingHandler
{
private readonly TokenManager _tokenManager;
private readonly ICacheManager _cacheManager;
public HtgAuthenticationDelegatingHandler(TokenManager tokenManager, ICacheManager cacheManager)
{
_tokenManager = tokenManager;
_cacheManager = cacheManager;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var htgtoken = await _tokenManager.GetMyToken();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", htgtoken);
try
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode != HttpStatusCode.Unauthorized &&
response.StatusCode != HttpStatusCode.Forbidden)
return response;
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", await _tokenManager.GetMyToken());
response = await base.SendAsync(request, cancellationToken);
return response;
}
catch (Exception e)
{
string exception = e.Message;
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = e.Message
};
}
}
}
public class TokenManager : ITransientDependency
{
private readonly IAmazonSecretsManager _secretManager;
public IConfiguration Configuration { get; }
private readonly IConfigurationRoot _appConfiguration;
private readonly ICacheManager _cacheManager;
public TokenManager(IAmazonSecretsManager secretManager, IConfiguration configuration,
IAppConfigurationAccessor configurationAccessor, ICacheManager cacheManager)
{
_secretManager = secretManager;
Configuration = configuration;
_cacheManager = cacheManager;
_appConfiguration = configurationAccessor.Configuration;
}
public async Task<string> GetHTGToken()
{
return await UpdateHtgTokenCacheAsync();
}
public static bool IsValidJwtToken(string accesstoken)
{
var tokenExpiryDate = JWTTokenExpiryTime(accesstoken);
return tokenExpiryDate > DateTime.UtcNow;
}
public static DateTime JWTTokenExpiryTime(string token)
{
var handler = new JwtSecurityTokenHandler();
if (!(handler.ReadToken(token) is JwtSecurityToken tokenread)) return DateTime.MinValue;
return tokenread.ValidTo;
}
private async Task<string> UpdateMyTokenCacheAsync()
{
//Get it from the app settings
var request = new HttpRequestMessage(HttpMethod.Post, _appConfiguration["Api:My:TokenEndPoint"]);
var clientId = xxxx;
var secret = yyyy;
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.CacheControl = CacheControlHeaderValue.Parse("no-cache");
request.Headers.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{clientId}:{secret}")));
request.Content = new StringContent($"grant_type=client_credentials&scope=manage:all",
System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
var client = new HttpClient();
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
var token = JObject.Parse(body)["access_token"]?.ToString();
var tokencache = _cacheManager.GetCache("tokencache");
try
{
await tokencache.SetAsync(new KeyValuePair<string, object>[]
{
new KeyValuePair<string, object>("mytoken", token),
}, null, JWTTokenExpiryTime(token));
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return token;
}
}
public class MyServiceManager : MyDomainServiceBase, IMyServiceManager
{
private readonly HttpClient _httpClient;
public MyServiceManager(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> GetStates()
{
try
{
var response = await _httpClient.GetAsync(
"state?additionalProp1=&additionalProp2=&additionalProp3=");
return response;
}
catch (Exception ex)
{
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = ex.Message
};
}
}
}
<br> I am basically getting the token from an authentication server , storing that token and getting the token again if it has expired.
Eveyrthing works fine for the first time but my http client is getting disposed on the second time.
Any idea?
Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.
I have been using New Relics for Application Monitoring, now I would like to use their Log feature , I would like to export the log that's being generated to New Relics.
Below is a documentation I am referring :
https://docs.newrelic.com/docs/logs/enable-log-management-new-relic/logs-context-net/net-configure-log4net https://github.com/newrelic/newrelic-logenricher-dotnet/tree/master/src/Log4Net/NewRelic.LogEnrichers.Log4Net
The idea is to move the logs from the server to New Relics for further analysis.
I need some help and guidance to configure Abp Log4Net config file to make it work.
Secondly I would like to export the Audit Log information to a AuditLog.txt using Log4Net and send it to New Relics to store in their Logs.
I was trying to add the Volo.Abp.BlobStoring to by Aspnetzero project speciffically Application project and I am encountering this error :
Severity Code Description Project File Line Suppression State Error CS0121 The call is ambiguous between the following methods or properties: 'System.AbpStringExtensions.IsNullOrWhiteSpace(string)' and 'Abp.Extensions.StringExtensions.IsNullOrWhiteSpace(string)' XXXX.YYYY.Application \XXXX.YYYY.Application\Configuration\Host\HostSettingsAppService.cs 308 Active.
Also can you guide me how to integrate it for multi tenancy so that each tenant can configure their own provider, something like the way you did for External providers.
I am using ASPNET Zero .net core and angular to build an admin tool to control more than 2000 databases. All databases are in MySQL and I am basically creating one database per tenant. The host database and the tenant databases are in different servers.
My issue is I create multiple background jobs to handle certain type of things.
E.g. Let's say user clean up, so basically I have a job which looks for an email address in host databases and comb through each of the 2000 databases and delete the user from every database. The problem is while doing so I am ending up creating 2000 threads across the MySQL server and most of them goes to sleep. MYSql server then timesout and the whole database is crashing
namespace ABC.BackgroundJobs
{
public class CreateUserToAllShowsJob : BackgroundJob<CreateUserInShowsJobArgs>, ITransientDependency
{
private readonly UserManager _userManager;
private readonly IUserJobStateNotifier _userJobStateNotifier;
private readonly IBackgroundJobEmailer _backgroundJobEmailer;
private readonly MySQLUserManager _mySQLUserManager;
private readonly IRepository<Tenant> _tenantRepository;
private readonly IRepository<UserGroupOrganization> _usergrouporganizationsRepository;
private readonly IRepository<UserAccount, long> _userAccountRepository;
private readonly RoleManager _roleManager;
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IRepository<UserGroup> _usergroupsRepository;
private readonly IRepository<Role> _roleRepository;
private readonly IIocResolver _iIocResolver;
private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
public CreateUserToAllShowsJob(IUserJobStateNotifier userJobStateNotifier,
IBackgroundJobEmailer backgroundJobEmailer,
MySQLUserManager mySQLUserManager,
UserManager userManager,
IRepository<Tenant> tenantRepository,
IRepository<UserGroupOrganization> usergrouporganizationsRepository,
IRepository<UserAccount, long> userAccountRepository,
RoleManager roleManager,
IUnitOfWorkManager unitOfWorkManager,
IRepository<UserGroup> usergroupsRepository,
IRepository<Role> roleRepository,
IIocResolver iIocResolver,
IRepository<OrganizationUnit, long> organizationUnitRepository
)
{
_userManager = userManager;
_userJobStateNotifier = userJobStateNotifier;
_backgroundJobEmailer = backgroundJobEmailer;
_mySQLUserManager = mySQLUserManager;
_tenantRepository = tenantRepository;
_usergrouporganizationsRepository = usergrouporganizationsRepository;
_userAccountRepository = userAccountRepository;
_roleManager = roleManager;
_unitOfWorkManager = unitOfWorkManager;
_usergroupsRepository = usergroupsRepository;
_roleRepository = roleRepository;
_iIocResolver = iIocResolver;
_organizationUnitRepository = organizationUnitRepository;
}
[UnitOfWork]
[Audited]
public override void Execute(CreateUserInShowsJobArgs args)
{
var hostUser = _userManager.FindByIdAsync(args.ActionRequestedBy).Result;
var newUser = _userManager.FindByIdAsync(args.ÚserId).Result;
try
{
AsyncHelper.RunSync(() => AddUserToAllShows(newUser, args.UserGroupIds, args.AssignedRoleNames, args.OrganizationIds, hostUser));
}
catch (Exception exception)
{
// sends notification
_userJobStateNotifier.AddUserToAllShowsAsync(new BackgroundJobStatusDto()
{
Message = exception.Message,
JobName = LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToAllShowsJob"),
}, Abp.Notifications.NotificationSeverity.Error).Wait();
// sends email
if (!ReferenceEquals(hostUser, null) && !hostUser.EmailAddress.IsNullOrEmpty())
{
// sends email
_backgroundJobEmailer.SendBackgroundJobFailInfo(new BackgroundJobEmailInput()
{
EmailAddress = hostUser.EmailAddress,
Message = exception.Message,
JobName = LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToAllShowsJob"),
JobFailedDateTime = DateTime.UtcNow
});
}
// logs the exception
Logger.Error(exception.Message, exception);
throw exception;
}
}
private async Task AddUserToAllShows(User input, List<int> UserGroups, List<string> AssignedRoleNames, List<long> OrganizationUnits, User hostUser)
{
if (!ReferenceEquals(UserGroups, null) && UserGroups.Any())
{
var hostUserRoles = AssignedRoleNames.ToList();
var tenants = await (from tenant in _tenantRepository.GetAll().Where(p => OrganizationUnits.Contains(p.OrganizationUnitId.Value))
join usergrporg in _usergrouporganizationsRepository.GetAll().Where(p => UserGroups.Contains(p.UserGroupId)) on
tenant.OrganizationUnitId equals usergrporg.OrganizationId
join useracc in _userAccountRepository.GetAll().Where(p => p.UserName == input.UserName) on
tenant.Id equals useracc.TenantId
into userAccount
from userAccounts in userAccount.DefaultIfEmpty()
select new { tenantId = tenant.Id, userAccounts.UserName, tenant.OrganizationUnitId, tenant.TenancyName }).ToListAsync();
var userGroupRoleIds = await _usergroupsRepository.GetAll().Where(p => UserGroups.Contains(p.Id))
.Select(p => p.RoleId.Value).Distinct().ToListAsync();
var roleDisplayNames = new List<string>();
if (userGroupRoleIds.Any())
{
var roles = await _roleRepository.GetAll().Where(p => userGroupRoleIds.Contains(p.Id)).ToListAsync();
hostUserRoles = roles.Select(p => p.DisplayName).ToList();
}
else
{
//from UI Role NAmes will passs, By using role name we are getting Displaynames
var roles = await _roleRepository.GetAll().Where(p => hostUserRoles.Contains(p.Name)).Select(p => p.DisplayName).ToListAsync();
hostUserRoles = roles;
}
foreach (var role in hostUserRoles)
{
roleDisplayNames.Add(_roleManager.GetTrimmedRoleName(role));
}
var user = await _userManager.FindByNameAsync(input.UserName) ??
await _userManager.FindByEmailAsync(input.EmailAddress);
var orgIds = tenants.Where(p => p.UserName == null).Select(p => p.OrganizationUnitId);
if (orgIds.Any())
{
bool isException = false;
var orgNames = await _organizationUnitRepository.GetAll().Where(p => orgIds.Contains(p.Id)).Select(p => p.DisplayName).ToListAsync();
await _userJobStateNotifier.AddUserToAllShowsAsync(new BackgroundJobStatusDto()
{
Message = $"{ LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToAllShowsJob")}:" +
$"{string.Format(LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "Addtoshowsjobstarted"), input.UserName, string.Join(',', orgNames))}",
}, Abp.Notifications.NotificationSeverity.Info);
foreach (var tenant in tenants.Where(p => p.UserName == null).Distinct())
{
using (var uowForAdduser = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
{
try
{
using (_unitOfWorkManager.Current.SetTenantId(tenant.tenantId))
{
//Copy the details
var userinputforTenant = new UserEditDto();
userinputforTenant.Name = user.Name;
userinputforTenant.Surname = user.Surname;
userinputforTenant.UserName = user.UserName;
userinputforTenant.EmailAddress = user.EmailAddress;
userinputforTenant.PhoneNumber = user.PhoneNumber;
userinputforTenant.Password = user.Password;
var createUserinput = new CreateOrUpdateUserInput
{
User = userinputforTenant,
SetRandomPassword = true,
AssignedRoleNames = assignedRoleNames.ToArray(),
OrganizationUnits = OrganizationUnits,
SendActivationEmail = false,
DbTemplates = new List<int>() { }
};
using (var _userAppService = _iIocResolver.ResolveAsDisposable<UserAppService>())
{
await _userAppService.Object.CreateInternalUserAsync(createUserinput);
}
//await CreateInternalUserAsync(createUserinput);
}
await _unitOfWorkManager.Current.SaveChangesAsync();
await _mySQLUserManager.GrantPrivilegeToUserForShow(new MySqlUserPrivilegeInput()
{
TenantId = tenant.tenantId,
UserName = input.UserName,
Password = SimpleStringCipher.Instance.Decrypt(user.PasswordForMysqlUser)
});
}
catch (Exception exception)
{
isException = true;
var msg = $"{string.Format(LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToShowExceptionMessage"), tenant.TenancyName, orgNames)} {exception.Message}";
// sends notification
await _userJobStateNotifier.AddUserToAllShowsAsync(new BackgroundJobStatusDto()
{
Message = msg,
}, Abp.Notifications.NotificationSeverity.Error);
// sends email
if (!hostUser.EmailAddress.IsNullOrEmpty())
{
await _backgroundJobEmailer.SendBackgroundJobFailInfo(new BackgroundJobEmailInput()
{
EmailAddress = hostUser.EmailAddress,
Message = msg,
JobName = LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToAllShowsJob"),
JobFailedDateTime = DateTime.UtcNow
});
}
Logger.Error(msg, exception);
}
finally
{
await uowForAdduser.CompleteAsync();
}
}
}
if (isException)
{
throw new UserFriendlyException(string.Format(LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToAllShowsJobFailedReinitiateMesage"),
user.Name, string.Join(',', orgNames)));
}
await _userJobStateNotifier.AddUserToAllShowsAsync(new BackgroundJobStatusDto()
{
Message = $"{ LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "CreateUserToAllShowsJob")}:" +
$"{string.Format(LocalizationHelper.GetString(ABCConsts.LocalizationSourceName, "AddtoshowsjobCompletd"), input.UserName, string.Join(',', orgNames))}",
}, Abp.Notifications.NotificationSeverity.Info);
}
}
}
}
}
The code more or less looks like the above. Can you help me understand how to execute job which can safely go through each of the database and do something. Any example should help.
We use Pomona for MySQl.We have a requirement to keep the collation to utf8mb4_unicode_ci. So we went ahead and used the dataannotation to make sure all columns have the same unicode.
Here are the issues :
migrationBuilder.Sql("alter database character set utf8mb4 collate utf8mb4_unicode_ci");
This only makes the non varchar column to automatically use the collation as utf8mb4_unicode_cikai_ci. So in order to change the collation of these tables either i have to change the initial migration which i am not a big fan of it or go and change the entity with override.Can you suggest what would be a good approach to solve this issue ?