Moodle, how to update $USER content after updating user role through data base - moodle

A user logs in to the server, current role of the user in a course context is editing teacher. I want to change the role to student, and test if attempt capability exists or not for the user in course context?
To change the role of user to student for a given context, I manually update the role_assignment table's role column from editing teacher role to student. Changes done directly show no effect unless the user logs out and logs in again.
To test the capability of the user I use $USER global variable.
Even after manually changing the role of the user to student $USER is not updated: $USER shows the changes only after user logs out and logs in again.
I think issue is related to browser cache, session.
How can I update $USER directly as soon as I change role?
Code used to change role of user:
$index = $context->depth-1;
$array = explode("/",$context->path);
$context_id = $array[$index];
// get logged in user id
$userId = $USER->id;
$user = $DB->get_record('role_assignments', array('contextid'=>$array[$index], 'userid'=> $userId));
$user->roleid = 5;
$roles = get_user_roles($context, $userId);
$user->timemodified = time();
$DB->update_record('role_assignments', $user, $bulk=false);

I think the code you want looks something a bit more like this:
$coursecontext = $context->get_course_context();
$teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher']);
$studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']);
role_assign($studentroleid, $USER->id, $coursecontext->id);
role_unassign($teacherroleid, $USER->id, $coursecontext->id);
I wasn't quite sure what you were trying to do by exploding the context path, so I assumed you were trying to get the course context (which I've done above). If you just wanted the id of the current context, then $context->id would have done.
It's not a good idea to hard code roleids, 5 is the student role in most clean installs, but looking it up is more reliable.
Using the proper Moodle functions to assign/unassign roles is much more reliable than manually messing around with database entries (if you want, you can take a look in lib/accesslib.php and see exactly what those functions are doing internally).

Related

Azure AD Graph API ApproleAssignment does not allow zero GUID

I am getting an error when trying to add AppRoleAssignment for a user:
{"odata.error":{"code":"Request_BadRequest","message":{"lang":"en","value":"One or more properties are invalid."},"date":"2017-10-21T14:49:42","requestId":"3aacf13e-5620-40da-8fd0-fb2d4130f171","values":null}}
When i use an actual ApproleId, everything works fine. However, when i set
AppRoleAssignment.Id = new Guid(); i get the above error;
This does not make sense, because the documentation says that this is allowed
by setting zero GUID and the same has been stressed in other posts on SO.
What am i missing here?
Full code:
AppRoleAssignment appRoleAssignment = new AppRoleAssignment()
{
Id = new Guid(),
ResourceId = Guid.Parse(servicePrincipal.ObjectId),
PrincipalId = Guid.Parse(user.ObjectId),
PrincipalType = "User"
};
user.AppRoleAssignments.Add(appRoleAssignment);
await user.UpdateAsync();
Based on my experience, there are two scenarios we may get this issue.
First, if there is customize roles in the service principal you want to assign the default role.
Second, if we have already assigned the default role to that person before.
Please check whether you are in one of these two scenarios and let me know if you still have the problem about this issue.

How to handle Zend Framework End User INI/Config settings

