Issue with WebMethod being Static in C# ASP.NET Codebehind - c#

Due to a problem caused by having multiple forms on a single page, I used an AJAX call to a WebMethod to submit my form instead of using ASP controls. However, in doing this, the previous method I had used to create a new entry into my database no longer works because a WebMethod must be static.
I have authenticated my user already using ASPX authentication, and am trying to retrieve the username and ID of that user with codebehind. The user has already been authenticated on Page_Load, but it seems I cannot access this information through my WebMethod. Is this possible to do inside of a static WebMethod? Thank you for all of your help in advance!
[WebMethod]
public static void CreateJob()
{
Submit_Job();
}
public static void Submit_Job()
{
if (Page.User.Identity.IsAuthenticated)
{
try
{
string username = Context.User.Identity.Name;
}
catch
{
Context.GetOwinContext().Authentication.SignOut();
}
}
var manager = new UserManager();
var usernameDatabase = new ApplicationUser() { UserName = username };
usernameDatabase = manager.Find(username, "password here");
if (usernameDatabase != null)
{
IdentityHelper.SignIn(manager, usernameDatabase, isPersistent: false);
string jobTitle = Request.Form["jobTitle"];
using (var ctx = new CreateUserContext(ConfigurationManager.ConnectionStrings["myconnectionstring"].ConnectionString))
{
Job job = new Job()
{
job_title = jobTitle
};
ctx.Jobs.Add(job);
ctx.SaveChanges();
}
}
}
Edit:
There are errors for example with Page.User.Identity.IsAuthenticated -- Page, Context, and Request all appear that they cannot be static.
The specific error:
(An object reference is required for the non-static field, method, or property 'Control.Page') as well as with Context and Request.

Moving it from a simple comment
I had the same issue recently.
Luckily, whenever a user signs in our application, we store the user information encrypted into a session variable, so I retrieve that information, pass it to our user's class constructor, which decrypts it and I can use my logged in users info without a hassle.
So, my solution is to store the users info in the Session, but be careful what you store. Maybe serialize the users object and store in the session, then, whenever you need it
public void Page_Load()
{
// Retrieve authenticated user information
UserClass userObject = GetUserCredentials();
// Call a method that turns the authenticated user object into a string that contains the users session information. Given the sensivity of this information, might want to try to encrypt it or offuscate it. Store it in a session variable as a string
Session["UserContext"] = userObject.SerializeUser()
/* rest of the page code goes here */
}
[WebMethod(EnableSession=true)]
public static void CreateJob()
{
Submit_Job();
}
public static void Submit_Job()
{
// Lets get the authenticated user information through the session variable. Due to the static nature of the method, we can't access the Session variables directly, so we call it using the current HttpContext
string serializedUserInfo = )HttpContext.Current.Session["UserContext"].ToString();
// Let's create the users object. In my case, we have a overcharged constructor that receives the users serialized/encrypted information, descrypts it, deserializes it, and return a instance of the class with the deserialized information
UserClass userObject = new UserClass(serializedUserInfo);
// Do whatever the method has to do now!
}
On the subject of serialization, a quick google search with "c# object serialization" will bring you several good matches. XML and JSON are 2 of the most used kind of serialization, specially on web methods. Binary serialization is a good option to also obfuscate information of the logged in user

Related

Dynamics CRM Online Object caching not caching correctly

