Yii RBAC make Users update profile by himself - mongodb

I'm trying to do this with mongodbauthmanager. I'm follow step by step in Usage section but finally i'm getting PHP warning: Illegal offset type. I had posted this question at Yii Extension before clone to SO:
Please tell me what is wrong?
1// Config
'authManager'=>array(
'class' =>'CMongoDbAuthManager',
'showErrors' => true,
),
2// Create auth items in db
$auth = new CMongoDbAuthManager();
$bizRule = 'return Yii::app()->user->id==$params["User"]->_id;';
$auth->createTask('updateSelf', 'update own information', $bizRule);
//I had tried with $auth->createOperation() but they has the same error
$role = $auth->createRole('user');
$role->addChild('updateSelf');
$auth->save();
and here is result in db
result in db http://i.minus.com/iIpXoBlDxaEfo.png
**3// Checking access in controller ** - UPDATE CODE AND ERROR
public function actionUpdate($id)
{
$model=$this->loadModel($id);
$params = array('User'=>$model);
if (!Yii::app()->user->checkAccess('updateSelf', Yii::app()->user->id,$params) )
{
throw new CHttpException(403, 'You are not authorized to perform this action');
}
//another statement ...
}
4// Getting error:
Fatal error : Cannot use object of type MongoId as array in F:\Data\03. Lab\www\yii\framework\web\auth\CAuthManager.php(150) : eval()'d code on line 1
RESOLVED PROBLEM
Base-on the answer of #Willem Renzema, I resolve my problem. Now, I update here and hope it useful for someone have this error.
0// First, config authManager with defaultRoles
'authManager'=>array(
'class'=>'CMongoDbAuthManager',
'showErrors' => true,
'defaultRoles'=> array('user'),//important, this line help we don't need assign role for every user manually
),
1// Fix save id in UserIdentity class
class UserIdentity extends CUserIdentity
{
private $_id;
//...
public function authenticate()
{
//...
$this->_id = (string)$user->_id;//force $this save _id by string, not MongoId object
//...
}
//...
}
2// Fix $bizrule in authe items
($bizrule will run by eval() in checkAccess)
//use _id as string, not MongoId object
$bizRule = 'return Yii::app()->user->id==(string)$params["User"]->_id;';
3// And user checkAccess to authorization
public function actionUpdate($id){
/**
* #var User $model
*/
$model=$this->loadModel($id);
$params = array('User'=>$model);
if (!Yii::app()->user->checkAccess('updateSelf', $params) )
{
throw new CHttpException(403, 'You are not authorized to perform this action');
}
//...
}
4// Done, now we can use checkAccess :D