I have searched and searched for this but I think my terminology isn't correct as it keeps giving me the application settings for the zend site rather than an application settings for the End User.
I'd like to have a config.ini type file that the end user can edit values in. I'd like it to be ONLY the settings I wish them to see and to be able to create the value names as I think would make sense to them. So it would be something like
[General]
SiteName=MySite
ShowResources=TRUE
[Database]
Server=myServer
databasepath=mydbpath
...
So my two questions.
1. What is this type of file called because when I search application settinsg, I get the ZF application settings not one for an end user (presumably)
What is the best way to handle this type of file?
Thanks
In your bootstrap add:
protected function _initConfig()
{
$config = new Zend_Config_Ini(APPLICATION_PATH.'/configs/config.ini');
Zend_Registry::set('config', $config);
return $config;
}
replace config.ini with whatever you want the filename to be.
You can then access this config object anywhere in your application either as an application resource or through the registry (Zend_Registry::get('config')). So to get the SiteName from your example:
$config = Zend_Registry::get('config');
echo $config->General->SiteName;
For things like database settings, you'll want to access these in the bootstrap so you can use them to setup other resources. I would recommend you don't try and include database settings in your application.ini as well, instead manually setup the DB resource by adding another bootstrap method:
protected function _initDb()
{
$this->bootstrap('config');
$config = $this->getResource('config');
$db = Zend_Db::factory('Pdo_Mysql', array(
'host' => $config->Database->Server,
'username' => $config->Database->Username,
'password' => $config->Database->Password,
'dbname' => $config->Database->Dbname
));
return $db;
}
To explain this some more, $this->bootstrap('config'); ensures the config resource is loaded first. $this->getResource('config'); returns the config resource (the one created by the _initConfig() method). It then uses the data from this object to create the DB connection.
It's an INI file, which you can read and write via Zend_Config.
ZF has no concept of "user settings" -- users are defined by you, not by the framework.
Apps usually store user configs in a database, but that's totally up to you. You could store a directory of INI files instead. Either way, you have to do the implementation yourself.
Edit: Given that you have a ZF app that you're distributing to the customer, and they're only ever going to connect to one database with it, that changes things significantly. (I thought you originally meant that you'd have one instance of the app simultaneously connecting to multiple databases.)
In your case, I would use the standard ZF application/configs/application.ini file for your application's "internal" settings. Then, I'd have a separate local.ini (or whatever) in that same application/configs directory, which contains only those settings that you want the customer editing. Distribute a skeleton local.ini file with the app, that has instructions right in it, something like this:
; Remove the comment from this line.
;configured = 1
; You need to put your database credentials in here.
db_host = "PUT YOUR DATABASE SERVER NAME HERE"
db_user = "PUT YOUR DATABASE USERNAME HERE"
db_pass = "PUT YOUR DATABASE PASSWORD HERE"
Then just load the local.ini file via Zend_Config. I'd also add a check to your index controller's init method that checks to see if you're properly configured:
$localConfig = Zend_Registry::get('local_config'); // or wherever you put it
if (!$localConfig->configured) {
$this->_helper->redirector('config', 'error');
}
And then make a error/config view that says:
You didn't read the instructions. Go do that now.
Note there's nothing stopping the customer from editing anything they want, but this makes a logical separation and makes it harder to accidentally screw something up.

How can I create a user account in my User table using Entity Framework Migrations?

Previously I used a database initializer to seed or pre-populate my tables when creating them using Code First. This all worked great but I wanted to move on to using the EF 4.3 migrations.
I updated to EF 4.3 in my project and removed the database initializer code and put it within the Seed method on the migrations Configuration.cs file. This is all working ok for me for prepopulating my tables except for when I want to seed a default user into my user table.
I use the following code within my Seed method: (currently this doesn't use the AddOrUpdate code as I'm not sure how to use that for this situation - hence the username check)
// create default User
MembershipCreateStatus status = new MembershipCreateStatus();
User admin = context.Users.Find("TestGuy");
if (admin == null)
{
Membership.CreateUser("TestGuy", "TestGuy123", "testguy#test.com");
if (status == MembershipCreateStatus.Success)
{
admin.Firstname = "Test";
admin.Surname = "Guy";
admin.DateLastActivity = DateTime.Now;
admin.LastActivityRoute = "/Home/Index";
Role adminRole = context.Roles.Find("Administrator");
admin.Roles = new List<Role>();
admin.Roles.Add(adminRole);
}
}
When I try and run Update-Database I get this error:
The password-answer supplied is invalid.
I think this is just down to how I call the CreateUser but I can't seem to work out how to get around this bug. I've tried putting in just nulls for the security question and answer but that doesn't seem to work.
Has anyone an example of seeding a user using the 'AddOrUpdate' option?
EDIT 1:
As per one of the comments below, I am using Altairis Security Toolkit to manage my membership. My web.config is setup as follows:
<membership defaultProvider="TableMembershipProvider">
<providers>
<clear />
<add name="TableMembershipProvider" type="Altairis.Web.Security.TableMembershipProvider, Altairis.Web.Security" connectionStringName="DataContext" minRequiredPasswordLength="8" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" minRequiredNonAlphanumericCharacters="0" passwordStrengthRegularExpression="" />
</providers>
</membership>
EDIT 2:
I have also put this as an open ticket on the Altairis forum.
http://altairiswebsecurity.codeplex.com/workitem/32273
EDIT 3:
To get this working without any error I had to setup my code as follows:
// create default User
MembershipCreateStatus status = new MembershipCreateStatus();
User admin = context.Users.Find("TestGuy");
if (admin == null)
{
Membership.CreateUser("TestGuy", "TestGuy123", "testguy#test.com", null, null, true, out status);
if (status == MembershipCreateStatus.Success)
{
admin = context.Users.Find("TestGuy");
admin.Firstname = "Test";
admin.Surname = "Guy";
admin.DateLastActivity = DateTime.Now;
admin.LastActivityRoute = "/Home/Index";
Role adminRole = context.Roles.Find("Administrator");
admin.Roles = new List<Role>();
admin.Roles.Add(adminRole);
}
}
The above will compile now when running Update-Database but no new user is generated. If anyone has suggestions to try that would be great!
Thanks,
Rich
I think your Membership is set to require question and answer. Have you tried putting a check in using "if (Membership.RequiresQuestionAndAnswer)" and see which way it branches.
To Admin: By the way I wanted to post this first as a comment as I was flagged last time when using an answer, but I don't see any option here for adding comments, only answers.
From doing some research on this issue it's become apparent that the user seeding should be created outside the migrations seed logic - at least for now until they make it easier to do!
As a work around I followed the example from this Stackoverflow question (Best way to incrementally seed data in Entity Framework 4.3) to manually make my new user on start up outside of the migrations configuration file.
It's not ideal, at least to me, as all 'startup' data is not in one area but it gets the job done and isn't that difficult to follow.

Using "seen" on a trail in KRL

I want to have a trail that helps keep track of values I want to persist for users. If a user has not entered their name, I want to display a form for them to enter their name to use for lookups.
I want to be able to check if the name is on the trail. If the name is on the trail then display the data for that user. If the name is not on the trail then I want to display a form for them to enter their name.
I am looking for some help on how I would accomplish this. It was suggested to encode a struct as json and pushing that on to a trail and then search for it. Some direction on how this would be done would be helpful. Would I use the following?
if seen ent:user_data with <regexp> then {
<get and show data>
} else {
<show form to user>
}
If you just want to save a simple string for later then you can do something like the following using an entity variable
in the pre block retrieve saved name from entity variable:
savedName = ent:userName || "";
in the postlude save or clear the entity variable:
set ent:userName userName;
clear ent:userName;
Example app => https://gist.github.com/722849
Example bookmarklet => http://mikegrace.s3.amazonaws.com/forums/stack-overflow/example-persistant-trail-bookmarklet.html
Example run on http://example.com results
first run on example.com
after clicking submit
reloading the page and running the app again
clearing trail by running on yahoo.com
running app on yahoo.com before saving name or after clearing
Note: When you want to save something else like an age, you can just use a different entity variable like
ent:userAge
The sky is the limit. ; )

