Authorization code for access control - angularjs

How to write a angularjs code to check if the value passing is already exist in the array and if exists, user should be given access rights, if not, he should not be given rights

I think this should work
In your HTML you need to bind the ng-click with a button that will check the privilege and perform accordingly. Lets say the button is like this
<button type="button" ng-click="checkPrivilege();">Check Privilege</button>
Inside your controller you need to do some operations like this
//say, this is our privilege JSON
$scope.privilege = [
{
accessName: "ACCOUNT_ADD",
roles:['ADMIN','MANAGER']
},
{
accessName: "ACCOUNT_DELETE",
roles:['ADMIN']
}
];
$scope.checkPrivilege = function(){
//first you need to find out the role of the logged in user, lets say
//you get the role value from getLoggedInUserRole() function
$scope.userRole = getLoggedInUserRole();
//lets say the selected access name is 'ACCOUNT_ADD'
$scope.allowedAccessName = "ACCOUNT_ADD";
angular.forEach($scope.privilege, function(value, key) {
var privilegedRoles = privilege[key].roles;
var privilegedAccessName = privilege[key].accessName;
//check if the json object contains the same accessName which we need
if($scope.allowedAccessName === privilegedAccessName){
//check if the role is allowed or not for this user
if( privilegedRoles.indexOf(userRole) > -1 ){
//the logged in user has this privilege to accessName
break;
}else{
// the logged in user has no access to this accessName
}
}
});
}
You can look at the comment and understand. This code may not be exactly the same which you are expecting but you will surely get an idea about how you can achieve your goal.

Related

Using AngularJS to validate dynamically created 'input' element

I have a table that displays several entries, each has an <input>. The user can dynamically add additional inputs by clicking an "add entry" button. I need to iterate over them before saving and validate each one. I simplified my example to check that the value of each input is greater than 100 (ultimately I will use a pattern-match to validate MAC and IP addresses).
I can probably handle it if I could select all <input>s, but I would really like to select a specific <input> using an index I already have in my scope. I read that angular.element is a way, but I need to select something that was dynamically created, and thus not named something easy like id="myInput". Unless I use an id of "input" and append a unique number with Angular's $index in the id attribute?
Here is my Fiddle that shows what I'm doing. Line 44 is an if() that should check if any <input> is greater than 100. The "Save Row" button validates that the input is greater than 100, but if you edit a line, I need the "Save" button to validate any that the user has edited (by clicking Edit next to it).
tl;dr:
How can I use Angular to select an <input> that has been created dynamically?
I have updated your fiddle in a clean way so that you can maintain the validation in a generic method for both add & edit.
function validateBinding(binding) {
// Have your pattern-match validation here to validate MAC and IP addresses
return binding.ip > 100;
}
Updated fiddle:
https://jsfiddle.net/balasuar/by0tg92m/27/
Also, I have fixed the current issue with editing you have to allow multiple editing without save the first row when clicking the next edit on next row.
The validation of 'save everything' is now cleaner in angular way as below.
$scope.changeEdit = function(binding) {
binding.onEdit = true;
//$scope.editNum = newNum;
$scope.showSave = true;
};
$scope.saveEverything = function() {
var error = false;
angular.forEach($scope.macbindings, function(binding) {
if(binding.onEdit) {
if (validateBinding(binding)) {
binding.onEdit = false;
} else {
error = true;
}
}
});
if (error) {
alert("One/some of the value you are editing need to be greater than 100");
} else {
$scope.showSave = false;
}
}
You can check the updated fiddle for the same,
https://jsfiddle.net/balasuar/by0tg92m/27/
Note: As you are using angular, you can validate the model as above and no need to retrieve and loop the input elements for the validation. Also for your case, validating the model is sufficient.
If you need some advanced validation, you should create a custom
directive. Since, playing around with the elements inside the
controller is not recommended in AngularJS.
You can use a custom class for those inputs you want to validate. Then you can select all those inputs with that class and validate them. See this Fiddle https://jsfiddle.net/lealceldeiro/L38f686s/5/
$scope.saveEverything = function() {
var inputs = document.getElementsByClassName('inputCtrl'); //inputCtrl is the class you use to select those input s you want to validate
$scope.totalInputs = inputs.length;
$scope.invalidCount = 0;
for (var i = 0; i < inputs.length; i++){
if(inputs[i].value.length < 100){
$scope.invalidCount++;
}
}
//do your stuff here
}
On line 46 a get all the inputs with class "classCtrl" and then I go through the input s array in order to check their length.
There you can check if any of them is actually invalid (by length or any other restriction)