First off, your original use of checkAccess was correct. Using Yii::app()->user->checkAccess() you are using the following definition:
http://www.yiiframework.com/doc/api/1.1/CWebUser#checkAccess-detail
Now, CWebUser's implementation of checkAccess calls CPHPAuthManager's implementation, which is where you encountered your problem with an illegal offset type.
http://www.yiiframework.com/doc/api/1.1/CPhpAuthManager#checkAccess-detail
An Illegal offset type means you are attempting to access an array element by specifying its key (also known as: offset) with a value that doesn't work as a key. This could be another array, an object, null, or possibly something else.
Your stack trace posted on the extensions page reveals that the following line gives the problem:
if(isset($this->_assignments[$userId][$itemName]))
So we have two possibilities for the illegal offset: $userId and $itemName.
Since $itemName is clearly a string, the problem must be with $userId.
(As a side note, the fact that your stack trace revealed surrounding code of this error also revealed that, at least for CPHPAuthManager, you are using a version of Yii that is prior to 1.1.11. Observe that lines 73 and 74 of https://github.com/yiisoft/yii/blob/1.1.11/framework/web/auth/CPhpAuthManager.php do not exist in your file's code.)
At this point I would have guessed that the problem is that the specified user is not logged in, and so Yii::app()->user->id is returning null. However, the new error you encountered when placing Yii::app()->user->id as the 2nd parameter of checkAccess reveals something else.
Since the 2nd parameter is in fact what should be the $params array that appears in your bizRule. Based on the error message, this means that Yii::app()->user->id is returning a mondoId type object.
I was unfamiliar with this type of object, so looked it up:
http://php.net/manual/en/class.mongoid.php
Long story short, you need to force Yii::app()->user->id to return the string value equivalent of this mondoId object. This likely set in your UserIdentity class in the components folder. To force it to be a string, simply place (string) to force a type conversion.
Example:
$this->_id = (string)$User->_id;
Your exact code will vary, based on what is in your UserIdentity class.
Then, restore your checkAccess to the signature you had before, and it should eliminate the Illegal offset error you encountered originally.
Note however that I have not used this extension, and while performing the following actions should fix this issue, it may cause new issues if the extension relies on the fact that Yii::app()->user->id is a mondoId object, and not a string.

Related

How to debug in TYPO3 if <f:debug> returns strings instead of object?

In a custom TYPO3 8.7.12 extbase extension I am unable to f:debug items in templates.
We are in the listAction controller and simply do:
$institutions = $this->institutionRepository->findAll();
$this->view->assignMultiple([
'institutions' => $institutions,
// ... pagination limit ...
]
);
And in the template:
<f:debug>
{institutions}
</f:debug>
This returns
sometimes the string 'Array' in the fluid debugger (can't reproduce now)
When the code is on 3 lines: #1273753083: Cannot cast object of type "TYPO3\CMS\Extbase\Persistence\Generic\QueryResult" to string.
Or also #1273753083: Cannot cast object of type "TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage" to string.
When the code is on 1 line: #1234386924: Cannot create empty instance of the class "TYPO3\CMS\Extbase\Persistence\ObjectStorage" because it does not implement the TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface.
If I loop through {institutions} with f:for and then f:debug:
<f:for each="{institutions}" as="institution" iteration="i">
<f:debug>
{institution}
</f:debug>
</f:for>
I get the first property of the object, e.g. the name.
EDIT: this is due to a __toString() magic method in the model. If I remove it, instead I get the namespace and uid STUBR\Extension\Domain\Model\Institution:55 – this looks again as if the object isn't rendered.
Wait... php.net says The __toString() method allows a class to decide how it will react when it is treated like a string. So could something be treating (typecasting?) the object as a string?
Working with the properties is normal, the issue just occurs when trying to print the whole object.
Where should I look? Lazy loading? There are some lazy loading properties, but not that many. Or maybe something is missing from the class? Or is there a workaround.
PS:
Unable to print_r or var_dump the query result, I get a memory limit error.
I saw https://wiki.typo3.org/Exception/CMS/1234386924 but initStorageObjects() is already called in the constructor
To answer the question;
<f:debug>{institutions}</f:debug>
will be parsed as an object, but any whitespace inside will make it parse as a string so.
The following methods do the same job as <f:debug> and work similarly in my case:
\TYPO3\CMS\Core\Utility\DebugUtility::debug(
$var = $variable,
$header = 'Institutions',
$group = ''
);
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump(
$variable,
$title = 'Institutions',
$maxDepth = 8,
$plainText = FALSE,
$ansiColors = TRUE,
$return = FALSE,
$blacklistedClassNames = NULL,
$blacklistedPropertyNames = NULL
);
execute in list or show action in controller.
It's less convenient than with f:debug (because you have to do the work in two different places, e.g. when you're in a loop in the template, you have to go to the controller and build that loop again), but it's a helpful workaround.
EDIT: I found it's sufficient to do
<f:debug>{var}</f:debug>
on one line

FilterOperator bug in using quotes, same code different behavior across systems/time

this.getView().getModel().read("/QualificationProficiencySet", {
filters: [new sap.ui.model.Filter({
path: "Qobjid",
operator: sap.ui.model.FilterOperator.EQ,
value1: nQObjid
})],
success: function(data) {
that._profData = data.results;
that._oQuickView.setModel(new sap.ui.model.json.JSONModel(that._profData), "proficiencyModel");
// delay because addDependent will do a async rerendering and the actionSheet will immediately close without it.
jQuery.sap.delayedCall(200, that, function() {
that._oQuickView.openBy(oLink);
});
},
error: function(evt) {}
});
nQObjidis of type string - always.
Yesterday on our development system I saw the error
"Invalid parametertype used at function 'eq' (Position: 8)"
I noticed that the filter was appended in the URL without single quotes around the value of nQObjid. Strange because at the moment it's added as the value of the filter operator it's clearly a string. I couldn't find any related issues, but I put a (dirty) workaround in place by doing value1: "'"+nQObjid+"'".
This worked, until today, same system, didn't change the code, but suddenly the quotes are part of the value inside the gateway. So I remove the "'"again and tested, works. Then I transport the solution to production to find out that I now have the same problem on production with "Invalid parametertype used at function 'eq'.. Another user on production does not have this issue, so I'm a bit lost.
Similar issue: new SAPUI5 updat to 1.42 has odata bug "Invalid Parameters...
This may not solve your problem but it's too long for a comment, that's why I am posting it here:
When doing a read request, the framework is making a call to a helper class: V2 ODataModel.js Line #4231
aUrlParams = ODataUtils._createUrlParamsArray(mUrlParams);
The helper class then calls a private method: ODataUtils.js Line #72
return "$filter=" + this._createFilterParams(aFilters, oMetadata, oEntityType);
This private method is doing a bunch of stuff, most importantly calling another private method which is actually building the strings ODataUtils.js Line #128
sFilterParam = that._createFilterSegment(oFilter.sPath, oMetadata, oEntityType, oFilterSegment.operator, oFilterSegment.value1, oFilterSegment.value2, sFilterParam);
One of the first thing this method does is formatting your value, and I guess here is where your problem occurs: ODataUtils.js Line #393
oValue1 = this.formatValue(oValue1, sType);
The formatValue function takes your value and its Edm.Type and depending on that type does different stuff. If your objectId is a string, then it should put single quotes at the beginning and the end: ODataUtils.js Line #468
sValue = "'" + String(vValue).replace(/'/g, "''") + "'";
If the type is undefined or some weird value that UI5 doesn't know, then your value is simply cast to a String (which is probably what happens in your case).
Why is the type undefined or weird? That's where you come in... you have to do a little debugging to find out what the actual values are. If the UI5 code is unreadable you can put sap-ui-debug=true as an URL parameter:
my.sap.system.com:8000/sap/bc/ui5_ui5/sap/ztest/index.html?sap-ui-debug=true
If it's a timing issue (metadata has not been loaded for whatever reasons) then wrapping your code in a Promise might help:
var oModel = this.getView().getModel();
oModel.metadataLoaded().then(function() {
oModel.read("/QualificationProficiencySet", {
// ...
});
}

Dynamics CRM - Unit Testing a plugin with RhinoMocks give weird result

I am writing a unit test for a plugin using Dynamics CRM with RhinoMocks.
After stubbing out the OrganizationService.Retrieve() method, when I invoke the stubbed out method, I am getting null back.
From what I can see (correct me if I'm wrong), is that the stubbed out method signature must the same as the invocation signature.
Here is my code:
TestSetup
var someGuid = Guid.Empty;
var organisationServiceMock = MockRepository.GenerateMock<IOrganizationService>();
organisationServiceMock.Expect(x => x.Retrieve("someCrmEntity", someGuid, SomeCrmEntityColumnSetQuery.ColumnSet))
.Return(new Entity
{
LogicalName = "someCrmEntity",
Id = Guid.NewGuid(),
});
SomeCrmEntityColumnSetQuery Code
public static class SomeCrmEntityColumnSetQuery
{
public static ColumnSet ColumnSet => new ColumnSet("column1", "column2");
}
Invocation Code
var someEntity = organisationServiceMock.Retrieve("someCrmEntity", someGuid, SomeCrmEntityColumnSetQuery.ColumnSet);
//someEntity is null
Things I have tried
Removed the ColumnSet and replaced it with null - this works
Replaced the static class SomeCrmEntityColumnSetQuery with a default instance (new ColumnSet())
I have set the someGuid to Guid.Empty thinking that it was not "joining" on the correct Guid hence the null return value.
I have tried to replace .Expect() with .Stub() - no joy
Edit
In the expectation, I have tried the .WhenCalled(...) and that is how I found out that if I replace the columnSet argument with a null in the expectation and the invocation, it works. So it's go to do with something in my static class that represents a ColumnSet. The code works as I have it running in my DEV environment.
If anyone can share some light on this, that would be magic!
Charles
So I found the answer after watching a PluralSight video on RhinoMocks.
My problem was that when setting up the stub, the stub does not take values but rather the signature of the method that you are stubbing out. For e.g:
var organisationServiceMock = MockRepository.GenerateMock();
//Wrong
organisationServiceMock.Expect(x => x.Retrieve("someCrmEntity", someGuid, SomeCrmEntityColumnSetQuery.ColumnSet)).Return(new Entity());
//The stub does not care about what values are being sent into the method when invoked but rather if the method signature types match.
//Correct
organisationServiceMock.Expect(x => x.Retrieve(Arg.Is.Anything, Arg.Is.Anything, Arg.Is.Anything)).Return(new Entity());
//During the invocation, stubbed method now expects the first argument to be a string, then 2nd to be a Guid, 3rd to be a ColumnSet.
I hope this helps anyone who has also been struggling with this. :)

Supporting "recursive objects" in lua

I'm fairly new to lua and have the following problem with an assignment from a class:
We currently extend lua to support objects and inheritance. The Syntax for that is
Class{'MyClass',
attribute1 = String,
attribute2 = Number
}
Class{'MySubClass', MyClass,
attribute3 = Number
}
This works perfectly fine. The real problem lies within the next task: We should support "recursive types", that means a call like
Class{'MyClass', attribute = MyClass}
should result in an class with a field of the same type as the class. When this "class-constructor" is called the variable MyClass is nil, thats why the parameter table doesnt't have an entry attribute. How is it possible to access this attribute?
My first thought was using some kind of nil-table which gets returned every time the global __index is called with an unset key. This nil-table should behave like the normal nil, but can be checked for in the "class-constructor". The problem with this approach are comparisons like nil == unknown. This should return true, but as the __eq meta method of the nil-table is never called we cannot return true.
Is there another approach I'm currently just ignoring? Any hint is greatly appreciated.
Thanks in advance.
Edit:
Here the relevant part of the "testfile". The test by which the code is rated in class is another one and gets published later.
three = 3
print( three == 3 , "Should be true")
print( unknown == nil , "Should be true" )
Class{'AClass', name = String, ref = AClass}
function AClass:write()
print("AClass:write(), name of AClass:", self.name)
end
aclass = AClass:create("A. Class")
aclass:write()
Since MyClass is just a lookup in the global table (_G), you could mess with its metatable's __index to return a newly-defined MyClass object (which you would later need to fill with the details).
However, while feasible, such an implementation is
wildly unsafe, as you could end up with an undefined class (or worse, you may end up inadvertantly creating an infinite lookup loop. Trust me, I've been there)
very hard to debug, as every _G lookup for a non-existing variable will now return a newly created class object instead of nil (this problem could somewhat be reduced by requiring that class names start with an uppercase character)
If you go that route, be sure to also override __newindex.
How about providing the argument in string form?
Class{'MyClass', attribute = 'MyClass'}
Detect strings inside the implementation of Class and process them with _G[string] after creating the class
Or alternatively, use a function to delay the lookup:
Class{'MyClass', attribute = function() return MyClass end}

Zend_Framework 1.12 SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax

Before anything I am aware of the multiple questions asked concerning this exception. I have looked through them but have not found an answer for my particular problem. Most of the questions use the Zend_Db_Table::getDefaultAdapter(); , and I am not using that. Instead I am using an Application_Model_DbTable_Name and am left wondering if it´s possible to do so.
Also, I do have access since that´s the first thing I checked when I saw the error. The database is local and I access it with the same user/password through MySqlWorkBench.
My goal is to delete a row when two columns meet the criteria set in the controller action, like so:
public function deleteAction(){
$request = $this->getRequest();
$this->_validateDigit($request->getParam('id'));
// _validateDigit(..) checks that the variable is numeric and if it´s not it redirects to
// an error page
$db = new Application_Model_DbTable_BecasPerfiles();
$select = $db->select();
$select->where('unidad=?', $this->_getUnit())
->where('id=?', (int) $request->getParam('id'));
$db->delete($select->getPart(Zend_Db_Select::WHERE));
$this->_redirect('/profile/view-profiles/unit/'.$this->_getUnit());
}
private function _getUnit()
{
return Zend_Registry::getInstance()['session']->unit;
//unit is nothing but a string
}
Here is my DbTable class (real simple):
class Application_Model_DbTable_BecasPerfiles extends Zend_Db_Table_Abstract
{
protected $_name = 'becas_perfiles';
}
Here is the error that spits out:
Message: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in
your SQL syntax; check the manual that corresponds to your MySQL server version for
the right syntax to use near 'AND (id=7))' at line 1
Here is what calls my attention AND (id=7)), see the extra parenthesis? where is that coming from?
Here is the result of var_dump($select->getPart(Zend_Db_Select::WHERE));
array(2) { [0]=> string(33) "(unidad='Galería de Arte ULPGC')" [1]=> string(10) "AND (id=7)" }
Just for the fun of it, I tried switching the order of the where clause:
$select->where('id=?', (int) $request->getParam('id'))
->where('unidad=?', $this->_getUnit());
Here is the output:
Message: SQLSTATE[42000]: ...
syntax to use near 'AND (unidad='Galería de Arte ULPGC'))' at line 1
There it is again, AND (unidad='Galería de Arte ULPGC')) that second parenthesis. I don´t really know if that´s the problem (but I figure it is because otherwise I don´t know what could posssibly be wrong).
I tried just using one where condition (like id), and it deleted just fine. I´d really appreciate your help, thank you!
The problem here is that there is a mismatch between what getPart() returns and what delete() expects.
As you already pointed out, var_dump($select->getPart(Zend_Db_Select::WHERE)); also returns the logical operators in the where statement, but the delete() operator either expects an array in the form "field => value" or a string containing the full where clause.
So the simplest (untested) approach to fix your problem would be to pass $db->delete(implode(' ', $select->getPart(Zend_Db_Select::WHERE))); so that delete() receives a fully qualified where clause as string instead of an array it cannot handle.