MediaWiki - Setting a default group for new users - mediawiki

I'm aware of this question but the given (accepted) answer tries to resolve a particular problem so I don't consider it a duplicate of my question.
I have defined a group and I want that all new users as default are on this group. Later on, I can delete a user from this group if needed by the Special:Userrights page.
How to achieve this?

Normally, you would use the built in user group if you wanted to change permissions for new users, and have a other user groups for when you need to change that (i.e. adding or removing rights). That would be much more straightforward, easier to maintain, and easier to understand for other administrators of your wiki.
If you really need to add every new user to another user group (you don't tell us why this would be necessary) you can use wgAutopromote, as pointed out in the comments:
$wgAutopromote = array(
'myUserGroup' => array( APCOND_EDITCOUNT, 0 )
);
Also, note that you can't directly remove a user from an autopromotion group through Special:UserRights.
Edit: As per the comments I understand that the unusual solution you are suggesting in your question is actually not needed. You should just create a user group for user that are not supposed to access the internal documents (e.g. $wgGroupPermissions['InternalDocs']['restrictedusers'] = false;), and then configure your extension to prevent them from accessing some documents. You might want to consider a better maintained extension for than the one you are using, btw, see: Editing permissions of MediaWiki default namespaces
Also, be adviced that MediaWiki is designed for openess, and any attempt to circumvent that, will have holes. See https://www.mediawiki.org/wiki/Security_issues_with_authorization_extensions

Related

Dynamically Customizable Group-Based Authorization in Rails

Currently, in my app there are just 3 roles visitors can have:
admin that has all privileges
user that can can perform several actions about him/her-self within the system
guest that can just watch and send bug reports
Everything is primitively implemented, as follows: in a DB each user has a field where his being admin (stands for 2 in the field) or user (1) is indicated, and in the application_controller.rb it is just checked if logged_in? && current_user.DB_FIELD == 2 (or > 0), and in the necessary controller there occurs a before_filter check, etc.
However, such a simple implementation worked great till recently when we decided to extend the functionality of the system, that is, partly, to allow admin to join users into groups, but there are some moments. For better understanging of what I am going to ask, let me describe the situation from the way I see it (maybe you can suggest something much better and logical):
I am an admin. I open /groups, and see a list of groups.
What is a group? A group, on the one hand, is a set of permissions, and on the other hand, is a combination of users that should have the same permissions within my app.
What is a permission? A permission is one action that each user of the group it assigned to can perform.
I want to unite new users in one group, but this group doesn't exist. So I click the button (which stands for /groups/new), and the Create Group window pops up. There, I have a textfield for a group name, a bulk of checkboxes, each stands for a permission, a field for adding users, and a Save button. I write the group name, check all the permissions I want to assign to this group, add users to this group (I am going to implement this through ajax search: starting typing a user's name, he/she appears, click Enter, and one user is added, then repeat these actions if needed - is it an OK approach?), and click Save.
Ok, I got a new group with several users. But stop, I realized I forgot to add one more person! I return to the Edit Group window (/groups/edit), and refill the misfilled fields. Click Save - and again some magic (I mean, update operations over the DB).
And so, what I have at the final stage? I can freely c/r/u/d the groups, managing users and permissions in them, and perform it in a very GUI-driven way (I mean, checkboxes, ajax search field, etc.)
For two weeks I have been googling/stackoverflowing/scrutinizing info about rails role- and group-based authorizations; have found a lot of solutions like cancan, easy_roles, troles, etc. gems, but cannot find in any of them how to implement a group-based approach, which is dynamic (or customizable? or dynamically customizable?). The only thing that really 100% suits my needs is a redmine permission and permission group approach, but it is overcomplicated due to its over9000-functionality, so I couldn't even fully understand how it is implemented, let alone implement it on my own.
And the questions are (assuming that the set of permissions is permanent so can be hardcoded, and the set of groups is absolutely free; also, if the user doesn't belong to any group he/she has default user permissions; moreover, permissions are not just for c/r/u/d operations, but also for the manually created methods):
What is the best way to implement the above mentioned system? Any existing yet not found by me gem or approach?
How to painlessly-for-scalability store the permissions and the permission groups? A bitmask, or separate permission, permission-to-group assignment, and group tables?
How to painlessly put users into groups? A group field in the user's DB row, or a separate user-to-group assignment table?
Preferably, that the permissions assigned to the group the user being added to, instantly, without any user relogins, apply to him.
Thank you in advanced!
Through several nights I finally came to a solution, which is, to my mind, rather easy yet powerful, but obviously not the best (but still an) implementation.
So, we have now +1 tables, which is of groups, where the columns are id, name, and permission. The last column is a usual integer which represents all the permissions in a decimal number.
The permissions are "aliased" in the controller: e.g. 1 stands for can_manage_smth, 2 stands for can_view_smth, etc.
The permission choice panel is in the /groups section, and is a simple set of checkboxes, applying an onchange action to each we ajaxly perform an OR operation with the permission stored in the table (e.g. we select 3 checkboxes standing for the values of 1, 8, and 16, then we get in our table 25, which is, in turn, a result of 1 | 8 | 16).
So answering my questions:
Not the best but still a solution.
It almost does not affect the scalability because adding a new permission (which is a very rare action) will just demand a new alias of the permission and its before_filter checkings in the beginning of the controller. And I used a bitmask but not as a binary but just a usual decimal value with which simple binary logic operands can play.
No separate user-to-group assignment tables, just a single group_id column in a user table (which already existed).
Hope everything implemented will work perfectly. If any issues occur, I will indicate here. Also, if any new implementation ideas come.
Anyway, thanks to everybody!

Shiro + Keys stored as strings = failure point or unnecessary concern?

Not getting into too many specifics, this is a high level question.
I've always gone by the idea that it is never a good idea to store primary keys in places that does not have a constraint. For example, storing a primary key in a EAV style architecture ("USER_ID",144). If that user is ever deleted it will not be reflected in the EAV mapping and cause issues farther down the road.
So I'm creating a new application using shiro as a security/permission framework and I need users to be able to edit themselves but not other users, I also need other users to be able to edit anyone. Simple enough:
user1 = "user:441:edit"
user2 = "user:edit"
In addition, I could have someone that can only edit a subset of users, something like this
user3 = "user:459:edit","user:460:edit","user:461:edit"
Or, someone that can edit users that are in a department but only that department
user4 = "department:5898:user:edit"
If someone from user3's list is deleted there's no way to update that user's permissions without magic (going through all the permissions and finding the ones to remove).
Now I don't plan to reset the keys but if it was to ever happen and I DON'T clean up the old keys I could have users suddenly being able to edit users who were recently created after reusing the old keys.
I could mitigate some of this by using a generated code ("user:ciS84nFSHK:edit") that is unique across ALL TABLES to manage deletion of permissions. However, adding in a few hundred million records makes me think this can grow unwieldy quickly.
Am I using Shiro improperly? Am I simply overfocused on keys getting mangled? Have you solved these issues? Any help would be appreciated.
After doing some more research with Shrio I've decided to go with pst's suggestion and simply reference the values via a GUID (maybe a 10-digit-alphanumeric instead to preserve space).
So user1 = "user:441:edit" would become "user:96aae854-fc40-42ff-b948-c8944c2fca92:edit" this will help negate the concern about storing keys as strings.

Database schema for ACL

I want to create a schema for a ACL; however, I'm torn between a couple of ways of implementing it.
I am pretty sure I don't want to deal with cascading permissions as that leads to a lot of confusion on the backend and for site administrators.
I think I can also live with users only being in one role at a time. A setup like this will allow roles and permissions to be added as needed as the site grows without affecting existing roles/rules.
At first I was going to normalize the data and have three tables to represent the relations.
ROLES { id, name }
RESOURCES { id, name }
PERMISSIONS { id, role_id, resource_id }
A query to figure out whether a user was allowed somewhere would look like this:
SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)
Then I realized that I will only have about 20 resources, each with up to 5 actions (create, update, view, etc..) and perhaps another 8 roles. This means that I can exercise blatant disregard for data normalization as I will never have more than a couple of hundred possible records.
So perhaps a schema like this would make more sense.
ROLES { id, name }
PERMISSIONS { id, role_id, resource_name }
which would allow me to lookup records in a single query
SELECT * FROM permissions WHERE role_id = ? AND permission = ? ($user_role_id, 'post.update')
So which of these is more correct? Are there other schema layouts for ACL?
In my experience, the real question mostly breaks down to whether or not any amount of user-specific access-restriction is going to occur.
Suppose, for instance, that you're designing the schema of a community and that you allow users to toggle the visibility of their profile.
One option is to stick to a public/private profile flag and stick to broad, pre-emptive permission checks: 'users.view' (views public users) vs, say, 'users.view_all' (views all users, for moderators).
Another involves more refined permissions, you might want them to be able to configure things so they can make themselves (a) viewable by all, (b) viewable by their hand-picked buddies, (c) kept private entirely, and perhaps (d) viewable by all except their hand-picked bozos. In this case you need to store owner/access-related data for individual rows, and you'll need to heavily abstract some of these things in order to avoid materializing the transitive closure of a dense, oriented graph.
With either approach, I've found that added complexity in role editing/assignment is offset by the resulting ease/flexibility in assigning permissions to individual pieces of data, and that the following to worked best:
Users can have multiple roles
Roles and permissions merged in the same table with a flag to distinguish the two (useful when editing roles/perms)
Roles can assign other roles, and roles and perms can assign permissions (but permissions cannot assign roles), from within the same table.
The resulting oriented graph can then be pulled in two queries, built once and for all in a reasonable amount of time using whichever language you're using, and cached into Memcache or similar for subsequent use.
From there, pulling a user's permissions is a matter of checking which roles he has, and processing them using the permission graph to get the final permissions. Check permissions by verifying that a user has the specified role/permission or not. And then run your query/issue an error based on that permission check.
You can extend the check for individual nodes (i.e. check_perms($user, 'users.edit', $node) for "can edit this node" vs check_perms($user, 'users.edit') for "may edit a node") if you need to, and you'll have something very flexible/easy to use for end users.
As the opening example should illustrate, be wary of steering too much towards row-level permissions. The performance bottleneck is less in checking an individual node's permissions than it is in pulling a list of valid nodes (i.e. only those that the user can view or edit). I'd advise against anything beyond flags and user_id fields within the rows themselves if you're not (very) well versed in query optimization.
This means that I can exercise blatant
disregard for data normalization as I
will never have more than a couple
hundred possible records.
The number of rows you expect isn't a criterion for choosing which normal form to aim for.
Normalization is concerned with data integrity. It generally increases data integrity by reducing redundancy.
The real question to ask isn't "How many rows will I have?", but "How important is it for the database to always give me the right answers?" For a database that will be used to implement an ACL, I'd say "Pretty danged important."
If anything, a low number of rows suggests you don't need to be concerned with performance, so 5NF should be an easy choice to make. You'll want to hit 5NF before you add any id numbers.
A query to figure out if a user was
allowed somewhere would look like
this:
SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions
WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)
That you wrote that as two queries instead of using an inner join suggests that you might be in over your head. (That's an observation, not a criticism.)
SELECT p.*
FROM permissions p
INNER JOIN resources r ON (r.id = p.resource_id AND
r.name = ?)
You can use a SET to assign the roles.
CREATE TABLE permission (
id integer primary key autoincrement
,name varchar
,perm SET('create', 'edit', 'delete', 'view')
,resource_id integer );

MySql : how can I implement a sort of "read/not read yet" topics?

I'm about to implement a list of topic/argument in my forum, and I'd like to insert a sort of flag like "read/not read yet" for each message, regard each user in my website.
I think at somethings like this : a table watched_topics with id(INT), user(VARCHAR) and topic_id(INT). When a user watch the page, I'll insert (if the data doesn't exist) these information.
When another user will insert a new message in a topic, I'll delete from the table watched_topics all line with that topic_id.
That could provide a trouble : Think about to 9000 topics and 9000 users that have watched all topics : the table will be so big (9000x9000=81000000).
So, I think is not the best strategy to implement this kind of stuff! Any suggestion would be appreciated :)
Cheers
May I suggest a different approach?
Make use of web browser history mechanism.
Every topic can get a new, unique URL every time a new message is added there. It could include the number of messages, last modified time or a combination of both.
If the user did see the topic, he must have visited it, so a properly set up CSS can help identifying the read ones. You can even use some client-side scripts to modify the behaviour of the page based on that.
Another way to do that would be to keep the watched topics table the way you want to do it, but also store last visit time in user's profile and show all topics as read that haven't changed since that time.
However it's pretty safe to assume that all users reading all topics is very unlikely.
Your suggestion sounds good. I would make user-field also a foreign key - it gives you a bit more flexibility.
Are you sure all 9000 topics are read by all 9000 users? I mean is this reality? Like you said, topic-entries are deleted when new message is added. And when that happens, another 9000 entries are deleted :)
I would index the table and go with your suggestion (with user_id change). If the table size gets in your way, you can always change the implementation later. Most likely it will never be the issue anyway.
For the deletion: you could save what the latest msg-ID was the user saw. This way you do not have to perform a lot of delete actions every time a msg is posted in a much-viewed topic.

