ReportViewer control using ReportServerCredentials.NetworkCredentials - asp.net-mvc-2

I'm using the ReportViewer control to access a SSRS 2008 Report from a VS2010 ASP.NET MVC 2 Web Application deployed on IIS7 with the following setup:
Forms Authentication and Anonymous Authentication enabled
ASP.NET Impersonation disabled
App pool identity configured to use local user account that has access rights to specific folders on the server required by the application
The webserver is not part of the domain but the SSRS server is on the domain
Since I need to use separate credentials to access SSRS Reports, I have implemented IReportServerCredentials as explained here:
http://msdn.microsoft.com/en-us/library/microsoft.reporting.webforms.ireportservercredentials(v=vs.100).aspx
The problem I'm having is it always goes to IReportServerCredentials.ImpersonationUser instead of IReportServerCredentials.NetworkCredentials where I need it to go because I'm retrieving the required credentials off web.config.
Maybe I'm missing something simple here but I've tried different combination of these settings and have had no luck. Any pointers on how I could get this working would be much appreciated!
My code:
[Serializable]
public sealed class MyReportServerCredentials :
IReportServerCredentials
{
public WindowsIdentity ImpersonationUser
{
get
{
// Use the default Windows user. Credentials will be
// provided by the NetworkCredentials property.
return null;
}
}
public ICredentials NetworkCredentials
{
get
{
// Read the user information from the Web.config file.
// By reading the information on demand instead of
// storing it, the credentials will not be stored in
// session, reducing the vulnerable surface area to the
// Web.config file, which can be secured with an ACL.
// User name
string userName =
ConfigurationManager.AppSettings
["MyReportViewerUser"];
if (string.IsNullOrEmpty(userName))
throw new Exception(
"Missing user name from web.config file");
// Password
string password =
ConfigurationManager.AppSettings
["MyReportViewerPassword"];
if (string.IsNullOrEmpty(password))
throw new Exception(
"Missing password from web.config file");
// Domain
string domain =
ConfigurationManager.AppSettings
["MyReportViewerDomain"];
if (string.IsNullOrEmpty(domain))
throw new Exception(
"Missing domain from web.config file");
return new NetworkCredential(userName, password, domain);
}
}
public bool GetFormsCredentials(out Cookie authCookie,
out string userName, out string password,
out string authority)
{
authCookie = null;
userName = null;
password = null;
authority = null;
// Not using form credentials
return false;
}
}
this.MyReportView.ServerReport.ReportServerCredentials = new MyReportServerCredentials();
Thanks

I implement this same class on every reporting page I need and it works correctly.
Check your web.config file.
I have:
<configuration>
<system.web>
<authentication mode="Windows"/>
</configuration>
</system.web>
Yes. Authentication mode "Windows".
However, I have worked also using Forms Authentication.
So I think this is more a permissions issue.
Check this post:
UAC Windows 7 (Proffesional) 64 bit and SSRS 2008 R2 (10.50.1617) 64 bit
Also this post:
Reporting Services 2008: asking for username and password

Related

How do I get current user in .NET Core Web API (from JWT Token)

