What's the best way to migrate to ServiceStack authentication framework when stuck with my_aspnet_* tables - mysql

I'm not quite ready to change up all my user/auth tables from the MySQL user/roles/profile provider format, but am moving off of MVC to ServiceStack.
Is there a pre-built IUserAuthRespository and/or CredentialsAuthProvider somewhere that can be used, or do I need to build one to provide this mapping?
If I need to build one, I assume implementing at the IUserAuthRepository level is the cleanest? Is there a minimum set of methods required to implement basic login/logout (and administrative "switch user" impersonation) functionality?
I tried implementing a custom CredentialsAuthProvider, which seems to work, but I'm unable to get local posts for impersonation to use the proper provider. Looking for a solution to that, I realized that maybe its better to implement the repository instead.
EDIT:
My current registration of the custom auth provider is:
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[]
{
container.Resolve<MySqlCredentialsAuthProvider>() //HTML Form post of UserName/Password credentials
}));
And calling code for the local post to the AuthenticateService is:
[RequiredRole(SystemRoles.Administrator)]
public object Any(ImpersonateUser request)
{
using (var service = base.ResolveService<AuthenticateService>()) //In Process
{
//lets us login without a password if we call it internally
var result = service.Post(new Authenticate
{
provider = AuthenticateService.CredentialsProvider,
UserName = request.Username,
//Password = "should-not-matter-since-we-are-posting-locally"
});
return result;
}
}

Integrating with existing User Auth tables
If you want to use your existing User/Auth tables, the easiest solution is to ignore the UserAuth repositories and implement a Custom CredentialsAuthProvider that looks at your existing database tables to return whether their Authentication attempt was successful.
Implement OnAuthenticated() to populate the rest of your typed IAuthSession from your database, e.g:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService,
string userName, string password)
{
//Add here your custom auth logic (database calls etc)
//Return true if credentials are valid, otherwise false
}
public override IHttpResult OnAuthenticated(IServiceBase authService,
IAuthSession session, IAuthTokens tokens,
Dictionary<string, string> authInfo)
{
//Fill IAuthSession with data you want to retrieve in the app eg:
session.FirstName = "some_firstname_from_db";
//...
//Call base method to Save Session and fire Auth/Session callbacks:
return base.OnAuthenticated(authService, session, tokens, authInfo);
//Alternatively avoid built-in behavior and explicitly save session with
//authService.SaveSession(session, SessionExpiry);
//return null;
}
}
Importing existing User Auth tables
If you want to import them into an OrmLite User Auth tables, you would configure to use the OrmLiteAuthRepository in your AppHost:
//Register to use MySql Dialect Provider
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(dbConnString, MySqlDialect.Provider));
Plugins.Add(new AuthFeature(
() => new CustomUserSession(), //Use your own typed Custom UserSession type
new IAuthProvider[] {
//HTML Form post of UserName/Password credentials
new CredentialsAuthProvider()
}));
//Tell ServiceStack you want to persist User Info in the registered MySql DB above
container.Register<IUserAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));
//Resolve instance of configured IUserAuthRepository
var userAuth = container.Resolve<IUserAuthRepository>();
//Create any missing UserAuth RDBMS tables
authRepo.InitSchema();
Then to import your data you can use the above MySQL DB connection to select from your existing tables then use the IUserAuthRepository to create new Users.
// Open DB Connection to RDBMS
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
//Example of fetching old Users out of a custom table (use your table instead)
var oldUsers = db.Select<OldUserInfo>();
// Clear existing UserAuth tables if you want to replay this import
//db.DeleteAll<UserAuthDetails>();
//db.DeleteAll<UserAuth>();
//Go through and create new User Accounts using Old User Info
foreach (var oldUser in oldUsers)
{
//Create New User Info from Old Info
var newUser = new UserAuth {
UserName = oldUser.UserName,
Email = oldUser.Email,
//...
};
//Create New User Account with oldUser Password
authRepo.CreateUserAuth(newUser, oldUser.Password);
}
}
After this you'll have new User Accounts from your old User Info which you can sign in with.

Related

Web Drop-in integration "advanced use case" redirect result

