Hi,
ABP already has a built-in system for managing 2FA through settings, which can be configured globally or per tenant:
// In Account Admin UI
await SettingManager.SetForCurrentTenantAsync(IdentityProSettingNames.TwoFactor.Behaviour, input.TwoFactorBehaviour.ToString());
We already set is per each tenant. So you can manage it through settings.
Google Authenticator is already supported built-in, you do not have to take any action: https://abp.io/docs/latest/modules/identity/two-factor-authentication#verification-providers
Just enable it and start using.
Remember, users should have a verified e-mail or phone number to activate 2FA.
As a second question, to SMS integration ABP has an infrastructure for it and has an implementation with Twilio: https://abp.io/docs/latest/modules/twilio-sms
But if you wish to use Infobip you should implment it on your own:
Volo.Abp.Sms nuget package to your project:abp add-package Volo.Abp.Sms
InfobipSmsSender that implements ISmsSender[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ISmsSender))]
public class InfobipSmsSender : ISmsSender, ITransientDependency
{
private readonly IConfiguration _configuration;
private readonly HttpClient _httpClient;
public InfobipSmsSender(IConfiguration configuration, IHttpClientFactory httpClientFactory)
{
_configuration = configuration;
_httpClient = httpClientFactory.CreateClient("Infobip");
}
public async Task SendAsync(SmsMessage smsMessage)
{
var baseUrl = _configuration["Infobip:BaseUrl"];
var apiKey = _configuration["Infobip:ApiKey"];
var sender = _configuration["Infobip:Sender"];
var requestBody = new
{
messages = new[]
{
new
{
from = sender,
destinations = new[] { new { to = smsMessage.PhoneNumber } },
text = smsMessage.Text
}
}
};
var request = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/sms/2/text/advanced")
{
Content = new StringContent(JsonSerializer.Serialize(requestBody), Encoding.UTF8, "application/json")
};
request.Headers.Add("Authorization", $"App {apiKey}");
await _httpClient.SendAsync(request);
}
}
{
"Infobip": {
"BaseUrl": "https://api.infobip.com",
"ApiKey": "your-api-key",
"Sender": "YourApp"
}
}
Account Pro module uses ISmsSender interface to send Security Code and Confirmation Code, so implementing this interface is enough to make it work.
Hi,
You use [AllowAnonymous] attribute on your controller but it seems you're using another appservices in your controller. Each appservice checks their own permissions. _userAppService field in your code is probably injected as IUserAppService and that app service has their own [Authorize] attribute or permission checks in their methods. If you want to bypass permission checks, you may want to use Repositories directly inside application services.
You can see it has its own permission to execute method: https://github.com/abpframework/abp/blob/af1e92c5aff2d7ad9991fd46a0f2eed4bf4f559c/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs#L160
Hi,
I checked the File-management module and found some ways to do it.
If you have access to the source code, I can** recommend to you downloading the source** code and take a look at to source code to find another implementation that fits your requirements better.
As I understand the case, the following way should work:
Looking at the code, the key components we need to modify are:
FileDescriptorAppService which uses IBlobContainer<FileManagementContainer>DirectoryDescriptorAppService which manages the directory structureusing System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace AbpSolution;
public interface IBlobContainerSelector
{
Task<string> GetCurrentContainerNameAsync();
}
public class YourBlobContainerSelector : IBlobContainerSelector, ITransientDependency
{
private readonly ICurrentUser _currentUser;
private readonly ICurrentTenant _currentTenant;
public YourBlobContainerSelector(
ICurrentUser currentUser,
ICurrentTenant currentTenant)
{
_currentUser = currentUser;
_currentTenant = currentTenant;
}
public async Task<string> GetCurrentContainerNameAsync()
{
// Implement your logic to select the container
// This could be based on user selection, tenant, or other criteria
return "selected-container-name";
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace AbpSolution;
public interface ICustomBlobContainerFactory
{
Task<IBlobContainer> GetContainerAsync();
}
public class CustomBlobContainerFactory : ICustomBlobContainerFactory
{
private readonly IBlobContainerSelector _containerSelector;
private readonly IBlobContainerFactory _blobContainerFactory;
public CustomBlobContainerFactory(
IBlobContainerSelector containerSelector,
IBlobContainerFactory blobContainerFactory)
{
_containerSelector = containerSelector;
_blobContainerFactory = blobContainerFactory;
}
public async Task<IBlobContainer> GetContainerAsync()
{
var containerName = await _containerSelector.GetCurrentContainerNameAsync();
return _blobContainerFactory.Create(containerName);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
using Volo.Abp.Features;
using Volo.Abp.ObjectExtending;
using Volo.FileManagement.Authorization;
using Volo.FileManagement.Files;
namespace Volo.FileManagement.Directories;
[RequiresFeature(FileManagementFeatures.Enable)]
[Authorize(FileManagementPermissions.DirectoryDescriptor.Default)]
public class DirectoryDescriptorAppService : FileManagementAppService, IDirectoryDescriptorAppService
{
protected IDirectoryManager DirectoryManager { get; }
protected IDirectoryDescriptorRepository DirectoryDescriptorRepository { get; }
protected IFileManager FileManager { get; }
protected IFileDescriptorRepository FileDescriptorRepository { get; }
protected FileIconOption FileIconOption { get; }
public DirectoryDescriptorAppService(
IDirectoryManager directoryManager,
IFileManager fileManager,
IDirectoryDescriptorRepository directoryDescriptorRepository,
IFileDescriptorRepository fileDescriptorRepository,
IOptions<FileIconOption> fileIconOption)
{
DirectoryManager = directoryManager;
FileManager = fileManager;
DirectoryDescriptorRepository = directoryDescriptorRepository;
FileDescriptorRepository = fileDescriptorRepository;
FileIconOption = fileIconOption.Value;
}
public virtual async Task<DirectoryDescriptorDto> GetAsync(Guid id)
{
var directoryDescriptor = await DirectoryDescriptorRepository.GetAsync(id);
return ObjectMapper.Map<DirectoryDescriptor, DirectoryDescriptorDto>(directoryDescriptor);
}
public virtual async Task<ListResultDto<DirectoryDescriptorInfoDto>> GetListAsync(Guid? parentId)
{
var subDirectories = await DirectoryDescriptorRepository.GetChildrenAsync(parentId);
var result = new List<DirectoryDescriptorInfoDto>();
foreach (var subDirectory in subDirectories)
{
result.Add(new DirectoryDescriptorInfoDto
{
Name = subDirectory.Name,
Id = subDirectory.Id,
ParentId = subDirectory.ParentId,
HasChildren = await DirectoryDescriptorRepository.ContainsAnyAsync(subDirectory.Id, false)
});
}
return new ListResultDto<DirectoryDescriptorInfoDto>(result);
}
[Authorize(FileManagementPermissions.DirectoryDescriptor.Create)]
public virtual async Task<DirectoryDescriptorDto> CreateAsync(CreateDirectoryInput input)
{
var directoryDescriptor = await DirectoryManager.CreateAsync(input.Name, input.ParentId, CurrentTenant.Id);
input.MapExtraPropertiesTo(directoryDescriptor);
await DirectoryDescriptorRepository.InsertAsync(directoryDescriptor);
return ObjectMapper.Map<DirectoryDescriptor, DirectoryDescriptorDto>(directoryDescriptor);
}
[Authorize(FileManagementPermissions.DirectoryDescriptor.Update)]
public virtual async Task<DirectoryDescriptorDto> RenameAsync(Guid id, RenameDirectoryInput input)
{
var directory = await DirectoryDescriptorRepository.GetAsync(id);
directory.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
await DirectoryManager.RenameAsync(directory, input.Name);
await DirectoryDescriptorRepository.UpdateAsync(directory);
return ObjectMapper.Map<DirectoryDescriptor, DirectoryDescriptorDto>(directory);
}
public virtual async Task<PagedResultDto<DirectoryContentDto>> GetContentAsync(DirectoryContentRequestInput input)
{
var result = new List<DirectoryContentDto>();
var subDirectoryCount = await DirectoryDescriptorRepository.GetChildrenCountAsync(input.Id, input.Filter);
var subFileCount = await FileDescriptorRepository.CountDirectoryFilesAsync(input.Id, input.Filter);
// directory can be orderable for only its name
var directorySorting =
input.Sorting?.IndexOf("name asc", StringComparison.OrdinalIgnoreCase) >= 0 ?
"name asc" :
input.Sorting?.IndexOf("name desc", StringComparison.OrdinalIgnoreCase) >= 0 ?
"name desc" :
null;
var subDirectories = await DirectoryDescriptorRepository.GetChildrenAsync(input.Id, input.Filter, directorySorting, input.MaxResultCount, input.SkipCount);
result.AddRange(ObjectMapper.Map<List<DirectoryDescriptor>, List<DirectoryContentDto>>(subDirectories));
if (await AuthorizationService.IsGrantedAsync(FileManagementPermissions.FileDescriptor.Default))
{
var fileSkipCount = input.SkipCount <= subDirectoryCount ? 0 : input.SkipCount - subDirectoryCount;
var fileMaxResultCount = input.MaxResultCount - subDirectories.Count;
var subFiles = await FileDescriptorRepository.GetListAsync(input.Id, input.Filter, input.Sorting, fileMaxResultCount, fileSkipCount);
var subFilesDto = ObjectMapper.Map<List<FileDescriptor>, List<DirectoryContentDto>>(subFiles);
foreach (var fileDto in subFilesDto)
{
fileDto.IconInfo = FileIconOption.GetFileIconInfo(fileDto.Name);
result.Add(fileDto);
}
}
return new PagedResultDto<DirectoryContentDto>(subDirectoryCount + subFileCount, result);
}
[Authorize(FileManagementPermissions.DirectoryDescriptor.Delete)]
public virtual async Task DeleteAsync(Guid id)
{
await DirectoryManager.DeleteAsync(id);
}
[Authorize(FileManagementPermissions.DirectoryDescriptor.Update)]
public virtual async Task<DirectoryDescriptorDto> MoveAsync(MoveDirectoryInput input)
{
var directory = await DirectoryDescriptorRepository.GetAsync(input.Id);
directory.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
await DirectoryManager.MoveAsync(directory, input.NewParentId);
await DirectoryDescriptorRepository.UpdateAsync(directory);
return ObjectMapper.Map<DirectoryDescriptor, DirectoryDescriptorDto>(directory);
}
}
Configure<AbpBlobStoringOptions>(options =>
{
// Configure your blob containers
options.Containers.Configure("container1", container =>
{
container.UseDatabase();
// Add other configuration as needed
});
options.Containers.Configure("container2", container =>
{
container.UseAzure(options => { /* ... */});
// Add other configuration as needed
});
// ...and more...
});
Hi,
Can you share how you configured your Background Jobs in your each application? Including .Web and .Web_Int
Normally, if you disable job execution, it shouldn't even be triggered. It seems some misconfiguration.
Hi,
Can you share your Node.js, Yarn and npm versions?
# get node version
node -v
# get yarn version
yarn -v
# get npm version
npm -v
Check if your node & yarn version matches with Pre-requirements documentation: https://abp.io/docs/latest/get-started/pre-requirements#node-js-and-yarn
ABP uses classic yarn, which is Yarn v1.22+. Make sure this is correct.
Hi,
Some new templates don't have ABP Suite support right now.
Here 2 approaches you can follow:
You can always download source codes with ABP CLI with the following command:
.\account-pro-source folder:abp get-source Volo.Abp.Account.Pro -o .\account-pro-source
abp add-source-code Volo.Abp.Account.Pro --add-to-solution-file
Downloads the source into
modulesfolder and adds local reference to your project by replacing nuget references
Follow;
ABP has an option for making ExchangeType as Fanout but it applies globally for the application. That means you can make it for all the events, not a particular event.
Configure<AbpRabbitMqEventBusOptions>(options =>{
options.ExchangeType = ExchangeType.Fanout;
});
You'll need to configure it both published and subscriber applications.
Hi,
It's by design. The event handler is executed only once to prevent concurrent write operations at the same time.
It's the defaults of RabbitMQ. It uses kinda load-balancing operation across the subscribers and sends the event to only one single instance and awaits acknowledge (ACK) info to complete the event. If not, tries to send it again. It's the expected behaviour.
If your intent is for all instances to receive the event (for example, for broadcasting purposes), you’ll need to change the design slightly. A common approach is to use a publish/subscribe pattern with a fanout exchange. In this configuration, each service instance can have its own dedicated queue bound to the fanout exchange. Every time a message is published to the exchange, each bound queue gets its own copy of the message, and thus every instance can process it independently.
ABP Framework doesn't implement this fanout exchange logic since it's coupled to RabbitMQ and ABP uses abstraction over event-buses. That mean, you'll need to implement it on your own by using the RabbitMQ library itself instead using ABP abstractions.
The exception is thrown from there: https://github.com/abpframework/abp/blob/e23e92e8e181a093746d4d5d41e7ef2773a76ea5/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs#L27
It should be get from StaticStore, but it seems there is a missing configuration and it's not defined. Can you make sure your HttpApi.Host project has direct or indirect reference to Volo.Abp.AuditLogging.Application.Contract package? Normally it should.
It seems some files are locked or cannot be accessed. Can you make sure ABP suite or ABP Studio isn't running while trying to open it with abp suite command.
(Windows only) Here what I can suggest if you're not sure which process is working.
taskkill /im dotnet* -f
And then try to execute abp suite command