After a lot of struggling (and a lot of tuturials, guides, etc) I managed to setup a small .NET Core REST Web API with an Auth Controller issuing JWT tokens when stored username and password are valid.
The token stores the user id as sub claim.
I also managed to setup the Web API to validate those tokens when a method uses the Authorize annotation.
app.UseJwtBearerAuthentication(...)
Now my question:
How do I read the user id (stored in the subject claim) in my controllers (in a Web API)?
It is basically this question (How do I get current user in ASP .NET Core) but I need an answer for a web api. And I do not have a UserManager. So I need to read the subject claim from somewhere.
The accepted answer did not work for me. I'm not sure if that's caused by me using .NET Core 2.0 or by something else, but it looks like the framework maps the Subject Claim to a NameIdentifier claim. So, the following worked for me:
string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
Note that this assumes the Subject sub Claim is set in the JWT and its value is the user's id.
By default, the JWT authentication handler in .NET will map the sub claim of a JWT access token to the System.Security.Claims.ClaimTypes.NameIdentifier claim type. [Source]
There is also a discussion thread on GitHub where they conclude this behavior is confusing.
You can use this method:
var email = User.FindFirst("sub")?.Value;
In my case I'm using the email as a unique value
It seems a lot of people are looking at this question so I would like to share some more information I learned since I asked the question a while back.
It makes some things more clear (at least for me) and wasn't so obvious (for me as .NET newbie).
As Marcus Höglund mentioned in the comments:
It should be the same for "web api"..In ASP.NET Core Mvc and Web Api are merged to use the same controller.
That's definitely true and absolutely correct.
Because it is all the same across .NET and .NET Core.
Back than I was new to .NET Core and actually the full .NET world. The important missing information was that in .NET and .NET Core all the authentication can be trimmed down to System.Security.Claims namespace with its ClaimsIdentity, ClaimsPrinciple and Claims.Properties. And therefore it is used in both .NET Core controller types (API and MVC or Razor or ...) and is accessible via HttpContext.User.
An important side note all of the tutorials missed to tell.
So if you start doing something with JWT tokens in .NET don't forget to also get confident with ClaimsIdentity, ClaimsPrinciple and Claim.Properties. It's all about that. Now you know it. It was pointed out by Heringer in one of the comments.
ALL the claim based authentication middlewares will (if correctly implemented) populate the HttpContext.User with the claims received during authentication.
As far as I understand now this means one can safely trust on the values in the HttpContext.User. But wait a bit to know what to mind when selecting middleware. There are a lot of different authentication
middleware already available (in addition to .UseJwtAuthentication()).
With small custom extension methods you can now get the current user id (more accurate the subject claim) like that
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value; }
Or you use the version in the answer of Ateik.
BUT WAIT: there is one strange thing
The next thing that confused me back than: according to the OpenID Connect spec I was looking for "sub" claim (the current user) but couldn't find it. Like Honza Kalfus couldn't do in his answer.
Why?
Because Microsoft is "sometimes" "a bit" different. Or at least they do a bit more (and unexpected) things. For example the official Microsoft JWT Bearer authentication middleware mentioned in the original question.
Microsoft decided to convert claims (the names of the claims) in all of their official authentication middleware (for compatibility reasons I don't know in more detail).
You won't find a "sub" claim (though it is the single one claim specified by OpenID Connect). Because it got converted to these fancy ClaimTypes. It's not all bad, it allows you to add mappings if you need to map different claims into a unique internal name.
Either you stick with the Microsoft naming (and have to mind that when you add/use a non Microsoft middleware) or you find out how to turn the claim mapping of for the Microsoft middleware.
In case of the JwtBearerAuthentication it is done (do it early in StartUp or at least before adding the middleware):
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
If you want to stick with the Microsoft namings the subject claim (don't beat me, I am not sure right now if Name is the correct mapping):
public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier, StringComparison.OrdinalIgnoreCase))?.Value; }
Note that the other answers use the more advanced and way more convenient FindFirst method. Though my code samples show it without those you may should go with them.
So all your claims are stored and accessible (via one name or the other) in the HttpContext.User.
But where is my token?
I don't know for the other middleware but the JWT Bearer Authentication allows to save the token for each request. But this needs to be activated (in StartUp.ConfigureServices(...).
services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => options.SaveToken = true);
The actual token (in all it's cryptic form) as string (or null) can then be accessed via
HttpContext.GetTokenAsync("Bearer", "access_token")
There has been an older version of this method (this works for me in .NET Core 2.2 without deprecated warning).
If you need to parse and extract values from this string may the question How to decode JWT token helps.
Well, I hope that summary helps you too.
If you use Name to store the ID here:
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
In each controller method you can get the ID of the current user by:
var claimsIdentity = this.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;
I used the HttpContext and it works well:
var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
email = identity.FindFirst(ClaimTypes.Name).Value;
}
you can do this using.
User.Identity.Name
In my case I set ClaimTypes.Name to unique user email before JWT token generation:
claims.Add(new Claim(ClaimTypes.Name, user.UserName));
Then I stored unique user id to ClaimTypes.NameIdentifier:
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
Then in the controller's code:
int GetLoggedUserId()
{
if (!User.Identity.IsAuthenticated)
throw new AuthenticationException();
string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
return int.Parse(userId);
}
Mine worked using the following code in .net core 5 web api
User.Claims.First(x => x.Type == "id").Value;
asp.net core identity get user id
public async Task<IActionResult> YourMethodName()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
var userName = User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
string userEmail = applicationUser?.Email; // will give the user's Email
}
.net core identity get user id
public static class ClaimsPrincipalExtensions
{
public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(loggedInUserId, typeof(T));
}
else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
{
return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
}
else
{
throw new Exception("Invalid type provided");
}
}
public static string GetLoggedInUserName(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Name);
}
public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Email);
}
}

How can I add users to the web application manually?