angular JS select first instance of this item

So my question is: how do I scan the JSON in angular to find the first instance of isPrimary:true and then launch a function with the GUID that is in that item.
I have a webservice whos JSON defines available Accounts with a display name and a GUID this generates a dropdown select list that calls a function with the GUID included to return full data from a web service.
In the scenario where theres only 1 OPTION I dont show the SELECT and simply call the function with the single GUID to return the data from the service. If theres no options I dont show anything other than a message.
Code below shows what I currently have.
The Spec has now changed and the data they are sending me in the first service call which defines that select list is now including a property isPrimary:true on one of the JSON object along with its GUID as per the rest
I now need to change my interface to no longer use the SELECT list and instead fire the function call to the service for the item that contains the isPrimary:true property. However there may be multiple instances where isPrimary:true exists in the returning JSON so I just want to fire the function on the first found instance of isPrimary:true
Equally if that property isnt in any of the JSON items then just fire the function on the first item in the JSON.
My current Code is below - you can see the call to retrieve the full details is from function:
vm.retrieveAccount(GUID);
Where the GUID is supplied with each JSON object
Code is:
if (data.Accounts.length > 1) {
vm.hideAcc = false;
setBusyState(false);
//wait for the user to make a selection
} else if (data.Accounts.length == 1){
vm.hideAcc = true;
// Only 1 acc - no need for drop down get first item
vm.accSelected = data.Accounts[0].UniqueIdentifier;
vm.retrieveAccount(vm.accSelected);
} else {
// Theres no accounts
// Hide Drop down and show message
setBusyState(false);
vm.hideAcc = true;
setMessageState(false, true, "There are no Accounts")
}
Sample of new JSON structure
accName: "My Acc",
isPrimary: true,
GUID: "bg111010101"
Still think that's a weird spec, but simple enough to solve. Just step through the array and return the first isPrimary match. If none are found, return the first element of the array.
var findPrimary = function(data) {
if (!(Array.isArray(data)) || data.length == 0) {
return false; // not an array, or empty array
}
for (var i=0; i<data.length; i++) {
if (data[i].isPrimary) {
return data[i]; // first isPrimary match
}
}
// nothing had isPrimary, so return the first one:
return data[0];
}

joomla - Storing user parameters in custom component issue

