I am trying to allow multiple users to have the same email. I extended the OrmLiteAuthRepository overrode the AssertNoExistingUser but it is never called, even though i get the "duplicate email error". I know its hooked up because the getpermissions method is working.
public class MyOrmLiteAuthRepository : OrmLiteAuthRepository
{
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory) : base(dbFactory) { }
public MyOrmLiteAuthRepository(IDbConnectionFactory dbFactory, string namedConnnection = null)
: base(dbFactory, namedConnnection)
{
DbFactory = dbFactory;
NamedConnnection = namedConnnection;
}
protected override void AssertNoExistingUser(IDbConnection db, IUserAuth newUser, IUserAuth exceptForExistingUser = null)
{
//I hate using try catch for simple stuff, it is very slow,
//But this is only during new users being added so low risk for slow
//and the base class should be called to do its native stuff
try
{
base.AssertNoExistingUser(db,newUser);
}
catch (Exception e)
{
if (e.Message.Contains("Email"))
{
//mask duplicate email messages
return;
}
//throw any other errors on new user creation.
throw;
}
}
public IDbConnectionFactory DbFactory { get; set; }
public string NamedConnnection { get; set; }
public override ICollection<string> GetPermissions(string userAuthId)
{
//Ignore this as we have implemented our own security
// base.GetPermissions(userAuthId);
using (var ss = HostContext.ResolveService<SecurityService>(new BasicRequest()))
{
return ss.UserPermissions(Convert.ToInt32(userAuthId));
}
}
ServiceStack can authenticate using either Username or Email, but irrespective of which is used they must be unique in order to uniquely identify the user that is attempting to authenticate.
If you just want to persist the same email address in multiple users you can store it in PrimaryEmail which isn't validated or used in Authentication.
Related
I have developed an app with Xamarin Forms and implemented push notification using Azure Notification Hub.
I followed this tutorial from Microsoft site :
https://learn.microsoft.com/en-us/azure/notification-hubs/notification-hubs-backend-service-xamarin-forms
I receive the notification when send it to all ,The problem is when I use the 'Tag' in order to send it to specific user(I register this user with certain tag when he log in the application.)
This is not working and I don't even have an idea how to check where is the problem.
I tried to check out weather the tag is not assigned correctly or not assigned at all but it's not possible to check it.
What should I do?
How can I see all the tags I have in system and who is registered to the tag?
Maybe I didn't understood it correctly but I don't create the Tag before the user register ,I just add it to the Tags List when the user is registered to the system.
public class DeviceInstallation
{
[Required]
public string InstallationId { get; set; }
[Required]
public string Platform { get; set; }
[Required]
public string PushChannel { get; set; }
public IList<string> Tags { get; set; } = Array.Empty<string>(); //Generate tag when register device and add it to list
}
public async Task RegisterDeviceAsync(params string[] tags)
{
var deviceInstallation = DeviceInstallationService?.GetDeviceInstallation(tags);
if (deviceInstallation == null)
throw new Exception($"Unable to get device installation information.");
if (string.IsNullOrWhiteSpace(deviceInstallation.InstallationId))
throw new Exception($"No {nameof(deviceInstallation.InstallationId)} value for {nameof(DeviceInstallation)}");
if (string.IsNullOrWhiteSpace(deviceInstallation.Platform))
throw new Exception($"No {nameof(deviceInstallation.Platform)} value for {nameof(DeviceInstallation)}");
if (string.IsNullOrWhiteSpace(deviceInstallation.PushChannel))
throw new Exception($"No {nameof(deviceInstallation.PushChannel)} value for {nameof(DeviceInstallation)}");
await SendAsync<DeviceInstallation>(HttpMethod.Put, RequestUrl, deviceInstallation)
.ConfigureAwait(false);
var serializedTags = JsonConvert.SerializeObject(tags);
await SecureStorage.SetAsync(CachedTagsKey, serializedTags);
}
The following code is in the server:
public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
return false;
var installation = new Installation()
{
InstallationId = deviceInstallation.InstallationId,
PushChannel = deviceInstallation.PushChannel,
Tags = deviceInstallation.Tags
};
if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
installation.Platform = platform;
else
return false;
try
{
await _hub.CreateOrUpdateInstallationAsync(installation, token);
}
catch
{
return false;
}
return true;
}
I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema
Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}
What I'm trying to do is create a site in Orchard that doesn't have a way for a user to register. An administrator will create the users.
What I have is module that defines the parts, records, views, etc. That is basically working.
Now what I'm trying to do is add a UserPart (from Orchard.Users) to one of the parts in my module.
I'm not sure how to do that. I need the fields displayed for the UserPart with the fields for the parent part in the same view. This also needs to be done in a way that when a save happens, all of the UserPart fields get sent to the Orchard.Users module.
Any suggestions, pointers or links on how to do that?
Thanks!
UPDATE...
The Activating Filter is an interesting idea. I initially chose the migration route. For now, I'll try and get that method working.
For simplicity, let's say I have a "Company" type (there's more to the actual type) that has a "CompanyName" and a UserPart.
Here's what the different pieces look like...
Migrations.cs (simplified)
public int Create()
{
SchemaBuilder.CreateTable("CompanyPartRecord", table => table.ContentPartRecord()
.Column("CompanyName", DbType.AnsiString, c => c.WithLength(50))
.Column("UserId", DbType.Int32));
SchemaBuilder.CreateForeignKey("FK_CompanyPartRecord_UserPartRecord", "CompanyPartRecord", new[] {"UserId" }, "Orchard.Users", "UserPartRecord", new[] { "Id" })
ContentDefinitionManager.AlterTypeDefinition("Company", type => type.WithPart("CommonPart").WithPart("UserPart"));
}
CompanyPartRecord
public class CompanyPartRecord : ContentPartRecord
{
public virtual string CompanyName { get; set; }
public virtual int? UserId { get; set; }
}
CompanyPart
public class CompanyPart : ContentPart<CompanyPartRecord>
{
internal LazyField<UserPart> UserPartField = new LazyField<UserPart>();
public string CompanyName
{
get { return Record.CompanyName; }
set { Record.CompanyName = value; }
}
public UserPart User
{
get { return UserPartField.Value;}
set { UserPartField.Value = value; }
}
}
Handler
public class CompanyPartHandler : ContentHandler
{
private readonly IContentManager _manager;
public CompanyPartHandler(IRepository<CompanyPartRecord> repository, IContentManager manager)
{
_manager = manager;
Filters.Add(StorageFilter.For(repository));
OnActivated<CompanyPart>(OnActivatedHandler);
}
private void OnActivatedHandler(ActivatedContentContext context, CompanyPart part)
{
if(part.User == null)
{
part.User = _manager.Create<UserPart>("User");
}
else
{
part.User = _manager.Get<UserPart>(part.User.Id);
}
}
}
Driver
public class CompanyPartDriver : ContentPartDriver<CompanyPart>
{
protected override DriverResult Editor(CompanyPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Company_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/Company",
Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(CompanyPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Controller
public class AdminCompanyController : Controller, IUpdateModel
{
private readonly IOrchardServices _services;
private readonly INotifier _notifier;
private readonly IContentManager _contentManager;
private readonly ITransactionManager _transactionManager;
private readonly Localizer T = NullLocalizer.Instance;
public AdminCompanyController(IOrchardServices services)
{
_services = services;
_notifier = services.Notifier;
_contentManager = services.ContentManager;
_transactionManager = services.TransactionManager;
}
public ActionResult Create()
{
var company = _contentManager.New<CompanyPart>("Company");
var model = _contentManager.BuildEditor(company);
return View(model);
}
[HttpPost, ActionName("Create")]
public ActionResult CreatePOST()
{
var contentItem = _contentManager.New<CompanyPart>("Company");
var model = _contentManager.UpdateEditor(contentItem, this);
if (!ModelState.IsValid)
{
_transactionManager.Cancel();
return View(model);
}
_contentManager.Create(contentItem.ContentItem);
_notifier.Information(T("Company has been saved"));
return RedirectToAction("Index");
}
public ActionResult Edit(int Id)
{
var contentItem = _services.ContentManager.Get(Id);
dynamic model = _services.ContentManager.BuildEditor(contentItem);
return View(model);
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(int Id)
{
var contentItem = _contentManager.Get<CompanyPart>(Id);
var model = _contentManager.UpdateEditor(contentItem, this);
_notifier.Information(T("Company has been saved"));
return RedirectToAction("Index");
}
public ActionResult Delete(int Id)
{
var contentItem = _contentManager.Get<CompanyPart>(Id);
_contentManager.Destroy(contentItem.ContentItem);
return RedirectToAction("Index");
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
{
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
public void AddModelError(string key, LocalizedString errorMessage)
{
ModelState.AddModelError(key, errorMessage.ToString());
}
}
View (create)
#{ Layout.Title = T("Add Company").ToString(); }
#using (Html.BeginFormAntiForgeryPost())
{
#Display(Model)
}
Editor Template
#model SDS.Models.CompanyPart
<fieldset>
#Html.LabelFor(m => m.CompanyName)
#Html.TextBoxFor(m => m.CompanyName)
</fieldset>
#*
What goes here to display UserPart?
*#
So here's where I'm at. I can see the ContentItem (CompanyType). I can put in the name and save it. The name is getting saved to the db. Right now the UserPart is getting saved to the db, but all of the fields are blank.
The part I'm stuck on is what to put in the editor template to display the UserPart fields so that the values get to the UserPart driver and ultimately the db.
Any ideas on how to do that?
Thanks!
So you don't attach parts to parts, you attach parts to content items, and you can do that in multiple ways.
You can do it through the admin screen, but that isn't a code driven solution and would have problems if you have multiple environments or need to redeploy a fresh version of code.
You can attach the part when you create a new content item in the migration. This might be a good solution, if you already ran your migration you could possibly do it with an update migration. This allows the part to be managed through the admin screen, but has downsides because it can be removed and if you have code that relies on the part then you will start having errors.
The last way and best way is to attach the part dynamically using an Activating Filter.
ActivatingFilter class - Attaches a part to a content type from code. As opposed to attaching parts via migrations, parts attached using this filter will neither be displayed in the Dashboard, nor users will be able to remove them from types. It's a legitimate way of attaching parts that should always exist on a given content type.
So to do this:
1. Add a reference to Orchard.Users to your custom project.
2. Create a handler for you part. Such as MyPartHandler
3. Then add the activating handler like so
Filters.Add(ActivatingFilter.For<UserPart>("MyContentType"));
So now anywhere in your code you can access the UserPart if you already have your part, or the content item using
var userPart = myPart.As<UserPart>();
I'm writing come code that will be for an external app. I'm trying to use Microsoft Identity 2.0 and ASP.NET MVC 5. I've customized the UserViewModel to hold FirstName and LastName And a couple of other parameters. Whenever I register a user it logs in successfully but when I try to do the normal login with email/password. the SignInManager.PasswordSignAsync always returns false. Since all the code that checks that is internal to the framework I'm trying to figure out how to even debug the situation. I will provide some code here to help anybody who's willing to look at it. Thanks in Advance!
That is the generic login function that is provided by Microsoft in their examples. I've not changed anything there. But that is the function that has the error message.
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
This is the login function
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
This is the customized Identity Model
public class ApplicationUser : IdentityUser
{
[DisplayName("Prefix")]
public string Prefix { get; set; }
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Suffix")]
public string Suffix { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public System.Data.Entity.DbSet<JBIPlacementTracking.Models.PlacementViewModel> PlacementViewModels { get; set; }
public System.Data.Entity.DbSet<JBIPlacementTracking.Models.Placement> Placements { get; set; }
public System.Data.Entity.DbSet<JBIPlacementTracking.Models.PacketStatus> PacketStatus { get; set; }
}
I'm wondering if the problem might be that I don't have a custom LoginViewModel.
I'm definitely in over my head right now and if somebody has some good resources I'd really appreciate a link to it.
Thanks in Advance Again!
You are using Email as UserName at the login function. Make sure that upon registration, the email is inserted to both the UserName and Email Fields. I Also like to use use Trim() and ToLower() before inserting and after reading, but that's only to rest my mind about what is stored in my DB, and might not be necessary. Good luck.
You are using Email as UserName then you need to set EmailConfirmed field 'true' in AspNetUsers table.
I've been working with ServiceStack and it's Auth providers. Specifically "FacebookAuthProvider".
My issue here is that the service is called from an iOS app. This app already have a valid access token and i just want to pass this value to servicestack facebook authentication.
I've seen the tests on servicestack github page, but it still doesn't make sense to me.
Is it possible to pass this access token to servicestack, so the authentication skips the part where i ask for permission, since we already did the on the app?
Or am i approching this the wrong way?
Instead of using the builtin facebook auth provider i created my own CustomFacebookAuthProvider.
The reason is that the builtin version needs a browser to redirect the user to facebook for authentication and i didn't need that. I already had an access token.
So based on the official version FacebookAuthProvider.cs i created my own.
using System;
using System.Collections.Generic;
using System.Net;
using Elmah;
using Mondohunter.Backend.BusinessLogic.Interfaces;
using ServiceStack.Common.Extensions;
using ServiceStack.Common.Web;
using ServiceStack.Configuration;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
using ServiceStack.Text;
using ServiceStack.WebHost.Endpoints;
namespace Mondohunter.Interfaces
{
public class CustomFacebookAuthProvider : OAuthProvider
{
public const string Name = "facebook";
public static string Realm = "https://graph.facebook.com/";
public static string PreAuthUrl = "https://www.facebook.com/dialog/oauth";
public string AppId { get; set; }
public string AppSecret { get; set; }
public string[] Permissions { get; set; }
public CustomFacebookAuthProvider(IResourceManager appSettings)
: base(appSettings, Realm, Name, "AppId", "AppSecret")
{
this.AppId = appSettings.GetString("oauth.facebook.AppId");
this.AppSecret = appSettings.GetString("oauth.facebook.AppSecret");
}
public override object Authenticate(IServiceBase authService, IAuthSession session, Auth request)
{
var tokens = Init(authService, ref session, request);
try
{
if (request.oauth_token.IsNullOrEmpty())
throw new Exception();
tokens.AccessToken = request.oauth_token;
session.IsAuthenticated = true;
var json = AuthHttpGateway.DownloadFacebookUserInfo(request.oauth_token);
var authInfo = JsonSerializer.DeserializeFromString<Dictionary<string, string>>(json);
//Here i need to update/set userauth id to the email
//UpdateUserAuthId(session, authInfo["email"]);
authService.SaveSession(session, SessionExpiry);
OnAuthenticated(authService, session, tokens, authInfo);
//return json/xml/... response;
}
catch (WebException ex)
{
//return json/xml/... response;
}
catch (Exception ex)
{
//return json/xml/... response;
}
}
protected override void LoadUserAuthInfo(AuthUserSession userSession, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
if (authInfo.ContainsKey("id"))
tokens.UserId = authInfo.GetValueOrDefault("id");
if (authInfo.ContainsKey("name"))
tokens.DisplayName = authInfo.GetValueOrDefault("name");
if (authInfo.ContainsKey("first_name"))
tokens.FirstName = authInfo.GetValueOrDefault("first_name");
if (authInfo.ContainsKey("last_name"))
tokens.LastName = authInfo.GetValueOrDefault("last_name");
if (authInfo.ContainsKey("email"))
tokens.Email = authInfo.GetValueOrDefault("email");
if (authInfo.ContainsKey("gender"))
tokens.Gender = authInfo.GetValueOrDefault("gender");
if (authInfo.ContainsKey("timezone"))
tokens.TimeZone = authInfo.GetValueOrDefault("timezone");
LoadUserOAuthProvider(userSession, tokens);
}
public override void LoadUserOAuthProvider(IAuthSession authSession, IOAuthTokens tokens)
{
var userSession = authSession as CustomUserSession;
if (userSession == null) return;
userSession.Email = tokens.Email ?? userSession.PrimaryEmail ?? userSession.Email;
}
}
}
I hope it makes sense.