I have a set of commands like:
.kick
.unban
.ban
.unvouch
.vouch
.add
.del
.say
Those commands are used in a chat room where I have several users with different access, for example:
Admin is allowed to use all commands.
Moderator is allowed to use .kick, .vouch .unvouch .say
Vip is allowed to use .say
Basic cannot use any command
When a command is used it goes to a bot that is present in the room, that bot will them verify the user, access and everything before performing the command.
Initially I have a user class assigned to a list:
public class Users
{
public string Name { get; set; }
public string Comments { get; set; }
public string Access { get; set; }
}
public List<Users> userList = new List<Users>();
Now I want to implement an easy way to query/check/verify if a given user has access to use a given command, but I am not sure on how to approach it.
I was thinking about having a second class assigned to a list something like the this:
public class UserAccess
{
public string AccessLevel { get; set; }
public List<string> Commands = new List<string>();
}
public List<UserAccess> accessList = new List<UserAccess>();
And query it with something like:
var user = userList.Find(x => x.Name == currentUser);
if (user != null && accessList.Exists(x => x.AccessLevel == user.Access && x.Commands.Contains(str_cmd))
{
// use the command
}
else
{
// cannot use the command
}
As I mentioned above, I have a background worker that is constantly reading the chat messages to capture when a user has typed a command which will then verify and process everything in a queue.
Registered users and access level are filled from my website API which returns JSON to my application when it starts and updates data every now and then when major commands are issued.
This is just an example, I could be over thinking the idea but I did like to hear some advices and ideas of how I could deal with this ?
You can try something like this. Although you may want to define your access list through a Db or through attributes/properties on where your actual commands are defined.
public class User
{
public static readonly UserAccess[] AccessList = {
new UserAccess() { AccessLevel = "Admin",
Commands = {".kick",".ban",".unban"}
},
new UserAccess() { AccessLevel = "User",
Commands = {".add",".del"}
},
new UserAccess() { AccessLevel = "Vip",
Commands = {".say"}
}};
public string Name { get; set; }
public string Comments { get; set; }
public string Access { get; private set; } //Assuming you can't modify this so we add the next property
public UserAccess AccessLevel { get; private set; }
public User(string access)
{
this.Access = access;
this.AccessLevel = AccessList.FirstOrDefault(x => x.AccessLevel == access);
}
}
public class UserAccess
{
public string AccessLevel { get; set; }
public List<string> Commands = new List<string>();
public bool HasCommand(string command)
{
return this.Commands.Any(x => x == command);
}
}
looks like an IRC bot or services application. the "usual" approach for this is to have a list of commands/privileges with an int representing the needed access level, and a list of users with the corresponding access-level ... whenever a command/privilege is used, try to get the users accesslevel from the list, or a default value (0) if the user is not on the list. then compare that value to the value of the used command/privilege
advantage: rather easy to implement
disadvantage: restricts commands/privileges levels to be somewhat hierarchical
alternative that would allow arbitrary complex permissions:
used Types:
User - represents a user
Group - represents a group of User-Objects
Context - represents the context of the ruleset (in terms of IRC this could be a channel)
Privilege - represents a privilege or command that can be used
Permission - says that a Privilege is either granted or denied
User- and Group-Objects can be associated with a list of Permissions by the context
a context stores a list of effective permissions for users that hold permissions in this context, or Users that are members of groups that hold permissions in this context. those effective permissions are determined the following way:
for each user itterate all group memberships
for each group itterate all permissions
if the permission is denying a Privilege, add this privilege as denied to the effective permissions of this user, overwriting any permission for this privilege that may already be present in that list
else, if the permission is granting a privilege add it to the effective permissions only if no permission for this privilege is present in the list
finally itterate over the users permissions
add the permission to the effective permissions list, overwriting any permission for this privilege that may already be present in the list.
all privileges get a default permission, initialized as "denied" (stored in the context)
when permissions are changed at runtime, the effective permissions are to be rebuilt.
when the system has to check a permission, it looks up the user and eventually does authentication. if the user is authenticated, the effective permissions are looked up by the context. if effective permissons are found for that user, the requested privilege is looked up, and the corresponding permission is checked. if we either get granted or denied, the check is completed. if no permission is found, the default permission for that privilege is used.
Related
I have 8 buttons, each performs a different task, i.e. edit, delete, create etc, and a context​Menu for each of the task
I've a table called Moderations in DB, which consists of bools i.e. groupTitle, canEdit, canDelete, canCreate..... groupTitle is string not bool
I have a bool function canDoIt(task, userid) to check whether the logged in user (which will have specific groupTitle), can perform or can't (function return true or false for provided task, in short)
Suppose, I want to check whether a logged in user can perform the task or not, check via canDoit(task, userid), and If he cannot, the button will be disabled otherwise won't....
OnForm_Load I throw the function (or may be another time when I need it) and check for each button, i.e.
btnEdit.Enabled = canDo("canEdit", userID)
btnDelete.Enabled = canDo("canDelete", userID)
btnCreate.Enabled = canDo("canCreat", userID)
cnxMenuEdit.Enabled = canDo("canEdit", userID)
cnxMenuDelete.Enabled = canDo("canDelete", userID)
.
.
.
.....and so on and so forth.
My method work fine and good but I have doubts and questions.
First question, is good to be so?
Second question, is it professional?
Another is, will that effect program or database performance?
You need to keep your logged in user's access rights in memory.
Create some user model that will contains list of access right, load it to some static object when user logging in and then you can check access when you need.
Something like that
public class UserModel
{
public List<UserAccessRight> AccessRights {get; set;}
}
public class UserAccessRight
{
public string Name {get; set;}
}
public static class SomeAuthHelperClass
{
public UserModel CurrentUser {get; set;}
// some helper methods to retrieve data etc.
public static CanDo(string accessRight)
{
return CurrentUser.AccessRights.Contains(ar => ar.Name.Equals(accessRight);
}
}
.....
btnEdit.Enabled = SomeAuthHelperClass.CanDo("EditSomething");
Does anyone have an actual example of how to use int RefId as proposed in this question?
I am trying to get authentication going but I need to marry up the userAuth data with my own user table. The problem is I have no idea how to pass an additional parameter to the "/register" method. I guess I'm looking for an event like "OnAddUser" which will allow me to throw some additional parameters into the mix.
I managed to get the user registration working pretty quickly, it was super easy. Maybe the problem is that it was too easy? I can see it work but I can't figure out how to get between it and the database.
Either the dictionary approach or the RefId approach will probably work for me, it's just no obvious to me how use either.
Is it possible to override the create user altogether? I found this code:
MyServices.cs
which looks like it's doing the create user in place of "/register" but there are some other articles that suggest that you can't override the ServiceStack DTOs, you have to use the default tables.
You could include your own Register Service by using a copy of the RegisterService source code and modify it to suit your needs, e.g. Use a custom Register DTO with the additional properties you want.
But you can easily pass additional params without changing the existing Register DTO by adding it to the ?querystring which you can access inside your Services with:
var myParam = base.Request.QueryString["myParam"];
Otherwise the way to add your Custom Logic during registration or Authentication is to tap into the existing Session or Auth Events.
TechStacks has an example of this in its CustomAuthUserSession:
public class CustomUserSession : AuthUserSession
{
public string DefaultProfileUrl { get; set; }
public string GithubProfileUrl { get; set; }
public string TwitterProfileUrl { get; set; }
public override void OnAuthenticated(IServiceBase authService,
IAuthSession session,
IAuthTokens tokens,
Dictionary<string, string> authInfo)
{
base.OnAuthenticated(authService, session, tokens, authInfo);
var appSettings = authService.TryResolve<IAppSettings>();
var userAuthRepo = authService.TryResolve<IAuthRepository>();
var userAuth = userAuthRepo.GetUserAuth(session, tokens);
var dbConnectionFactory = authService.TryResolve<IDbConnectionFactory>();
foreach (var authTokens in session.ProviderOAuthAccess)
{
if (authTokens.Provider.ToLower() == "github")
{
GithubProfileUrl = session.GetProfileUrl();
}
if (authTokens.Provider.ToLower() == "twitter")
{
TwitterProfileUrl = session.GetProfileUrl();
if (appSettings.GetList("TwitterAdmins").Contains(session.UserName)
&& !session.HasRole(RoleNames.Admin))
{
userAuthRepo.AssignRoles(userAuth, roles:new[]{RoleNames.Admin});
}
}
DefaultProfileUrl = GithubProfileUrl ?? TwitterProfileUrl;
using (var db = dbConnectionFactory.OpenDbConnection())
{
var userAuthInstance = db.Single<CustomUserAuth>(x =>
x.Id == this.UserAuthId.ToInt());
if (userAuthInstance != null)
{
userAuthInstance.DefaultProfileUrl = this.DefaultProfileUrl;
db.Save(userAuthInstance);
}
}
}
}
}
Which fetches the Profile Url of the User when they login via GitHub or Twitter. Will assign the Admin role to users in the TwitterAdmins AppSetting, which is a way to assign admin rights to known Twitter users. Finally the retrieved Profile Url is added to the CustomUserAuth POCO Table and saved.
TechStacks tells ServiceStack to use its own CustomUserAuth table instead by registering a generic OrmLiteAuthRepository:
var authRepo = new OrmLiteAuthRepository<CustomUserAuth, UserAuthDetails>(dbFactory);
container.Register<IUserAuthRepository>(authRepo);
authRepo.InitSchema();
Where it will now Save User Information in the CustomUserAuth instead of the default UserAuth table.
I am developing an ASP.NET MVC 4 application. userMenus is a static variable that is loaded every time a user logs in.
public class MenuCL
{
public static List<UserMenu> userMenus = new List<UserMenu>(); // the static variable
}
public class UserMenu
{
public decimal MenuID { get; set; }
public string MenuName { get; set; }
public string Controller { get; set; }
public bool Permission { get; set; }
}
I use that static variable to check whether or not the logged in user has permission to a menu/controller in a custom authorize filter.
It works fine when a single user is logged in, but when two or more users are logged-in, it's all messed up, I mean the error page("you don't have access to this page") is displayed to a user that has permission to the menu/controller.
Only now I realized it's the static variable that is causing all the trouble, after I read this :
The static variables will be shared between requests. Moreover they will be initialized when application starts, so if the AppDomain, thus application gets restarted, their values will be reinitialized.
So I need a replacement for this static variable. Anyone has any suggestion?
You can still use a static field which is a property that provides access to a session variable.
public static List<UserMenu> UserMenus
{
set
{
Session["UserMenus"] = value;
}
get
{
return Session["UserMenus"] == null ? new List<UserMenu>() : (List<UserMenu>) Session["UserMenus"];
}
}
In order to get this working on a web farm which uses a session state server (or sql server), you need to put [Serializable] attribute on top of UserMenu.
I don't think, this way you need to modify your code very much.
My question is, why do you want to use static variable? Do you want to share the values across the application? In this case you can better use session.
Updated
Assume lst as a non static List of UserMenu. Then you can use the following method to store it in session and get it bak whenever you want.
To store
Session["usemenulist"] = lst;
To get it back
try
{
lst = (List<UserMenu>)Session["usemenulist"];
}
catch
{
}
Note
If you are getting the values from the database lo load it to the List for the first time, then you can query database to get it from the database whenever you want, instead of storing it in the session. (This is another option apart from Session, you may try this way also if you want.)
When my user in the students Role login to the system, he can select various classes that he's enrolled. I already have a filter that'll redirect him to the select class page so he must select a class to access the system, and change it anytime he wants and the whole system's context will change.
As for now, i'm storing IdClass in the session variable, using the code below, and the system uses it to filter all the related queries and functions, like showing all the lessons from the current class. My question is: is this a good practice? Is this right or is there any better and efficient way? I'm trying to follow patterns.
[Serializable]
public sealed class Session
{
private const string SESSION_FOO = "STUDYPLATFORM_GUID";
private Session()
{
this.IdClass= 0; // Construct it to 0 so it evaluate as there's no Class selected.
}
/* This is the session's public IdClass that
i can get and set throughout the application. */
public int IdClass { get; set; }
public static Session Current
{
get
{
if (HttpContext.Current.Session[SESSION_FOO] == null)
{
HttpContext.Current.Session[SESSION_FOO] = new Session();
}
return HttpContext.Current.Session[SESSION_FOO] as Session;
}
}
}
I have create a permission object that stores userId, Groups user is in and user´s permissions. This is a public class
I also need to have a static object that stores a list of those permissions objects that if a administration changes anything in the permissions all changes apply immediately for every logged user
I have a couple of questions:
Should I create this static object when the first user logs in or there is a mechanism a should use to create that list before the first user log-in (For instance when we start our app on IIS)?
Would it be easy to remove the item list for a specific user when it log-out?
This is a system requirement that permissions settings take effect as soon as the administrator make changes.
Edit 1:
public class permissionTemp
{
public static Guid userGuid { get; set; }
public static string[] grupos { get; set; }
public static string[] permissoes { get; set; }
}
public static class security
{
public List<permissionTemp> userPermissionSet { get; set; }
}
Think about a singleton, so you do not worry about creation time:
Singleton:
public class Permission
{
private Permission()
{ }
private static Permission _instance = null;
public static Permission Instance
{
get
{
if(_instance == null)
{
_instance = new Permission();
}
return _instance
}
}
Now you can have access to the same instance with
Permission.Instance
The object is created at the first access. So in the private constructor you can add your code to read the permissions fom database.
You can use the Application_Start method in the global.asax to run some code when the website starts for the first time. This will run before the first request is processed.
You can use the Session_End method in the global.asax to remove the item from the list. Also you can do it at the same time where you execute FormsAuthentication.SignOut (if you use Forms Authentication).
Note: I would use some locking mechanism to prevent multiple simultaneous access to the list. An alternative place to store the list would be in the WebCache. This is used by all users, so if it is updated by person x, next read from person y will be the updated version.
First of all i recommend to avoid creating static object for storing such sensetive information and also if any user has closed browser without clicking "Log out" then object will not be removed for that particular User.
Still if you need to do this to meet your requirement you can create it in that object in Applciation Start Event on Global.asax file when application start first time.