Hi for my custom component I need to set some custom parameters for joomla user for membership for checking if the user ni trial period or not and it can be change from the component admin panel for specific user.
The problem arises while retrieving the parameter. I think it is stored in cookie and it isn^t updated. I wrote the code like that to check it.
$user = JFactory::getUser(JRequest::getVar('id','0'));
echo $user->getParam('trialPeriod','0');
to save the value I am useing JHTML booleanlist.
$user->setParam('trialPeriod',$data['trialPeriod']);
$user->save();
Then is stores the value in joomla users table in the row of that user with column of params as;
{"trialPeriod":"0"}
in this situation it echoes the value as 0. Then I am changin the state of trialPeriod var as 1 and storing in db it updates the db as;
{"trialPeriod":"1"}
After all I am refreshing the page where the value is prompt the the screen the the value remains still the same as 0;
To clarify;
First of all there is no problem with saving the param it is changed properly. The problem is retrieving the changed one. The releated piece of code is following;
$user = JFactory::getUser();
$doc = JFactory::getDocument();
if($user->getParam('trialPeriod',0) == 0){
$ed = JFactory::getDate($obj->expirationDate);//obj is user from custom table and there is no problem with getting it.
$isTrialEnd = FALSE;
}else{
$ed = JFactory::getDate($user->getParam('trialExp',0));
$isTrialEnd = TRUE;
}
if($isTrialEnd){
//do something else
}else{
echo $user->getParam('trialPeriod','0');
}
actually big part of the code is unneccessary to explain it but you will get the idea.
What is the solution for this?
Editted.
$app = JFactory::getApplication();
$config = JFactory::getConfig();
$db = $this->getDbo();
$isNew = empty($data['uid']) ? true : false;
$params = JComponentHelper::getParams('com_dratransport');
if($isNew){
// Initialise the table with JUser.
$user = new JUser;
// Prepare the data for the user object.
$username = self::getCreatedUserName($data['type']);
$data['username'] = !empty($data['username']) ? $data['username'] : $username;
$data['password'] = $data['password1'];
$useractivation = $params->get('useractivation');
// Check if the user needs to activate their account.
if (($useractivation == 1) || ($useractivation == 2)) {
$data['activation'] = JApplication::getHash(JUserHelper::genRandomPassword());
$data['block'] = 1;
}
}else{
$user = JFactory::getUser($data['uid']);
$data['password'] = $data['password1'];
}
$membership = DraTransportHelperArrays::membershipCFG();
$membership = $membership[$data['membership']];
if($data['membership'] == 4)
$data['groups'] = array($params->get('new_usertype',2),$params->get($membership,2));
else
$data['groups'] = array($params->get($membership,2));
$data['name'] = $data['companyName'];
$user->setParam('trialPeriod',$data['trialPeriod']);
// Bind the data.
if (!$user->bind($data)) {
$this->setError(JText::sprintf('COM_USERS_REGISTRATION_BIND_FAILED', $user->getError()));
return false;
}
// Load the users plugin group.
JPluginHelper::importPlugin('user');
// Store the data.
if (!$user->save()) {
$app->enqueuemessage($user->getError());
$this->setError(JText::sprintf('COM_USERS_REGISTRATION_SAVE_FAILED', $user->getError()));
return false;
}
this piece of code is for storing the data releated with the users table.
Turns out this was the fact that Joomla stores the JUser instance in the session that caused the problem.
When changing a user's parameters from the back-end, the changes are not reflected in that user's session, until she logs out and back in again.
We could not find an easy option to modify anther user's active session, so we resorted to the use of a plugin that refreshes the JUser instance in the logged-in users' session, something like the following:
$user = JFactory::getUser();
$session = JFactory::getSession();
if(!$user->guest) {
$session->set('user', new JUser($user->id));
}
(reference: here).

In Firebase, is there a way to get the number of children of a node without loading all the node data?