Zend_Auth fails to write to storage

I have yet another weird annoying thing going on with Zend.
Basically, I have the following code after creating a user domain:
$this->auth = Zend_Auth::getInstance();
$this->view->user = $this->user = $this->auth->getIdentity();
$this->user->idSite = $idSite;
$this->user->urlSite = $urlSite;
$this->auth->getStorage()->write($this->user);
What FURIOUSLY annoys me is that the auth->getIdentity() just moments after that:
[idSite] => 0
[urlSite] =>
So from here it gets worse: If I REFRESH or if any of the other parameters of the form fail and send me to the SAME FORM, but WITHOUT TOUCHING THE ABOVE SCRIPT, the auth-getIdentity() correctly returns:
[idSite] => 2431
[urlSite] => exampledomain
Which means that the code is correct and working, BUT if the form is filled out correctly and everything adds up nicely, I redirect to the next step: $this->_redirect('nextstep'), and neither idSite or urlSite remain empty forever.
Why is this? Why?
I've had the same issue and I think it is better to go via the route of using the session namespace functionality:
$oSession = new Zend_Session_Namespace('myStorage');
$oSession->foo = "bar";
$oSession->baz = 123;
And you can recover the data by:
$oSession = new Zend_Session_Namespace('myStorage');
$this->view->foo = $oSession->foo;
There are some more clues here: Zend_Auth the main message of which is that the storage of Zend_Auth data is actually just a namespace.
The default access to this would be similar to :
$oSession = new Zend_Session_Namespace('Zend_Auth');
I also had trouble with Zend_Auth not writing to storage. However, after seeing the answer by Ian Lewis and your response I realised it was probably writing ok, but not reading. I had previously changed the 'name' setting in my session to my own namespace. Once I removed this and started using the default again my Zend_Auth worked fine.