Access Cloudscribe SiteID outside Controller or Views - asp.net-core-mvc

I have a class which I want to get the Cloudscribe SiteId but I am unsure how to access it.
If I was using a controller then I would be able to rely on the SiteContext to be DI'd into the constructor but that does not work in this instance.
Can someone tell me how I can access the SiteId property in the code below?
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CoLabR
{
public class Messaging : Hub
{
private string _siteId;
public Messaging()
{
//Code here to get SiteID
_siteId = "<<Code for getting Site ID";
}
public Task Post(string message)
{
return Clients.Group(_siteId).InvokeAsync("Post", message);
}
public Task JoinRoom()
{
Groups.AddAsync(Context.ConnectionId, _siteId);
return Clients.Group(_siteId).InvokeAsync("Post", Context.User.Identity.Name + " joined. " + _siteId);
}
}
}

SignalR Core supports injecting into hubs so you should be able to just inject SiteContext into Messaging.
Here is a related question that shows how to do that.

Although the answer from #Pawel helped me, my final solution differed. I tried registering SiteContext in my Startup.cs and injecting SiteContext into my constructor for the Hub but it kept returning a different SiteID on each page load (or if I used Singleton it gave me the same SiteId across multiple CloudScribe Sites).
In the end my solution was to change the Hub constructor to the below code
public Messaging(SiteUserManager<SiteUser> userManager)
{
//Code here to get SiteID
_siteId = userManager.Site.Id.ToString();
}
This gave me a consistent SiteId for each Tenant site in my application.

Related

ASP NET Boilerplate, Login saved in ABPSession