You can get the child count via
firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });
But I believe this fetches the entire sub-tree of that node from the server. For huge lists, that seems RAM and latency intensive. Is there a way of getting the count (and/or a list of child names) without fetching the whole thing?
The code snippet you gave does indeed load the entire set of data and then counts it client-side, which can be very slow for large amounts of data.
Firebase doesn't currently have a way to count children without loading data, but we do plan to add it.
For now, one solution would be to maintain a counter of the number of children and update it every time you add a new child. You could use a transaction to count items, like in this code tracking upvodes:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
For more info, see https://www.firebase.com/docs/transactions.html
UPDATE:
Firebase recently released Cloud Functions. With Cloud Functions, you don't need to create your own Server. You can simply write JavaScript functions and upload it to Firebase. Firebase will be responsible for triggering functions whenever an event occurs.
If you want to count upvotes for example, you should create a structure similar to this one:
{
"posts" : {
"-JRHTHaIs-jNPLXOQivY" : {
"upvotes_count":5,
"upvotes" : {
"userX" : true,
"userY" : true,
"userZ" : true,
...
}
}
}
}
And then write a javascript function to increase the upvotes_count when there is a new write to the upvotes node.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});
You can read the Documentation to know how to Get Started with Cloud Functions.
Also, another example of counting posts is here:
https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js
Update January 2018
The firebase docs have changed so instead of event we now have change and context.
The given example throws an error complaining that event.data is undefined. This pattern seems to work better:
exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
const data = change.after.val();
const count = Object.keys(data).length;
return change.after.ref.child('_count').set(count);
});
```
This is a little late in the game as several others have already answered nicely, but I'll share how I might implement it.
This hinges on the fact that the Firebase REST API offers a shallow=true parameter.
Assume you have a post object and each one can have a number of comments:
{
"posts": {
"$postKey": {
"comments": {
...
}
}
}
}
You obviously don't want to fetch all of the comments, just the number of comments.
Assuming you have the key for a post, you can send a GET request to
https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.
This will return an object of key-value pairs, where each key is the key of a comment and its value is true:
{
"comment1key": true,
"comment2key": true,
...,
"comment9999key": true
}
The size of this response is much smaller than requesting the equivalent data, and now you can calculate the number of keys in the response to find your value (e.g. commentCount = Object.keys(result).length).
This may not completely solve your problem, as you are still calculating the number of keys returned, and you can't necessarily subscribe to the value as it changes, but it does greatly reduce the size of the returned data without requiring any changes to your schema.
Save the count as you go - and use validation to enforce it. I hacked this together - for keeping a count of unique votes and counts which keeps coming up!. But this time I have tested my suggestion! (notwithstanding cut/paste errors!).
The 'trick' here is to use the node priority to as the vote count...
The data is:
vote/$issueBeingVotedOn/user/$uniqueIdOfVoter = thisVotesCount, priority=thisVotesCount
vote/$issueBeingVotedOn/count = 'user/'+$idOfLastVoter, priority=CountofLastVote
,"vote": {
".read" : true
,".write" : true
,"$issue" : {
"user" : {
"$user" : {
".validate" : "!data.exists() &&
newData.val()==data.parent().parent().child('count').getPriority()+1 &&
newData.val()==newData.GetPriority()"
user can only vote once && count must be one higher than current count && data value must be same as priority.
}
}
,"count" : {
".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
newData.getPriority()==data.getPriority()+1 "
}
count (last voter really) - vote must exist and its count equal newcount, && newcount (priority) can only go up by one.
}
}
Test script to add 10 votes by different users (for this example, id's faked, should user auth.uid in production). Count down by (i--) 10 to see validation fail.
<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
window.fb = new Firebase('https:...vote/iss1/');
window.fb.child('count').once('value', function (dss) {
votes = dss.getPriority();
for (var i=1;i<10;i++) vote(dss,i+votes);
} );
function vote(dss,count)
{
var user='user/zz' + count; // replace with auth.id or whatever
window.fb.child(user).setWithPriority(count,count);
window.fb.child('count').setWithPriority(user,count);
}
</script>
The 'risk' here is that a vote is cast, but the count not updated (haking or script failure). This is why the votes have a unique 'priority' - the script should really start by ensuring that there is no vote with priority higher than the current count, if there is it should complete that transaction before doing its own - get your clients to clean up for you :)
The count needs to be initialised with a priority before you start - forge doesn't let you do this, so a stub script is needed (before the validation is active!).
write a cloud function to and update the node count.
// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.userscount = functions.database.ref('/users/')
.onWrite(event => {
console.log('users number : ', event.data.numChildren());
return event.data.ref.parent.child('count/users').set(event.data.numChildren());
});
Refer :https://firebase.google.com/docs/functions/database-events
root--|
|-users ( this node contains all users list)
|
|-count
|-userscount :
(this node added dynamically by cloud function with the user count)

Apex Managed Sharing trigger stopped working when changed field from Lookup to Master-Detail

I have two custom objects, listing and transaction. Initially I had a Lookup field on the transaction object to the listing object. I had Apex Managed Sharing set up to share the transaction with several user lookup fields on the related listing object. It was working perfectly.
I changed the field type of the listing field on the transaction object to Master-Detail and now I get the following error every time I try to save a new transaction:
"TransactionApexSharing: execution of AfterInsert caused by: line 1, column 1: trigger body is invalid and failed recompilation: Entity is not org-accessible"
Transaction and Listing objects are both set to Private and I can't find any typos in the code. The trigger hasn't been changed since it was working with the Lookup field.
Here is my code:
trigger TransactionApexSharing on Transaction__c (after insert, after update) {
if(trigger.isInsert || trigger.isUpdate){
Set<id> triggerIds = trigger.newMap.keyset();
List<Transaction__c> listWithParentData = [select Listing__r.Listing_Agent_1__r.id, Listing__r.Listing_Agent_2__r.id, Listing__r.Listing_Agent_3__r.id from Transaction__c where id in :triggerIds];
List<Transaction__Share> tranShrs = new List<Transaction__Share>();
Transaction__Share laShr;
Transaction__Share la2Shr;
Transaction__Share la3Shr;
Transaction__Share saShr;
Transaction__Share sa2Shr;
Transaction__Share sa3Shr;
for(Transaction__c atransaction : listWithParentData){
laShr = new Transaction__Share();
la2Shr = new Transaction__Share();
la3Shr = new Transaction__Share();
laShr.ParentId = atransaction.Id;
la2Shr.ParentId = atransaction.Id;
la3Shr.ParentId = atransaction.Id;
if (atransaction.Listing__r.Listing_Agent_1__c != null)
{
// Set the ID of user or group being granted access
laShr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_1__c;
// Set the access level
laShr.AccessLevel = 'edit';
// Set the Apex sharing reason
laShr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
// Add objects to list for insert
tranShrs.add(laShr);
}
if (atransaction.Listing__r.Listing_Agent_2__c != null)
{
la2Shr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_2__c;
la2Shr.AccessLevel = 'edit';
la2Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(la2Shr);
}
if (atransaction.Listing__r.Listing_Agent_3__c != null)
{
la3Shr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_3__c;
la3Shr.AccessLevel = 'edit';
la3Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(la3Shr);
}
}
for(Transaction__c mytransaction : trigger.new){
// Instantiate the sharing objects
saShr = new Transaction__Share();
sa2Shr = new Transaction__Share();
sa3Shr = new Transaction__Share();
// Set the ID of record being shared
saShr.ParentId = mytransaction.Id;
sa2Shr.ParentId = mytransaction.Id;
sa3Shr.ParentId = mytransaction.Id;
if (mytransaction.Selling_Agent_1_User__c != null)
{
saShr.UserOrGroupId = mytransaction.Selling_Agent_1_User__c;
saShr.AccessLevel = 'edit';
saShr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(saShr);
}
if (mytransaction.Selling_Agent_2_User__c != null)
{
sa2Shr.UserOrGroupId = mytransaction.Selling_Agent_2_User__c;
sa2Shr.AccessLevel = 'edit';
sa2Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(sa2Shr);
}
if (mytransaction.Selling_Agent_3_User__c != null)
{
sa3Shr.UserOrGroupId = mytransaction.Selling_Agent_3_User__c;
sa3Shr.AccessLevel = 'edit';
sa3Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(sa3Shr);
}
}
// Insert sharing records and capture save result
// The false parameter allows for partial processing if multiple records are passed
// into the operation
Database.SaveResult[] lsr = Database.insert(tranShrs,false);
// Create counter
Integer i=0;
// Process the save results
for(Database.SaveResult sr : lsr){
if(!sr.isSuccess()){
// Get the first save result error
Database.Error err = sr.getErrors()[0];
// Check if the error is related to a trivial access level
// Access levels equal or more permissive than the object's default
// access level are not allowed.
// These sharing records are not required and thus an insert exception is
// acceptable.
if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION
&& err.getMessage().contains('AccessLevel'))){
// Throw an error when the error is not related to trivial access level.
trigger.newMap.get(tranShrs[i].ParentId).
addError(
'Unable to grant sharing access due to following exception: '
+ err.getMessage());
}
}
i++;
}
}
}
When you flip the relationship from lookup to master detail a lot changes.
You lose fine grained access to details, that's it. Whatever rights user has to the master - he has them for the details as well (OK, excluding stuff like 'Read/Create/Edit/Delete' which is in Profiles, I'm talking about access to particular record, not general rights).
On the detail "OwnerId" will disappear (can't be queried, described etc)
Which has some finer points if you ever:
need approval process on detail and suddenly you can't use queues, you need to specify users directly.
users ask "what happened to My Transactions" in list views or reports
Last but not least - Detail__Share table disappears as well.
Try editing your trigger in the sandbox (just add 1 space or something), it will probably complain that there's no such table Transaction_Share.
You can either make sure that Agents have edit rights to parent listing (but that means they can edit ANY related transaction) and ditch the trigger or undo the M-D. It's really a case of going back to your users and asking for business logic ;)
Why did you flip it to M-D? Cascade delete, rollups etc. could be done with a bit of code if it turns out you can't afford losing this fine-grained edit access to each transaction.
But after a quick look at your code it seems to me you'll be fine with controlling the access on Listing level and not on the details?

Resources