I'm trying to understand if it's possible to retrieve the redirectResult when the user is redirected to our redirectUrl to invoke the /payment/details API endpoint to speed up delivering virtual goods instead of waiting for the web hook (I'm aware it will not work for "async" payment methods).
Looking at https://docs.adyen.com/online-payments/web-drop-in/advanced-use-cases/redirect-result it should be possible, however, the returnUrl in CreateCheckoutSessionResponse returned from com.adyen.service.Checkout#sessions (adyen-java-api-library 17.2.0 - checkout api version v68) does not contain the aforementioned redirectResult param so the configuration we pass into the drop-in template is missing this data and does not seem to be available in the onPaymentCompleted callback either (only resultCode and sessionData).
#Override
public RedirectResponse handleRedirectToPartner(PaymentContext paymentContext) throws PartnerIntegrationException {
final Payment payment = paymentContext.getPayment();
final Amount amount = new Amount();
amount.setCurrency(payment.getCurrency().toUpperCase());
amount.setValue((long) payment.getPriceInCents());
final CreateCheckoutSessionRequest checkoutSessionRequest = new CreateCheckoutSessionRequest();
...
checkoutSessionRequest.setChannel(CreateCheckoutSessionRequest.ChannelEnum.WEB);
checkoutSessionRequest.setReturnUrl(getReturnUrl());
try {
CreateCheckoutSessionResponse checkoutSessionResponse = checkout().sessions(checkoutSessionRequest);
JSONObject params = new JSONObject();
params.put("environment", testMode ? "test" : "live");
params.put("clientKey", adyenClientKey);
JSONObject session = new JSONObject();
session.put("id", checkoutSessionResponse.getId());
session.put("sessionData", checkoutSessionResponse.getSessionData());
params.put("session", session);
params.put("urlKo", getFailureUrl());
params.put("urlOk", checkoutSessionResponse.getReturnUrl());
params.put("urlPending", getUrlPending(checkoutSessionResponse.getReturnUrl()));
return new RedirectResponse(RedirectResponse.Type.REDIRECT_CUSTOM_HTML_ADYEN, null, params);
} catch (ApiException | IOException e) {
throw new PartnerIntegrationException("Failed creating Adyen session", e);
}
}
protected Checkout checkout() {
return new Checkout(new Client(adyenApiKey, testMode ? Environment.TEST : Environment.LIVE,
testMode ? null : liveEndpointUrlPrefix));
}
(async () => {
let configuration = ${partnerJsonParameters?string};
configuration.onPaymentCompleted = function(result, component) {
console.info(result);
if (result.sessionData) {
console.info(result.sessionData);
}
if (result.resultCode) {
console.info(result.resultCode);
}
handleServerResponse(result, configuration);
};
configuration.onError = function(error, component) {
console.error(error, component);
handleServerResponse(result, configuration);
};
let checkout = await AdyenCheckout(configuration);
checkout.create('dropin').mount('#dropin-container');
})();
The sessions request does not perform the payment, but only initiates the payment session with all required parameters and configuration.
The Web drop-in takes care of 'talking' to the Adyen backend and eventually, the payment outcome can be obtained in the frontend using the onPaymentCompleted handler.
onPaymentCompleted: (result, component) => {
console.info("onPaymentCompleted: " + result.resultCode);
...
}
See Use the result code
On the server-side it is possible to get the payment result with a /payments/details call in addition to /sessions if needed.
// get redirectResult appended to the returnUrl
String redirectResult = request.getParameter("redirectResult");
var paymentDetails = new PaymentsDetailsRequest();
paymentDetails.setDetails(Collections.singletonMap("redirectResult", redirectResult));
// use paymentDetails() method
var paymentsDetailsResponse = checkout.paymentsDetails(paymentDetails);
String resultCode = paymentsDetailsResponse.getResultCode();
Note that a synchronous result is not always available, hence relying on the webhook is best.
I'm the (non-developer) colleague testing with PAL enable and sadly the delay is way too long with them as well. It is crucial for us to be able to get trustworthy authorisation and deliver the goods for the customers in (tens of) seconds, not minutes.
Is there any way to achieve fast and trustworthy credit card transactions with your web drop-in without notifications?
This is possible with your soon-to-be-obsoleted HPP integration and I find it unbelievable that you could have impaired, worsen the integration and user experience. So, there must be some kind of misunderstanding and communication breakdown somewhere (I hope). :)
Sorry, could not just comment as I don't have enough reputation...

.NET Core 2.1 - Accessing Config/usermanager in a static helper