MySQL - Saving and loading

I'm currently working on a game, and just a while ago i started getting start on loading and saving.
I've been thinking, but i really can't decide, since I'm not sure which would be more efficient.
My first option:
When a user registers, only the one record is inserted (into 'characters' table). When the user tries to login, and after he/she has done so successfully, the server will try loading all information from the user (which is separate across multiple tables, and combines via mysql 'LEFT JOIN'), it'll run though all the information it has and apply them to the entity instance, if it runs into a NULL (which means the information isn't in the database yet) it'll automatically use a default value.
At saving, it'll insert or update, so that any defaults that have been generated at loading will be saved now.
My second option:
Simply insert all the required rows at registration (rows are inserted when from website when the registration is finished).
Downsides to first option: useless checks if the user has logged in once already, since all the tables will be generated after first login.
Upsides to first option: if any records from tables are deleted, it would insert default data instead of kicking player off saying it's character information is damaged/lost.
Downsides to second option: it could waste a bit of memory, since all tables are inserted at registration, and there could be spamming bots, and people who don't even manage to get online.
Upsides to first option: We don't have to check for anything in the server.
I also noted that the first option may screw up any search systems (via admincp, if we try looking a specific users).
I would go with the second option, add default rows to your user account, and flag the main user table as incomplete. This will maintain data integrity across your database, whereas every user record is complete in it's entirety. If you need to remove the record, you can simply add a cascading delete script to clean house.
Also, I wouldn't develop your data schema based off of malacious bots creating accounts. If you are concerned about the integrity of your user accounts, add some sort of data validation into your solution or an automated clean-house script to clear out incomplete accounts once the meet a certain criteria, i.e. the date created meeting a certain threshold.
You mention that there's multiple tables of data for each user, with some that can have a default value if none exist in the table. I'm guessing this is set up something like a main "characters" table, with username, password, and email, and a separate table for something like "favorite shortcuts around the site", and if they haven't specified personal preferences, it defaults to a basic list of "profile, games list, games by category" etc.
Then the question becomes when registering, should an explicit copy of the favorite shortcuts default be added for that user, or have the null value default to a default list?
I'd suggest that it depends on the nature of the auxiliary data tables; specifically the default value for those tables. How often would the defaults change? If the default changes often, a setup like your first option would result in users with only a 'basic' entry would frequently get new auxiliary data, while those that did specify their own entries would keep their preferences. Using your second option, if the default changed, in order to keep users updated, a search/replace would have to be done to change entries that were the old default to the new default.
The other suggestion is to take another look at your database structure. You don't mention that your current table layout is set in stone; is there a way to not have all the LEFT JOIN tables, and have just one 'characters' table?