I have a requirement where we need a plugin to retrieve a session id from an external system and cache it for a certain time. I use a field on the entity to test if the session is actually being cached. When I refresh the CRM form a couple of times, from the output, it appears there are four versions (at any time consistently) of the same key. I have tried clearing the cache and testing again, but still the same results.
Any help appreciated, thanks in advance.
Output on each refresh of the page:
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125410:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
To accomplish this, I have implemented the following code:
public class SessionPlugin : IPlugin
{
public static readonly ObjectCache Cache = MemoryCache.Default;
private static readonly string _sessionField = "new_sessionid";
#endregion
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
try
{
if (context.MessageName.ToLower() != "retrieve" && context.Stage != 40)
return;
var userId = context.InitiatingUserId.ToString();
// Use the userid as key for the cache
var sessionId = CacheSessionId(userId, GetSessionId(userId));
sessionId = $"{sessionId}:{Cache.Select(kvp => kvp.Key == userId).ToList().Count}:{userId}";
// Assign session id to entity
var entity = (Entity)context.OutputParameters["BusinessEntity"];
if (entity.Contains(_sessionField))
entity[_sessionField] = sessionId;
else
entity.Attributes.Add(new KeyValuePair<string, object>(_sessionField, sessionId));
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
private string CacheSessionId(string key, string sessionId)
{
// If value is in cache, return it
if (Cache.Contains(key))
return Cache.Get(key).ToString();
var cacheItemPolicy = new CacheItemPolicy()
{
AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
Priority = CacheItemPriority.Default
};
Cache.Add(key, sessionId, cacheItemPolicy);
return sessionId;
}
private string GetSessionId(string user)
{
// this will be replaced with the actual call to the external service for the session id
return DateTime.Now.ToString("yyyyMMdd_hhmmss");
}
}
This has been greatly explained by Daryl here: https://stackoverflow.com/a/35643860/7708157
Basically you are not having one MemoryCache instance per whole CRM system, your code simply proves that there are multiple app domains for every plugin, so even static variables stored in such plugin can have multiple values, which you cannot rely on. There is no documentation on MSDN that would explain how the sanboxing works (especially app domains in this case), but certainly using static variables is not a good idea.Of course if you are dealing with online, you cannot be sure if there is only single front-end server or many of them (which will also result in such behaviour)
Class level variables should be limited to configuration information. Using a class level variable as you are doing is not supported. In CRM Online, because of multiple web front ends, a specific request may be executed on a different server by a different instance of the plugin class than another request. Overall, assume CRM is stateless and that unless persisted and retrieved nothing should be assumed to be continuous between plugin executions.
Per the SDK:
The plug-in's Execute method should be written to be stateless because
the constructor is not called for every invocation of the plug-in.
Also, multiple system threads could execute the plug-in at the same
time. All per invocation state information is stored in the context,
so you should not use global variables or attempt to store any data in
member variables for use during the next plug-in invocation unless
that data was obtained from the configuration parameter provided to
the constructor.
Reference: https://msdn.microsoft.com/en-us/library/gg328263.aspx

Use value from a method in another form

I have the following scenario: a program I made has three type of user accounts: superuser, administrator and consultant.
The login forms works well and through a method I can get the type of the user and open the respective main form of said user.
The problem is this: there are some forms that the users share. But, I don't know how to have the application know which of the three main forms it must return depending of the user.
Question is: there is a way for keep the value (user type) from the login form and use it on the other forms?
Here is how I get the value of type in the database:
public string sacartipo()
{string tipo = "";
username = usuario.Text;
obj.Usuario = usuario.Text;
password = contra.Text;
obj.Contrasena = contra.Text;
tipo = obj.Logeo(username, password);
return tipo; //This is the variable that stores the type of user.
}
Make every form's constructor accept the user type, so that whenever you show a new form you tell it which user will use it.
You can store the user type(after the user logs in) in a session variable like Session["role"]=value and use it anywhere you want.
You can store users in an enum
public enum Users { Superuser, Administrator, Consultant }
and have a static object of Users type in your application that is seen everywhere (global)
public Users User;
on Login assign the appropriate value to the User static object, for eg.
MyApplication.User = Users.Consultant;
and in each form check user's permission on Load, eg.
var adminPageAllowedUsers = new[] {Users.SuperUser, Users.Administrator};
if(admingPageAllowedUsers.Contains(MyApplication.User))
{
//do the rest
}
else
{
//redirect user to another form
}
this is the logic I wanted to give you, and the codes are not tested, so excuse me for possible errors.
Assume sacartipo() was a method of Form1 and you want to call it an get the return value, you add the following method in Form2:
string GetTipo() {
var currentContext=SynchronizationContext.Current;
var tipoLocal=default(string);
SendOrPostCallback d=
dummy => {
using(var x=new Form1())
tipoLocal=x.sacartipo();
};
if(null!=currentContext)
currentContext.Send(d, null);
else
d(null);
return tipoLocal;
}
In the code of Form2, you call GetTipo() and what you expect to do have been done.

How do you access an asp.net code behind session object member variable in javascript?

I have a class that handles all of my session variables in my asp.net application. Moreover, I sometimes store objects in the session variables so as to allow me to code very similar to a normal .net application.
For example, here is an object in a session variable:
public cUser User
{
get { return (cUser)HttpContext.Current.Session["CurrentUser"]; }
set { HttpContext.Current.Session["CurrentUser"] = value; }
}
And here is the instance of MySessionClass:
public static MySessinClass Current
{
get
{
MySessionClass session = (MySessionClass)HttpContext.Current.Session["MySessionClass"];
if (session == null) {
session = new MySessionClass();
HttpContext.Current.Session["MySessionClass"] = session;
}
return session;
}
}
So, in my code behind aspx page, I would just do something such as
int userID = MySessionClass.Current.User.UserID;
This works great and I love it. However, I want to apply the same principle in javascript from my aspx page:
var userID = <%=MySessionClass.Current.User.UserID%>;
However, when I access that webpage, I get an error saying that it does not recognize the MySessionClass object.
So, given the context that I am in, what would be the best way to reference that UserID variable, from the object, from the session, in javascript? Maybe my syntax to reference it is off or I am going at it the wrong way. Any help would be appreciated, thank you.
Since you've defined it as a static member, you would need to qualify your reference with the page type:
var userID = <%=MyPageType.MySessionClass.Current.User.UserID%>;
That should be enough.

How to invalidate a C# WCF session if login is incorrect

I am writing a remote service for an application using WCF, in which login information is kept in a database. The service requires session establishment through a login or account creation call. There is no ASP involved.
Now, when a client starts a session by calling an exposed IsInitiating method, I check the account data provided against the information on the database and, if it is not correct, I want to invalidate that session and force the client to start again with a call to an IsInitiating method.
Looking at some other questions, I have found pros and cons for two ways to invalidate a session. One does so the hard way, by throwing a FaultException; the other with softer manners, storing accepted session IDs.
Now, the first one, although achieving what I desire, is way too aggressive, given that incorrect logins are part of the normal flow of the application. The second one, on the other hand, allows the client to continue calling non-initiating methods, eventhough they will be rejected, while also incurring in a considerable code overhead on the service due to the added thread safety requirements.
So, the question: Is there a third path which allows the service to invalidate the session initialization and communicate it to the client, so it is forced to make a new IsInitiating call?
A reduced version of the code I have:
[DataContractAttribute]
public class AccountLoginFault
{
public AccountLoginFault (string message)
{
this.Message = message;
}
[DataMemberAttribute]
public string Message { get; set; }
}
[ServiceContract (SessionMode = SessionMode.Required)]
public interface IAccountService
{
[OperationContract (
IsInitiating = true)]
[FaultContractAttribute (
typeof (AccountLoginFault),
ProtectionLevel = ProtectionLevel.EncryptAndSign)]
bool Login (AccountData account, out string message);
}
[ServiceBehavior (
ConcurrencyMode = ConcurrencyMode.Single,
InstanceContextMode = InstanceContextMode.PerSession)]
public class AccountService : IAccountService
{
public bool Login (AccountData account, out string message)
{
UserManager userdb = ChessServerDB.UserManager;
bool result = false;
message = String.Empty;
UserData userData = userdb.GetUserData (account.Name);
if (userData.Name.Equals (account.Name)
&& userData.Password.Equals (account.Password))
{
// Option one
// Get lock
// this.AcceptedSessions.Add (session.ID);
// Release lock
result = true;
} else
{
result = false;
// Option two
// Do something with session context to mark it as not properly initialized.
// message = "Incorrect account name or password. Account provided was " + account.Name;
// Option three
throw new FaultException<AccountLoginFault> (
new AccountLoginFault (
"Incorrect account name or password. Account provided was " + account.Name));
}
return result;
}
}
Throwing an exception is by far the easiest option because WCF enforces that the session cannot be re-used. From what I gather, what you would like the third party component to accomplish comes quite close to this functionality. But, instead of forcing the client to call IsInitialized again, you would force the client to create a new connection. This looks like a very small difference to me.
An alternative would be to have a private variable bool _authorised and check this variable at every method call.
Do something like this:
public ConnectResponseDTO Connect(ConnectRequestDTO request) {
...
if(LoginFailed)
OperationContext.Current.OperationCompleted += FaultSession;
}
private void FaultSession(object sender, EventArgs e) {
var context = (OperationContext) sender;
context.Channel.Abort();
}
This will fault the channel and the client will havce to reesatablish the session.

How do I maintain user login details in a Winforms application?

Hi can I'm very new to windows forms. Here I want to maintain state (like session in web applications) in windows forms.
Actually i want to store user login details in session. But i think there is no concept of session in winforms. So what is the alternative method to handle this type of situation.
Regards,
Nagu
There is no concept of Session variables in windows forms. What you can do is:
Create a internal class that holds the User name and password and any other variables and enumerations needed across the application (Something like Common.cs). These can be accessed through public properties across the application.
Have a parameterized constructor for all the forms and send the user name and the password whenever you are showing the form.
public class MyForm : Form
{
private string userName;
private string password;
}
Since windows forms are statefull (opposed to stateless for web forms), you can just use a field in your Form class.
You need to think more in terms of scope than session; as long as an object remains in scope you will be able to pull values from its public properties/fields.
In your case it would make sense to store the user details in a static class:
public static class LoginInfo
{
public static string UserID;
}
Now you can access the UserID simply from anywhere in your code:
MessageBox.Show(LogInfo.UserID);
In winforms you can use variables that are exposed to other forms through methods or properties.
You can also use static variables.
In the following example, you would have a controller for each window or group of windows. The controllers would be passed to one another depending on how they need to collaborate (what knowledge they need to share, etc). The important thing is to keep your application state in the controllers and limit the windows to handling user input and events.
// pseudocode, because I do not know WinForms that much
class MainController
{
private Guid securityToken;
public Guid SecurityToken
{
get { return securityToken; }
set { securityToken = value; }
}
}
class LoginWindowController
{
MainController mainController;
LoginWindow loginWindow;
public LoginWindowController(MainController mainController)
{
this.loginWindow = new LoginWindow(this);
this.mainController = mainController;
}
public void Show()
{
loginWindow.IsVisible = true;
}
public void HandleLogin()
{
Guid token =
myobject.Authenticate(loginWindow.Username, loginWindow.Password);
if (token != Guid.Empty)
{
mainController.SecurityToken = token;
}
}
}
In reply to your comment to my first reply:
You are creating the new instance of the Login form. How is that supposed to have values. It is a Login form and hence I believe you will be closing it as the user enters user name and password and clicks OK or whatever.
Then, there is no way you can get the values from the Login form as it is closed. If you need to stick to this approach, this could be a way:
Do not close the Login form, just hide it.
Pass the current instance to the next form. Like this:
In Login form:
NextForm nxt = new NextForm(this);
The constructor of NextForm will look like:
public NextForm(LoginForm frm){
// Code here
}
Now in NextForm, you can access the properties through "frm".
from a program i was using with a login form to store global variables and to store the password as a secure string. Within the program I am able to "run as" a specific user when I call processes. You can use it for other things besides process.start.
//to run process as another user
//create these global variables on the first
//form or piece of code in your program
class usernameGlobalVariable
{
public static string var = "";
}
class passwordGlobalVariable
{
public static SecureString var;
}
// use these as event handlers for text fields
//for your login form
private void usernameTextBox_TextChanged(object sender, EventArgs e)
{
usernameGlobalVariable.var = usernameTextBox.Text;
}
private void passwordTextBox_TextChanged(object sender, EventArgs e)
{
SecureString passWord = new SecureString();
foreach (char c in passwordTextBox.Text.ToCharArray())
{
passWord.AppendChar(c);
}
passwordGlobalVariable.var = passWord;
}
//put this on form that launches program
//this assigns variables for process.start
//change fileName to path and name of program
// use \\ in paths
string fileName = "c:\\hdatools\\Ping2.exe";
string arguments = "";
string domain = "domain";
//start the process
//put this on the page along w the above variables that
//launches the app as another user
//the .var variables are global
{
Process.Start(
fileName,
arguments,
usernameGlobalVariable.var,
passwordGlobalVariable.var,
domain);
}
It's unclear to me whether you are talking about a web application or a stand along application based upon one of your responses. If you are talking about a web application, you can use the Session properties on the Page object.
It would set the variables like this:
Session["username"] = "Username";
Session["fullname"] = "User's full name";
You could then access like:
lblGreetings.Text = "Hi " + Session["fullname"];
Is that what you were after?

Categories