AspNetCore 2 oData - Missing context on get all identity users - asp.net-core

I trying to create a web api with oData v4.
Now i try to get all Identity-User over oData.
This is working:
[EnableQuery]
public class UsersController : Controller
{
protected readonly UserManager<User> _userManager;
public UsersController(UserManager<User> userManager)
{
_userManager = userManager;
}
private static List<User> _users = new List<User>
{
new User { Id = 1, Name = "Flo", Email = ""},
new User { Id = 2, Name = "Felix", Email = ""},
new User { Id = 3, Name = "Andreas", Email = ""},
new User { Id = 4, Name = "Marco", Email = ""}
};
public IQueryable<User> Get()
{
return _users.AsQueryable();
}
}
And return this response:
{"#odata.context":"http://localhost:55503/oData/$metadata#Users(Id)","value":[{"Id":1},{"Id":2},{"Id":3},{"Id":4}]}
When i change the controller to return all Identity-Users this isn't working correctly.
[EnableQuery]
public class UsersController : Controller
{
protected readonly UserManager<User> _userManager;
public UsersController(UserManager<User> userManager)
{
_userManager = userManager;
}
public IQueryable<User> Get()
{
return _userManager.Users.AsQueryable();
}
}
And it returns this response:
[{"Id":"35909773-8b53-4d68-a770-b7cdfcffd0de"}]
But the response is missing the context. Can you give my a hint why?

I solved the problem:
var builder = new ODataConventionModelBuilder(serviceProvider);
builder.EntitySet<User>("Users");
return builder.GetEdmModel();

Related

Data variable is equal to null, even though there are elements in the database

I'm seeding sql tables with the use of context factory, It shows up and there is an element in mssql, but when I write a controller, I get an empty list with no entries in the table. Could it perhaps be the inheritance that is causing an issue? Classical concert is inherited from Concert.
ConcertController:
public class ConcertController : Controller
{
private readonly AppDbContext _context;
public ConcertController(AppDbContext context)
{
_context = context;
}
public IActionResult Index()
{
var data = _context.Concerts.ToList(); //this variable is null
return View();
}
}
DbContext:
public class AppDbContext : DbContext
{
public virtual DbSet<Concert> Concerts { get; set; }
public virtual DbSet<Ticket> Tickets { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Party>().HasBaseType<Concert>();
modelBuilder.Entity<ClassicalConcert>().HasBaseType<Concert>();
base.OnModelCreating(modelBuilder);
}
}
Data seeding:
var factory = new AppContextFactory();
using var context = factory.CreateDbContext();
var builder = WebApplication.CreateBuilder(args);
await AddData();
async Task AddData()
{
ClassicalConcert mozart;
await context.AddRangeAsync(new[]
{
mozart = new ClassicalConcert()
{
PerformerName = "Some random dude",
TicketsCount= 1000,
PerformanceDate= DateTime.Now,
Location = "Rnd location",
Description = "some rnd desc",
ImageURL = "some https",
VoiceType = Centaurea.Data.Enums.VoiceTypes.Bass,
ConcertName = "Mozarts back",
ComposersName = "Mozart",
}
});
await context.SaveChangesAsync();
}
Context Factory:
public class AppContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args = null)
{
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
optionsBuilder.UseSqlServer(configuration["ConnectionStrings:DefaultConnection"]);
return new AppDbContext(optionsBuilder.Options);
}
}
Picture of the Debuger:

.NET Core 3 Service result to Controller to FE (data or error reason)