I'm new on the asp net boilerplate framework, and i created a new mvc project multipage web application, without module zero.
I would like to use the AbpSession class that from what I understand has inside the user id that is taken over Thread.CurrentPrincipal.
However, I do not understand how to do after login, to save the user id in the Thread.CurrentPrincipal.
I've searched in the network and found several solutions, but in the AbpSession class the user id is always null.
The most optimal solution I found was this:
IList<Claim> claimCollection = new List<Claim>
{
new Claim(AbpClaimTypes.UserId, "5")
};
ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection);
var principal = new ClaimsPrincipal(claimsIdentity);
Thread.CurrentPrincipal = principal;
It's the first time I use principal and identity and despite being documented I did not quite understand how to use them with asp net boilerplate, and I did not find sample codes.
Do you know how to tell me the right way or tell me where to find some functional codes?
Thanks
Start expanding AbpSession
The last section has cleared the way of thinking. Let's roll up our sleeves and expand in this section.
AbpSession attributes have been injected into three base classes: Application Service, AbpController and ABP ApiController.
So we need to extend AbpSession at the domain level, which is the project at the end of. Core.
Now suppose we need to extend an Email attribute.
Extending IAbpSession
Locate the project at the end of. Core, add the Extensions folder, and then add the IAbpSession Extension interface inherited from IAbpSession:
namespace LearningMpaAbp.Extensions
{
public interface IAbpSessionExtension : IAbpSession
{
string Email { get; }
}
}
Implementing IAbpSession Extension
Add the AbpSession Extension class, which is based on Claims AbpSession and implements the IAbpSession Extension interface.
namespace LearningMpaAbp.Extensions
{
public class AbpSessionExtension : ClaimsAbpSession, IAbpSessionExtension, ITransientDependency
{
public AbpSessionExtension(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{}
public string Email => GetClaimValue(ClaimTypes.Email);
private string GetClaimValue(string claimType)
{
var claimsPrincipal = PrincipalAccessor.Principal;
var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);
if (string.IsNullOrEmpty(claim?.Value))
return null;
return claim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
//Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));
return claim;
}
Replace the injected AbbSession attribute
First replace the injected ABP Session in AbpController
Locate. ApplicationxxxControllerBase:AbpController. CS and inject IAbpSession Extension with attributes. Add the following code:
//AbpSession Hiding Parent Class
public new IAbpSessionExtension AbpSession { get; set; }
Replace the injected ABP Session in Application Service
Locate. ApplicationxxxAppServiceBase.cs. Introduce IAbpSession Extension with attributes, and add the following code as well:
//AbpSession Hiding Parent Class
public new IAbpSessionExtension AbpSession { get; set; }
Chaneg the injected ABP Session in Views AbpRazorPage
Locate. ApplicationxxxRazorPage.cs. Introduce IAbpSession Extension with attributes, and add the following code as well:
[RazorInject]
public IAbpSessionExtension AbpSession { get; set; }
Altough the question is very general, i would like to share you some code about how to add custom field to AbpSession in ASP.NET Core.
MyAppSession.cs
//Define your own session and add your custom field to it
//Then, you can inject MyAppSession and use it's new property in your project.
public class MyAppSession : ClaimsAbpSession, ITransientDependency
{
public MyAppSession(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{
}
public string UserEmail
{
get
{
var userEmailClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "Application_UserEmail");
if (string.IsNullOrEmpty(userEmailClaim?.Value))
{
return null;
}
return userEmailClaim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
//Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim("Application_UserEmail", user.EmailAddress));
return claim;
}

Automation error on Webclient.Headers

I have an error in the following code when using the Headers property:
Public Function UploadImage(image As String) As String
Dim wc As System.WebClient
'create WebClient
Set wc = CreateObject("System.Net.WebClient")
Call wc.Headers.Add("Authorization", "Client-ID " & ClientId) <------- Error occurs here
I have repro'd your issue as far as possible. You didn't mention what the error you were getting was, but I got:
Automation error -2146233079 (80131509)
I tried using
CallByName(wc, "Headers", VbGet)
... but that just returns
Automation error 440.
Oh well...
I looked up online, and found this link. My guess is that because the base class of the WebHeaderCollection class is not COM Visible, this causes the error.
My work-around would be to wrap up this functionality in a small .NET component, and make that COM visible.
An example of this would be something like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace WebClientWrapper
{
[ComVisible(true)]
[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
public class WebClientWrapper : WebClient
{
[ComVisible(true)]
public WebHeaderCollectionWrapper WHeaders
{
get
{
return new WebHeaderCollectionWrapper(base.Headers);
}
}
}
[ComVisible(true)]
[Guid("yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy")]
public class WebHeaderCollectionWrapper
{
WebHeaderCollection _whc;
internal WebHeaderCollectionWrapper(WebHeaderCollection whc)
{
_whc = whc;
}
[ComVisible(true)]
public void Add(string name, string value)
{
_whc.Add(name, value);
}
[ComVisible(true)]
public void Clear()
{
_whc.Clear();
}
}
}
(You will have to replace the GUIDs with your own values - use GUIDGEN.EXE).
Instantiate this component with CreateObject("WebClientWrapper.WebClientWrapper")
Now you would simply replace references to the Headers property to WHeaders (or whatever you want to call it). WHeaders gives you a true wrapper around WebHeaderCollection - you'll have to define all the other wrapped methods and properties yourself. I was hoping to define WHeaders as public WebHeaderCollectionWrapper, but that didn't seem to work.
Since WebClientWrapper inherits from WebClient, you should be able to use most of the properties and methods. Where you have troubles, just add new methods to the class wrapping up the functionality that doesn't work with VB.
Oh, and do remember to set the checkbox at Project Properties => Build => Output => Register for COM interop. And then reference the type library created.

WebApi External Bearer Authentication and Injecting User into respositories

I have a webapi project and a repositories project.
I have configured to use oauth, which uses owin middleware bearer token authentication.
I have a unitofwork with multiple repositories.
Inside the repositories I want to filter data based on the logged on user.
I would like all repositories to get the logged in user via dependency injection.
I can access the logged on user in the webapi action, but I am struggling to work out if/how I can inject the current user using DI; because the authorization is happening via the webapi Authorize?:
[Authorize(Roles = "User")]
public IQueryable<Folder> Folders()
{
// return UnitOfWork.FolderRepository.All().OrderBy(o=>o.FolderId).Skip(10).Take(50);
var test = HttpContext.Current.GetOwinContext().Authentication;
//test is populated with the logged on user here, but I don't want to set the user details of the UOW in every action in my controllers
return UnitOfWork.FolderRepository.All();
}
So in the action Folders the Authorize annotation logs the user on. But I have already instantiated the unit of work in the controller constructor with DI:
public FolderController(IUnitOfWork uow, UserManager<IdentityUser,int> usermanager)
{
UnitOfWork = uow;
UserManager = usermanager;
}
IOC container:
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.For<HttpContextBase>()
.HybridHttpOrThreadLocalScoped()
.Use(() => new HttpContextWrapper(HttpContext.Current));
x.For<IUnitOfWork>().HttpContextScoped().Use(
() => new UnitOfWork(new BreezeValidator
(new UserManager<AspNet.Identity.SQLServer.IdentityUser, int>(new UserStore(new SqlDatabase()))))
);
}
}
I had tried to pass in HttpContext.Current.GetOwinContext(), but at that point the authorization hasn't taken place and so the Principal has not been set.
I have looked at actionfilters (which are run after the authorization filter), but can't figure out how I would return a new unit of work instance with the logged on user set, back to the controller.
...Or whether I can set a property on the controller from an action filter?
So the question is really, how can I set the user details in all my controller's unitofwork, without lots of duplication?
Thanks
EDIT: I have a working solution, but still not sure it's the right way to go:
I created an action filter and then from there get the controller and set a UserPrincipal property on the controller's unitOfWork property.
using Project1.Web.Controllers;
using System;
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Project1.Web.Filters
{
public class InjectUserAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var action = actionContext.ControllerContext.Controller;
UowApiController ctrl = (UowApiController)actionContext.ControllerContext.Controller;
ctrl.UnitOfWork.UserPrincipal = actionContext.RequestContext.Principal;
}
}
Then, in my UnitOfWork setter of the UserPrincipal I set the UserPrincipal in the contained repositories:
public IPrincipal UserPrincipal
{
get
{
return this.userPrincipal;
}
set
{
this.userPrincipal = value;
((Repository<Folder>)FolderRepository).UserPrincipal = value;
}
}
This works now, but it doesn't achieve dependency injection.
Also I would like to know if this is a "right" way to do it, or what would be a better approach?
I was searching for the same thing and decided on this. I think this answer will be relevant to you as well.
Proper way to dependency inject authenticated user to my repository class
I've just added a getter to the service classes that accesses the user identity at request time.
public class MyService
{
//ctor...
public IEnumerable<Results> GetResults()
{
return _ResultRepository.GetResultsByUser(UserIdentity);
}
IIdentity UserIdentity
{
get { return Thread.CurrentPrincipal.Identity; }
}
}

Entity Framework Database First with proper use of Data Anotations

I have one project with EF and Code First approach and there using of Data Annotations was straight forward. Now I'm working with Database First and I see that using Data Annotations is more specific so I want to know the right steps to implement it.
The structure of my project that provides Data Access is this:
In ModelExtensions are all my files that I've created to add the Data Annotations to the DbContextModel.tt entities.
Here is the structure of one of my files in ModelExtensions:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
namespace DataAccess.ModelExtensions
{
[MetadataType(typeof(MCS_ContentTypesMetaData))]
public partial class MCS_ContentTypes : BaseEntity
{
}
internal sealed class MCS_ContentTypesMetaData
{
[Required]
[StringLength(10)]
public string Name { get; set; }
}
}
I have several questions here. First - the namespace. Should it be like this namespace DataAccess.ModelExtensions or I have to remove the .ModelExtensions part. I was looking at a project using DB first and there the namespace was just DataAccess not sure why it is needed (if so). Also - Do I need to add some other references to the DbContextModel.tt entities? Now I use standard C# classes for this and then rename them to : public partial class MCS_ContentTypes : BaseEntity. Do I have to use a special approach for creating those to explicitly expose the connection between the entity and this file?
1) The namespace of your extension models must be the same as the namespace of EF auto-generated entity classes - If the namespace of DbContextModel.tt entity classes is DataAccess, you should set the namespace of your classes to DataAccess.
2) I doesn't get your question completely, however in this approach, names of entity classes and your classes must be the same.
The following example shows what it should be. Suppose that EF generates the following entity class for you:
namespace YourSolution
{
using System;
using System.Collections.Generic;
public partial class News
{
public int ID { get; set; }
public string Title { get; set; }
}
}
So, your partial classes should be like the following:
namespace YourSolution
{
[MetadataType(typeof(NewsAttribs))]
public partial class News
{
// leave it empty.
}
public class NewsAttribs
{
// Your attribs will come here.
[Display(Name = "News title")]
[Required(ErrorMessage = "Please enter the news title.")]
public string Title { get; set; }
// and other properties you want...
}
}
So, you doesn't need any : BaseEntity inheritance.