I've recently moved from MVC5 over to .NET Core 2.1 (MVC). Can anyone help me with this please.
I have my ApplicationUser and I've extended the model/table to store the user's FirstName.
In the View, I want to be able to output the current user firstname value.
User in the view is a ClaimsPrincipal so I need to go off to the DB to grab the value I need or access UserManager to get it.
Now, I know I can get that in the controller but I don't want to have to create a JQuery call to grab it every time I need it.
What I do want is to be able to access it server side, ideally via a static helper class.
In the MVC5 I'd have a helper to do the job no problem. Something like this for example:
public static string GetCurrentUserFirstName()
{
string _usrRef = HttpContext.Current.User.Identity.GetUserId();
var user = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(_usrRef);
return user.FirstName;
}
However, .NET Core doesn't work that way.
In a controller I could say:
var user = await _userManager.GetUserAsync(User);
string firstName = user.FirstName;
or I could go off to the DB via a call using Dapper w/ my connection string.
I can't inject the UserManager or ConnectionStrings into the helper via the constructor as it is static.
Is there a way to access either of those in this static helper?
It's the little changes that get you the most!
Thanks to #Kirk Larkin - I've found the solution.
I have to admit, it feels a little more convoluted having to pass things around to gain access to them but this is a good, working solution.
The View:
#using Microsoft.AspNetCore.Identity
#using MyApplication.Helpers
#inject UserManager<ApplicationUser> UserManager
<div>
#await MyHelper.GetLoggedInUserFirstName(UserManager, User)
</div>
The MyHelper file:
public static async Task<string> GetLoggedInUserFirstName(UserManager<ApplicationUser> userManager, ClaimsPrincipal user)
{
string output = "";
try
{
var currentUser = await userManager.GetUserAsync(user);
if(currentUser!=null)
{
output = currentUser.FirstName ?? currentUser.Email;
}
}
catch(Exception e) { }
return output;
}

Should I have a layer in between my controller and model in Node-Express app?

I have an Express application where I use Sequelize to interact with my MySQL database. The models just represent the database tables with corresponding fields, without containing any additional logic. I want my controllers to not be fat and for that I think I should have a layer in between which contains all of the logic and which uses the model to interact with the database.
Is this a good practice and if it is what should I call this layer and what exactly should it contain?
Thanks, in advance!
First of all, great question.
Second of all, this is how I would do it:
in your models, say you have a user model, users.js.
In that model for your user/db interface, after your
const User = module.exports = <sql declaration and model reference>;
you can create other module exports like this:
module.exports.getUserById = function(id, callback){
<Sequelize logic goes here>
};
And this is essentially middleware/controller for your model class for handling routines.
You might use this by importing your user model and then calling your exported module:
const User = require("../models/users")
and then when it's time to call your function:
User.getUserById(id, function(err, user) {
<some logic with regard to your user>
});
You can extend your sequelize models and instances with methods and hooks, your controllers would ideally only make a few calls to these methods.
For instance, you could add something like this to your User model:
instanceMethods: {
encryptPassword: function(plainPassword) {
if(!this.salt){
this.salt = randomString.generate(10);
}
var cipher = crypto.createCipher('aes-256-cbc', this.salt);
cipher.update(plainPassword, 'utf8', 'base64');
var encryptedPassword = cipher.final('base64')
return encryptedPassword;
},
decryptPassword: function(){
var decipher = crypto.createDecipher('aes-256-cbc', this.salt);
decipher.update(this.password, 'base64', 'utf8');
var decryptedPassword = decipher.final('utf8');
return decryptedPassword;
}
}
And maybe even add a pre-save hook to check if the user is new and then encrypt the password before saving, you could create authentication methods to call this from your model and not your controller, and so on...

How to create data view models in Loopback?

Basically what the title asks. I'm wondering if it's possible to create a a custom view model in Loopback that is datasource ignorant?
My current process has been to create a view in MySQL, and then build a model in Loopback that overlays the view, but I recently realized that if we decide to migrate to a different back end, or change the datasource somehow, we'd have to figure out how to recreate the view.
Google searches on this have revealed bupkis, so I figured I'd throw it out here to see if anyone has knowledge on the topic.
Thanks in advance!
Using views in Loopback works well. Just treat the view as if it were a table and Loopback will treat it the same way. In fact you can actually perform some write operations against the view if you want to. Assuming you have already created the view in SQL here's a snippet to create end-points from an existing table or view in Loopback:
/**
* Creates a REST endpoint using the persistedModel.
* #function createPersistedModelApi
*/
function createPersistedModelApi(app, dataSourceName: string, tablename: string, callback) {
let eventInfo: Type.Event
let ds = app.datasources[dataSourceName];
ds.discoverSchema(tablename, null, function(err, schema) {
if (!err) {
// Set the key field.
schema.properties.rowid.id = true;
// Get a list of the fields
var fields = Object.keys(schema.properties);
// Set some properties on all fields.
fields.forEach(function(field) {
schema.properties[field].required = false;
schema.properties[field].nullable = true;
});
// Create the model.
ds.createModel(tablename,
schema.properties,
{
plural: tablename,
core: true,
base: "PersistedModel",
idInjection: false
}
)
// Get an instance of the model.
let model = ds.getModel(tablename);
// Make the model public with a REST API.
app.model(model, { dataSource: dataSourceName, public: true });
// Error
} else {
....
}
// Return
........
});
}