I am writing a closed ASP .NET MVC 5.1 application. Only authorized people can get in. I want to delete register action from the application. Can I add users manually by adding new rows in database associated with the web application?
How to do so in Visual Studio 2013?
It's generally not easy to add users directly to the database tables, as there are many interrelated concerns such as permissions, roles, and password hashing.
However, it is possible to "seed" the database in code. The following is an example of a Seed function using ASP.NET Identity.
protected override void Seed(ApplicationDbContext context)
{
//First, access the UserManager
var store = new UserStore<ApplicationUser>(context);
var manager = new UserManager<ApplicationUser>(store);
//Secondly, Create the user account
var user = new ApplicationUser
{
UserName = "ExampleUser",
UserProfileInfo = new UserProfileInfo
{
FirstName = "Example",
LastName = "User",
EmailID = "exampleuser#testdomain.com"
}
};
//Last, add the user to the database
manager.Create(user, "password123");
}
This function will run the next time that Update-Database is run in the NuGet Package Manager Console.

Jira 5.2 Seraph SSO Login behind reverse proxy

Since a few days I'm trying to enable SSO for Jira 5.2 and figured out, that the help page from Jira is outdated.
Each example uses an old version of atlassian-seraph (Jira 5.2 uses 2.6.0).
Goal:
I want to get automatically logged in into Jira if I'm logged in into Webseal (reverse proxy).
Background:
Jira is behind a reverse proxy (see picture).
This proxy authentificatates the user and holds the session.
If I'm logged in I want to be logged in in Jira, too
The only information provided is the user name
Question:
How to write a custom login module that reads the username from http_header and authentificates the user?
Links:
https://confluence.atlassian.com/display/DEV/Single+Sign-on+Integration+with+JIRA+and+Confluence
http://docs.atlassian.com/atlassian-seraph/latest/sso.html
https://answers.atlassian.com/questions/23245/how-to-integrate-jira-with-my-company-s-sso
In the end i figured it out by myself:
You need a custom authenticator
public class MyCustomAuthenticator extends DefaultAuthenticator {
protected boolean authenticate(Principal user, String password)
throws AuthenticatorException {
return true;
}
protected Principal getUser(String username) {
return getCrowdService().getUser(username);
}
private CrowdService getCrowdService() {
return (CrowdService)ComponentManager.getComponent(CrowdService.class);
}
}
Add the MyCustomAuthenticator to seraph-config.xml
<authenticator class="com.company.jira.MyCustomAuthenticator"/>
Write a Custom Filter to set the user name from http-header
public class CustomFilter extends PasswordBasedLoginFilter {
#Override
protected UserPasswordPair extractUserPasswordPair(
HttpServletRequest request) {
String username = request.getHeader("iv-header");
if (username != null && username.trim().length() != 0) {
return new PasswordBasedLoginFilter.UserPasswordPair(
username, "DUMMY", false);
}
return null;
}
}
Replace the filter within the web.xml
<filter>
<filter-name>login</filter-name>
<filter-class>com.company.jira.CustomFilter</filter-class>
</filter>
These jar's are needed for Jira 5.2
embedded-crowd-api-2.6.2
jira-core-5.2.1
atlassian-seraph-2.6.0
I am not familiar with Jira authentication, but I do understand well the SiteMinder/ WebSeal authentication.
Both systems authenticate user and send the user name in an HTTP header.
The name of HTTP header can be configured. Also, they can send additional user properties, like the user email in the additional HTTP headers.
TO authenticate a user behind SiteMinder/ WebSeal it is just required to take the HTTP header and to create an application session using the user name from the header.
You definitely can solve it in Jira. You have 2 options:
To use already created SiteMinder authenticator:
https://confluence.atlassian.com/display/DEV/SiteMinder+Custom+Seraph+Authenticator+for+Confluence
The problem that I did not find how to configure the HTTP header name for the user name header. It assumes that the header name is uid
You need to configure the header uid in WebSeal or try to obtain sources and make the header name configurable.
Implement your own authenticator according to your link:
http://docs.atlassian.com/atlassian-seraph/latest/sso.html
Obtain the user name using the code
httpServletRequest.getHeader(userNameHeaderName);

StarterSTS as a backup authentication store

I have setup startersts as the ClaimsProviderTrust in ADFS 2.0. Configured the login form to be displayed by changing the ADFS 2.0 web.config entry. I am trying to customize the login process here such that when some one enter's email address he will be logging into StarterSts otherwise through Active Directory which is provided by the ADFS 2.0 Installation. I had tried the code in the post http://blogs.msdn.com/b/card/archive/2010/01/27/customizing-the-ad-fs-2-0-sign-in-web-pages.aspx
protected void SubmitButton_Click( object sender, EventArgs e )
{
try
{
SignInWithTokenFromOtherSTS( UsernameTextBox.Text, PasswordTextBox.Text );
}
catch ( Exception ex )
{
//
// Fall back to signing in locally with the given username and password.
//
SignIn( UsernameTextBox.Text, PasswordTextBox.Text );
}
}
I just don't know what is the values for variables OtherSTSAddress and YourSTSAddress. Is there any more configuration i need to do after making this address correct
As per the link you provided:
const string OtherSTSAddress = "https://ipsts.federatedidentity.net/SecurityTokenService/InteropSts.svc/Sts";
const string YourSTSAddress = "https://your-sts/adfs/ls/";
So the former is the WS-Trust endpoint of StarterSTS.
The latter is the ADFS WS-Fed endpoint. Just substitute the URL of the box where you installed ADFS in the "your-sts" section.
According to the article, that's all you need.