Common DataAnnotations in ASP.Net MVC2

Howdy, I have what should be a simple question. I have a set of validations that use System.CompontentModel.DataAnnotations . I have some validations that are specific to certain view models, so I'm comfortable with having the validation code in the same file as my models (as in the default AccountModels.cs file that ships with MVC2). But I have some common validations that apply to several models as well (valid email address format for example). When I cut/paste that validation to the second model that needs it, of course I get a duplicate definition error because they're in the same namespace (projectName.Models). So I thought of removing the common validations to a separate class within the namespace, expecting that all of my view models would be able to access the validations from there. Unexpectedly, the validations are no longer accessible. I've verified that they are still in the same namespace, and they are all public. I wouldn't expect that I would have to have any specific reference to them (tried adding using statement for the same namespace, but that didn't resolve it, and via the add references dialog, a project can't reference itself (makes sense).
So any idea why public validations that have simply been moved to another file in the same namespace aren't visible to my models?
CommonValidations.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
namespace ProjectName.Models
{
public class CommonValidations
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public sealed class EmailFormatValidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value != null)
{
var expression = #"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$";
return Regex.IsMatch(value.ToString(), expression);
}
else
{
return false;
}
}
}
}
}
And here's the code that I want to use the validation from:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Growums.Models;
namespace ProjectName.Models
{
public class PrivacyModel
{
[Required(ErrorMessage="Required")]
[EmailFormatValid(ErrorMessage="Invalid Email")]
public string Email { get; set; }
}
}
You have declared EmailFormatValidAttribute as a subclass to CommonValidations. As such you need to reference it like CommonValidations.EmailFormatValidAttribute. Or alternatively move the EmailFormatValidAttribute class out of the CommonValidations class.
This should work:
[CommonValidations.EmailFormatValid(ErrorMessage="Invalid Email")]
public string Email { get; set; }
By the way, you can simplify your class as follows:
public class EmailFormatValidAttribute : RegularExpressionAttribute
{
public EmailFormatValidAttribute() :
base(#"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$")
{}
}
Also, take a look at this: Data Annotations Extensions. It's a great DataAnnotations library which has already the most common validations included in it.

Resources