building new app on .net core 3 and Angular. Overall all works, but I want to add more intelligence to service/controller part. This is one of the api's, but this logic can be applied to others as as well.
Here's my Login Controller:
[HttpPost]
public async Task<IActionResult> Login([FromBody] UserLoginDto userLogin)
{
var token = await _userService.LoginAsync(userLogin);
if (token != null)
{
return Ok(token);
}
else
{
return BadRequest("Something went wrong");
}
}
And here's my userService:
public async Task<string> LoginAsync(UserLoginDto userLogin)
{
var user = await _userManager.FindByEmailAsync(userLogin.Email);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(user, userLogin.Password, false, true);
if (result.Succeeded)
{
var roles = await _userManager.GetRolesAsync(user);
var tokenJson = _jwtManager.getJwtToken(user.Email, roles);
return tokenJson;
}
else
{
return null; // Return BadRequest and result reason (Failed, lockedout, etc)
}
}
else
{
return null; // User not found, return NotFound }
}
Here's my question - how should I return result from userService to Controller so, that I could respond to API call either with Ok(token) or BadRequest/NotFound with the reason.
If I keep all this LoginAsync code in controller, then it's easy, but I want to use service.
One option I was thinking was to introduce new class, something like:
public class BaseResult
{
public object Data { get; set; }
public long ResponseCode { get; set; }
public string ErrorMessage { get; set; }
}
then always return this class from service, but not fully like that idea either.
thanks!
Here is a working demo you could follow:
Model:
public class UserLoginDto
{
public string Email { get; set; }
public string Password { get; set; }
}
IUserService:
public interface IUserService
{
Task<IActionResult> LoginAsync(UserLoginDto userLogin);
}
UserService:
public class UserService: IUserService
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly IJwtManager _jwtManager;
public UserService(
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
IJwtManager jwtManager)
{
_userManager = userManager;
_signInManager = signInManager;
_jwtManager = jwtManager;
}
public async Task<IActionResult> LoginAsync(UserLoginDto userLogin)
{
var user = await _userManager.FindByEmailAsync(userLogin.Email);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(user, userLogin.Password, false, true);
if (result.Succeeded)
{
var roles = await _userManager.GetRolesAsync(user);
var tokenJson = _jwtManager.getJwtToken(user.Email, roles);
return new OkObjectResult(tokenJson);
}
else
{
// Return BadRequest and result reason (Failed, lockedout, etc)
if (result.IsNotAllowed)
{
if (!await _userManager.IsEmailConfirmedAsync(user))
{
// Email isn't confirmed.
return new BadRequestObjectResult("Email isn't confirmed.");
}
if (!await _userManager.IsPhoneNumberConfirmedAsync(user))
{
// Phone Number isn't confirmed.
return new BadRequestObjectResult("Phone Number isn't confirmed.");
}
return new BadRequestObjectResult("Login IsNotAllowed");
}
else if (result.IsLockedOut)
{
// Account is locked out.
return new BadRequestObjectResult("Account is locked out.");
}
else if (result.RequiresTwoFactor)
{
// 2FA required.
return new BadRequestObjectResult("2FA required");
}
else
{
// Password is incorrect.
return new BadRequestObjectResult("Password is incorrect.");
}
}
}
else
{
return new NotFoundObjectResult("Username is incorrect"); // User not found, return NotFound }
}
}
}
Controller:
public class HomeController : Controller
{
private readonly IUserService _userService;
public HomeController(IUserService userService)
{
_userService = userService;
}
[HttpPost]
public async Task<IActionResult> Login([FromBody] UserLoginDto userLogin)
{
var result= await _userService.LoginAsync(userLogin);
return result;
}
}
Startup.cs:
Not sure what is _jwtManager.getJwtToken in your code,so I just guess it is an interface and owns a JwtManager class implemented this interface.And it contains a getJwtToken method which generated the token.
services.AddScoped<IUserService, UserService>();
services.AddScoped<IJwtManager, JwtManager>();

How to create attributes on controller actions to check if user has claim