How can I get a report URL via the SSRS Web Service?

In my project I have a web reference to SSRS (2005). I would like to display links that can take users directly to rendered reports. I know I can provide a link such as this one:
http://server/ReportServer/Pages/ReportViewer.aspx?/path/to/report&rs:Command=Render&rc:parameters=false&rs:format=HTML4.0
The question is how can I get that URL from the web service? And if the report takes parameters is there a way to provide values to the web service and have it format the URL for me?
I know I can build the URL myself, but I don't like reinventing wheels.
There are a few things to think of about HOW SSRS works and HOW MUCH TIME you want to invest in monkeying with it.
I. You can traverse the root but I highly doubt you meant that. From the root you can add items whether they are directories or reports. And to add to that you can add the parameter directly to the Rest URI to render a report and you may also output a value as well. For example:
Main part of address root:
http:// <server>/ReportServer/Pages/ReportViewer.aspx?
path to directory:
%2fTest
path to report (labeled it the same name lol)
%2fTest
what to do with it? (render it)
&rs:Command=Render
Put a paremeter in and execute it as well (Yes I called my parameter Test too!)
&Test=Value
Put it all together:
http:// <servername>/ReportServer/Pages/ReportViewer.aspx?%2fTest%2fTest&rs:Command=Render&Test=Value
II. You have a database you can query for traversing things but I believe MS does NOT document it well. Generally it is a SQL Server database named 'ReportServer' on whatever server you installed SSRS on. Generally most items are in the table 'dbo.Catalog' with 'Type' of 2 for reports. You can get their info and even parameters from them there.
III. You want to go full bore and dive into .NET and just talk to the service directly? You can do that too. You need the two main services though to do that:
A: http://<Server Name>/reportserver/reportservice2010 (gets info on existing items on server)
B: http:// <Server Name>reportserver/reportexecution2005 (gets info for in code creating reports to types directly in code)
I had another thread on exporting this here: Programmatically Export SSRS report from sharepoint using ReportService2010.asmx; but you will to get info as well probably. ONCE you have created the proxy classes (or made a reference to the web services) you can do code in .NET like so. These services do all the magic so without them you can't really model much in SSRS. Basically I create a class that you pass the 'SERVER' you need to reference to the class like 'http:// /ReportServer'.
private ReportingService2010 _ReportingService = new ReportingService2010();
private ReportExecutionService _ReportingExecution = new ReportExecutionService();
private string _server { get; set; }
public ReaderWriter(string server)
{
_server = server;
_ReportingService.Url = _server + #"/ReportService2010.asmx";
_ReportingService.Credentials = System.Net.CredentialCache.DefaultCredentials;
_ReportingExecution.Url = _server + #"/ReportExecution2005.asmx";
_ReportingExecution.Credentials = System.Net.CredentialCache.DefaultCredentials;
}
public List<ItemParameter> GetReportParameters(string report)
{
try
{
return _ReportingService.GetItemParameters(report, null, false, null, null).ToList();
}
catch (Exception ex)
{
MessageBox.Show("Getting Parameter info threw an error:\n " + ex.Message);
return new List<ItemParameter> { new ItemParameter { Name = "Parameter Not Found" } };
}
}
public List<CatalogItem> GetChildInfo(string dest)
{
try
{
return _ReportingService.ListChildren("/" + dest, false).ToList();
}
catch (Exception ex)
{
MessageBox.Show("Getting Child info of location threw an error:\n\n" + ex.Message);
return new List<CatalogItem> { new CatalogItem { Name = "Path Does Not exist", Path = "Path Does not exist" } };
}
}
ListChildren is the way to go. You can always set the second parameter to true to return all catalog items when you have reports in many folders.
Dim items As CatalogItem() = rs.ListChildren(reportPath, True)