Where do I store my Azure role settings that are not to be stored as plain text?

Looks like the standard way of storing settings of Azure roles is under <ConfigurationSettings> tag in the .cscfg file. Looks convenient, but the file is not encrypted in any way - it is an XML that is uploaded to the Azure portal as plain text and is stored as plain text and can be edited at any time.
In my application I'll need settings that should not be stored as plain text - like for example a password to my SQL Azure database. I'd rather not have a plaintext XML file with that password. How do I store such role settings?
The typical way to do this on-premises is to use DPAPI on a single machine. Of course, this has problems on a web farm. To work around this, you can share a single key on each machine and encrypt. The easiest way to do this is to use certificate based encryption.
Nothing against the SQL Azure posts referenced by Michael, but that had to be the longest series ever to tell you to use the PKCS12 configuration provider. The only reason to use that provider is that it works in conjuction with the built-in tooling from ASP.NET that can read from appSettings automatically. It doesn't help with ServiceConfiguration that needs to change.
If all you want to do is securely protect a setting (typically in ServiceConfig) and you don't mind writing a utility class to do it, then you can use these two functions with any certificate (with private key) uploaded to Windows Azure. This is exactly how the password for remote access is encrypted in the Service Configuration.
Encrypt:
var passwordBytes = UTF8Encoding.UTF8.GetBytes("p#ssw0rd");
var contentInfo = new ContentInfo(passwordBytes);
var thumb = "F49E41878B6D63A8DD6B3650030C1A06DEBB5E77";
var env = new EnvelopedCms(contentInfo);
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Where (xc => xc.Thumbprint == thumb).Single();
env.Encrypt(new CmsRecipient(cert));
Convert.ToBase64String(env.Encode()).Dump();
}
finally
{
if (store != null)
store.Close();
}
Decrypt:
var thumb = "F49E41878B6D63A8DD6B3650030C1A06DEBB5E77";
var cipherText = "MIIBrwYJKoZIhvcNAQcDoIIBoDCCAZwCAQAxggFgMIIBXAIBADBEMDAxLjAsBgNVBAMTJWR1bm5yeTd0YWIucmVkbW9uZC5jb3JwLm1pY3Jvc29mdC5jb20CECNRAOTmySOQTA2HuEpAcD4wDQYJKoZIhvcNAQEBBQAEggEAkIxJNnCb1nkZe3Gk2zQO8JQn2hOYM9+O9yx1eprTn7dCwjIlYulUMIYwFCMDI7TiYCXG7cET2IP/ooNBPYwxzAvEL5dUVIMK9EDE0jyRP3sGPGiSvG0MW8+xZuQx4wMGNSwm2lVW1ReVRGEpTeTcUFSBCPvXsULpbqCqXtSTgjsHngxgOKjmrWBIdrxCDxtfzvNPgSQ2AVqLTRKgFTN9RHUwJJ2zhGW+F+dBfxai3nlr7HN7JKiIdlNA0UjCd/kSIZqNfPlvd2V58RBMpkW+PEp3vpBa/8D/fhU3Qg/XBNXhroES7aVDB5E16QYO6KgPdXMCpLcQ4e9t1UhokEwUizAzBgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECEImLeoQJeVkgBCQ94ZxmHnVkBWrID+S4PEd";
X509Store store = null;
try
{
store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var cert = store.Certificates.Cast<X509Certificate2>().Where (xc => xc.Thumbprint == thumb).Single();
var bytes = Convert.FromBase64String(cipherText);
var env = new EnvelopedCms();
env.Decode(bytes);
env.Decrypt();
Encoding.UTF8.GetString(env.ContentInfo.Content).Dump();
}
finally
{
if (store != null)
store.Close();
}
Check the SQL Azure blog, they blogged exactly about this!
Securing Your Connection String in Windows Azure: Part 1 (original)
Securing Your Connection String in Windows Azure: Part 2 (original)
Securing Your Connection String in Windows Azure: Part 3 (original)
also, previous posts at
http://blogs.msdn.com/b/sqlazure/archive/tags/security/