I have added a few custom claims to my user and I was wondering if I want to check if these claims exist on controller actions using attributes, I know that we can create a class and extend attribute from .Net and the general idea is to check if user has claim or not, I'm not really clear on the implementation.
Maybe something like this:
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
public class ClaimRequirementAttribute : Attribute
{
public ClaimRequirementAttribute(string claimType)
{
new Claim(claimType, null);
}
}
public class ClaimRequirementFilter
{
public void OnAuthorization(HttpContext httpContext)
{
var hasClaim = httpContext.User.HasClaim(x => x.Type ==
CapabilityClaims.CanReadSpore);
if (!hasClaim)
{
}
}
}
You can get the Claims of a specific user, using the GetClaimsAsync method of UserManager.
You can use the following method:
public class TestController : Controller
{
private readonly UserManager<AppUser> _userManager;
public TestController(UserManager<AppUser> userManager)
{
_userManager = userManager;
}
public CheckIfClaimsExist(string email)
{
var user = await _userManager.FindByEmailAsync(email);
if(user != null)
{
var claims = await _userManager.GetClaimsAsync(user);
}
}
}
Note: AppUser class is a custom class which extends IdentityUser class from identity server.
After some long research i found this answer using filters
which ended up being the best approach
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(params string[] claimType) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] { claimType };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly string[] _claimTypes;
public ClaimRequirementFilter(string[] claimTypes)
{
_claimTypes = claimTypes;
}
public void OnAuthorization(AuthorizationFilterContext authContext)
{
if (authContext == null)
{
throw new ArgumentNullException(nameof(authContext));
}
var user = authContext.HttpContext.User;
var resourceId = authContext.RouteData.Values["id"].ToString();
var claimType = _claimTypes
.All(s => (user.Claims)
.Any(c => c.Type == s && (c.Value == resourceId || c.Value == string.Empty)));
if (user == null || !claimType)
{
authContext.Result = new ForbidResult();
}
}

How do I refer to ASP.NET identity Usermanager inside of a static class?

I want to create a new user during the seed method of the program but how do I call CreateAsync??
public class DbInitializer
{
private readonly UserManager<ApplicationUser> _userManager;
public DbInitializer(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public static void Initialize(IApplicationBuilder app)
{
OneDbContext context = app.ApplicationServices.GetRequiredService<OneDbContext>();
context.Database.EnsureCreated();
//If there are no users, create a test user
if (!context.Users.Any())
{
var user = new ApplicationUser { UserName = "test", Email = "test#test.com" };
var result = _userManager.CreateAsync(user, "test");
}
error CS0120: An object reference is required for the non-static field, method, or property 'DbInitializer._userManager'

asp.net-core least steps to create and save new entry from API

I want to take a few post query parameters from an API i have and create a new entry. I wanted to do this with in the method with out needing to load context or something.
namespace fais.printing_services.Controllers
{
[Produces("application/json")]
[Route("api/[controller]/[action]")]
public class printController : Controller
{
private readonly IHostingEnvironment _appEnvironment;
public printController(IHostingEnvironment appEnvironment)
{
_appEnvironment = appEnvironment;
}
/**/
[HttpPost]
public IActionResult request(string id="test_default", string url = "", string html = "")
{
print_job _print_job = new print_job();
_print_job.html = html;
_print_job.options = options; //json object string
_print_job.url = url;
using (ApplicationDbContext db = new ApplicationDbContext())
{
db.print_job.Add(_print_job);
db.SaveChanges();
}
return Json(new
{
save = true
});
}
}
}
I just want to be able create a new print_job entry and save it when the API is called and return a json response.
Add ApplicationDbContext to controller constructor, it will be injected automatically (if your Startup.cs is like recommeneded):
private readonly IHostingEnvironment _appEnvironment;
private readonly ApplicationDbContext _db;
public printController(IHostingEnvironment appEnvironment, ApplicationDbContext db)
{
_appEnvironment = appEnvironment;
_db = db;
}
[HttpPost]
public IActionResult request(string id="test_default", string url = "", string html = "")
{
var _print_job = new print_job()
{
html = html,
options = options,
url = url,
}
_db.print_job.Add(_print_job);
_db.SaveChanges();
return Json(new { save = true });
}