Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


NotionCommotion last won the day on September 22 2021

NotionCommotion had the most liked content!

Contact Methods

  • Website URL

Profile Information

  • Gender
    Not Telling

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

NotionCommotion's Achievements

Prolific Member

Prolific Member (5/5)




Community Answers

  1. class IteratorIterator implements OuterIterator and this class permits access to methods of the inner iterator via the __call magic method. class RecursiveIteratorIterator implements OuterIterator interface OuterIterator extends Iterator Should I conclude that any class that implements OuterIterator permits access to methods of the inner iterator via the __call magic method, and not just IteratorIterator? Thanks PS. Totally off topic, but is there a way to embed a link when one quotes something (i.e. your quote would include https://www.php.net/manual/en/class.iteratoriterator.php)? Array ( [0] => RecursiveIteratorIterator [1] => IteratorIterator [2] => FilterIterator [3] => RecursiveFilterIterator [4] => CallbackFilterIterator [5] => RecursiveCallbackFilterIterator [6] => ParentIterator [7] => LimitIterator [8] => CachingIterator [9] => RecursiveCachingIterator [10] => NoRewindIterator [11] => AppendIterator [12] => InfiniteIterator [13] => RegexIterator [14] => RecursiveRegexIterator [15] => RecursiveTreeIterator )
  2. Thanks requinix, I suspected something like that but could find no documentation. Also suspect it is documented somewhere but I just looked at the wrong place. Must say, though, that it is well hidden documentation.
  3. Start commenting out code until you don't get the error. Or better yet, comment out all first and try just echo('hello');. If still an error, try just an html page without php. Check your php error logs and http logs. If php runs but not all your script, check what versions of php are running on both local and kkgramping.
  4. What's not working? Do you have erroring enabled? If not, enable it. Also, do sanity checks such as echo('SITE_ROOT: '.SITE_ROOT); to make sure they are what you think they should be.
  5. I'm confused. Why do I not get an error with $it->getSubPathName() and $it->getSubPath()? $it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)); $m = array_flip(get_class_methods($it)); printf('class: %20s getSubPathName: %s getSubPath: %s key: %s'.PHP_EOL, get_class($it), isset($m['getSubPathName'])?'y':'n', isset($m['getSubPath'])?'y':'n', isset($m['key'])?'y':'n'); print_r(get_class_methods($it)); echo 'SubPathName: ' . $it->getSubPathName() . PHP_EOL; echo 'SubPath: ' . $it->getSubPath() . PHP_EOL; echo 'Key: ' . $it->key() . PHP_EOL.PHP_EOL; output: class: RecursiveIteratorIterator getSubPathName: n getSubPath: n key: y Array ( [0] => __construct [1] => rewind [2] => valid [3] => key [4] => current [5] => next [6] => getDepth [7] => getSubIterator [8] => getInnerIterator [9] => beginIteration [10] => endIteration [11] => callHasChildren [12] => callGetChildren [13] => beginChildren [14] => endChildren [15] => nextElement [16] => setMaxDepth [17] => getMaxDepth ) SubPathName: . SubPath: Key: src/.
  6. Thanks for the Party model link. I will definitely spend some time going over it as well as its predecessor Bill of Materials. AFAIK roles dictate the user's job title (ROLE_ADMIN , ROLE_HR_MANAGER) and can also be used slightly differently by dictating some capability of the job title (ROLE_CAN_FIRE_PEOPLE) and grant permission by resource type but not per resource. For my requirements, I would need to create individual roles for every project (ROLE_CAN_VIEW_PROJECT_123, etc) which doesn't sound right. I agree that voters give me an easy and consistent way to hook and handling things, but don't dictate how they are handled and leave that to me. They will give me some attribute string (could be an action such as view/edit or whatever I want), the resource, and user (actually token) which is great, but I will still need other information like whether a given user can view content in a given project, and therefor would need "something" similar to the original schema I showed. No? So, in order to grant permission by individual resources, I need an identifier. Just realized I had project.access_control_id referencing project_access_control.id and project_access_control.resource_id referencing project.id which would identify, but don't think it is the right way. I also had project_access_control.resource_id reference project.id which will also identify, however, don't know whether I like my use of subtyping and extending abstract_access_control by project_access_control (and asset_access_control and vendor_access_control). So, maybe a third way to identify the thing? The following represents how the Symfony ACL bundle does it. Not sure whether their exact schema will work for me but sure they are doing things that I haven't thought of. I like how it is less coupled to my domain, but at the same time don't like how it is less coupled to my domain (no FKs acl_object_identifier to naics_code.id, etc, acl_sercurity_identifiers to user.id). Not sure why they don't have a FK between acl_classes.id and acl_object_identifier.class_id. My confusion is how I would expose security settings to an admin user. The admin user will have some role ROLE_MANAGE_ACL so no problem there but will need to perform some queries to show all resources that user 123 has been given permission and all users that have permission to act on resource 321. Normally this is done with FK's and joins but I don't have them and instead have tables which include the names of the resources and users. Maybe not possible?
  7. Thanks again gizmola, What I heard is when I have OOP code that clearly benefits from inheritance and am persisting in a relational database, I should not be ashamed of using the supertype/subtype model. Guess it paid off asking the same question over and over again until I got the answer I wanted! By the way, can't claim credit myself and it was Rudy years ago who set me in this direction. I also heard the party model mentioned. benanamen recommended this approach a few months back. Didn't make the connection until now, but this appears to be a model which has super/sub types at its core.
  8. I completely agree! Most of the time, I am controlling "what content should I see based on my place within an organization or department". For a couple edge cases, however, I need to control whether "I have access to this feature or function". I don't really understand attribute based access control, however, based on my recent research, looks like the first need is best handled by using RBAC (role based) and the second is best handled by using ACL. Agree? If so, then I will need to use two models. Also, I am using Symfony which really focuses on RBAC, and while they have an ACL bundle, they recommend not using it (and it also doesn't appear to support changing the required resource permission). There is also Laminas's ACL component which you previously brought to my attention and I have since spent some time investigating. I was very interesting, had some great features, and was highly decoupled, however, I kind of concluded that it was overkill. Maybe not? High level requirements and proposed solutions are: Normal role access control. Use Symfony's normal approach (which happens to be voters) Ability to give regular users additional permissions on a per resource basis. While not a must have feature, it is pretty simple since the required permission of the resource doesn't change. If this was the only one-off, I would likely just use a resource/user junction table to store extra roles and call it good, but have the next one... Ability to define both the required permission to perform some action on a given resource as well as the authority of each user for that specific resource. Unfortunately, this is an important requirement. Are you thinking that Laminas's ACL component would be well suited to meet this need? If so, would like to discuss more. I haven't yet gotten through the details, but also felt that groups were the key. Sometimes called them groups and other times called them memberships. For my use case, I don't think I need multiple groups but just one group per project. If more requirements are later added, maybe then more groups, but oh well. If project contains content, all the content in some projects needs to be available to all and for other projects only is available to some, how do you think your groups approach would work? PS. Would you agree that Unix file permissions and ACL are both examples of Discretionary Access Control? https://ebrary.net/26657/computer_science/access_control_strategies says they are and just want to make sure I should believe them. The remained of my reply is really to respond to your other comments. I will completely take and appreciate any critique you offer, however, I am not necessarily asking for it. public_id is an identifier ID which is unique per entity type for a given tenant, and is just used so I don't have to expose the DB PK or use a UUID or anything. I actually think my homegrown solution is pretty clean, however, I am always concerned when I need to use a homegrown solution for what seems to be a common need. tenant_user belongs to tenant and is joined by tenant_id. vendor_user belongs to vendor, is joined by vendor_id, and vendor belongs to tenant and is joined by tenant_id. As such, I really don't need tenant_id in vendur_user, but I wish to keep content completely isolated between tenants, and ended up adding tenant_id to every entity except (for a few common entities which are not owned by a tenant). tenant/vendor_default_permission is like unix permission 0755 and represents my Permission class. I didn't show tenant but if I did, you would see similar, and they act as prototypes until they get to their final home. I copy them from tenant to project_access_control so that the project_access_control version could be changed without changing the tenant version, and then again copy it to project_member for the same reason. tenant/vendor_read_permission represents my PermissionEnum class, and several bits in tenant/vendor_default_permission hold the same content which I know is a big SQL no-no. It is just used so I can do queries without indexing concern when using bitwise operators in the where clause, and is on my list of things to maybe change. class Permission { private const READ = 0b00000011, CREATE = 0b00001100, MODIFY = 0b00110000, COWORKER= 0b01000000, OWNER = 0b10000000 ; public function __construct( private PermissionEnum $read, private PermissionEnum $create, private PermissionEnum $modify, private bool $restrictToOwner, private bool $coworkers, ) { } public static function create(int $value): self { return new self( PermissionEnum::from($value&self::READ), PermissionEnum::from($value&self::CREATE >> 2), PermissionEnum::from($value&self::MODIFY >> 4), boolval($value&self::COWORKER), boolval($value&self::OWNER), ); } // other methods... public function getValue(): int { $rs = $this->read->value | $this->create->value << 2 | $this->modify->value << 4; $rs = $this->coworkers?$rs|self::COWORKER:$rs & (~ self::COWORKER); $rs = $this->restrictToOwner?$rs|self::OWNER:$rs & (~ self::OWNER); return $rs; } } enum PermissionEnum: int { private const MAP = [ 'public' => 0b00, 'owner' => 0b01, 'restrict' => 0b10, 'override' => 0b11 //Not sure what this should be used for. ]; case public = 0b00; case owner = 0b01; case restrict = 0b10; case override = 0b11; //Not sure what this should be used for. }
  9. Thank you gizmola, While the subtyping is somewhat of a different topic, would like your thoughts and will reply with two separate responses. I use this approach all the time and it seems like others rarely if at all do. Either I know more than everyone else in the world or there is something I don't know which makes this model less necessary. Humm, which one is it... Let's use your example from learndatamodeling. Why would I use this model? It looks pretty with all the common data in the employee table. I don't think there is any normalization rule to go this way. Agree? If so, not a good reason. I have one or more other tables that need to reference employee. Yes, very often. Sure, I could have two joins to both full time and hourly employee, but that is more complicated from both a SQL perspective (can't use non-null, require triggers to check) and PHP perspective (no AbstractEmployee to type declare), and is also less performant (will require unions instead of joins) and less flexible. I keep on hearing favor composition over inheritance, and while I finally understand and agree that it should be used in many cases, don't see the value for modelling entities such as this one. Is there a better way to handle requirements such as this? Or maybe it is a common design pattern and I just think I am the only one using it? Some of my attempts searching for this holy grail.
  10. Was thinking "class table inheritance" which is my understanding just using super/subtype SQL to help enforce inheritance on the DB level. PS. Sorry, after posting this question, started thinking it wasn't appropriate. Thing is programming it totally not my day job but only my hope to quit my day job and do something else, and the only people I know that know programming are here.
  11. I think I am close on how I should implement access control, however, considering that I am making up much of the approach myself, I expect that there are some flaws in my approach and would appreciate a reality check. Use Case and User Requirements The application is a content management system which allows capital project users to request content from vendors for a specific project, vendor users provide the content, and facility operation users to utilize the content upon project completion. Note that multiple content records will always remain associated with one project. Capital project users and facility operation users both belong to the tenant (owner) organization. Vendor users belong to a vendor organization which has a many-to-one relationship to tenant owner. As such, I have a AbstractUser which is extended using CTI by TenantUser and VendorUser. Allowed permissions per user type are: Vendor users can: Read the organization they belong to (the vendor company) and read/update their own profile. View projects and perform CRUD operations on project content based on the project, user, specification, and whether they own/uploaded the original content. Tenant normal users can: Read the organization they belong to (the tenant/owner company) and read/update their own profile. Manage a given resource type (i.e. all vendors) when allowed by admin. Manage a specific resource (i.e. Acme Pipe and Fittings Company) when allowed by admin. Read everything except for projects and content contained within projects unless the projects have been made available to them. View projects and perform CRUD operations on project content based on the project, user, specification, and whether they own/uploaded the original content. Tenant admin users can: Do everything a normal user can plus manage resources. Assign permission to a normal tenant user to manage all resources of a given type (i.e. allow them to manage all vendors). Assign permission to a normal tenant user to manage a specific resources (i.e. allow them to manage Acme Pipe and Fittings Company). The utilized access control strategy should also promote: a simple and intuitive ux which reflects the way normal people think without requiring large amounts of script to maintain. consistency among projects yet allow flexibility by having new projects be prepopulated with a clone of the tenant wide default permissions prototype. Proposed Implementation It seems like role based access control provides the most intuitive for most needs, however, as far as I could tell, will not allow all functionality and my intent is to use multiple strategies. To implement RBAC, I will: Create action roles such as ROLE_VIEW_VENDOR and ROLE_MANAGE_VENDOR, ROLE_VIEW_PROJECT and ROLE_MANAGE_PROJECT, ROLE_VIEW_ASSET and ROLE_MANAGE_ASSET, etc. Create user roles such as: ROLE_USER which is any authenticated user and can only view/edit their own profile. ROLE_VENDOR_USER which uses hierarchy to extend ROLE_USER. ROLE_TENANT_USER which uses hierarchy to extend ROLE_USER, ROLE_VIEW_VENDOR, ROLE_VIEW_ASSET, etc ROLE_ADMIN_USER which uses hierarchy to extend ROLE_TENANT_USER, ROLE_MANAGE_VENDOR, ROLE_MANAGE_PROJECT, ROLE_MANAGE_ASSET, ROLE_MANAGE_USER, etc. Then to allow a tenant user to manage a given resource type (i.e. all vendors) when allowed by, ROLE_MANAGE_VENDOR can be added to the user's role array. For the remaining functionality, I will need to join the user to the resource, and I refer to this intersecting entity as "ResourceMember". Then to allow a tenant user to manage a specific resource (i.e. Acme Pipe and Fittings Company) when allowed by admin, ROLE_MANAGE_VENDOR can be added to ResourceMember user roles property. Or maybe since ResourceMember joins a single resource thus we already know it is a vendor resource, could just add role ROLE_MANAGE. The CRUD operations on project content gets more complicated. My plan for this is a quasi Unix file permissions where each project is analogous to a directory and content added to the project is analogous to a file. The project holds the tenant and vendor user required permission settings needed to create, read, update, etc content, and each could be "other" (any authenticated user), "owner" (they uploaded it and admin hasn't reassigned), and "restrict". If "restrict" and they do not belong to the membership group (i.e. joined by resource_member), they are denied, but if they do, a similar check is performed using resource required permissions for that specific user stored in ResourceMember and its restrict will be final. Note that the ERD below shows these permissions as integers but they represent permissions such as 0755 (but no execution of course!). I don't plan on allowing the project (container) to have an owner so create/owner doesn't really make sense but the others seem to. When creating a new project, the project permissions and project-member permissions for both a tenant and vendor user will be cloned from the tenant's default prototypes and stored in the project, and when adding a member, the appropriate copy of the project-member permission stored in the project will be cloned and stored in the membership entity. One other detail. User roles and additional group member entity roles are stored in a JSON array, and I might have performance issues when performing a GET collection request. I thought that some DB's might be able to handle this, but am suspect, and instead will have some duplicating read flags persisted in the DB which will be used to dynamically create the appropriate SQL without needing to hydrate the entities. Up to now, I am just asking whether my approach is viable or not and if so whether you have any recommendations, advice, caution, etc, however, do have a specific question. Up above, I said "I will need to join the user to the resource". No issue with the user but not so with the resource side. My current approach is to join another table which has a one-to-one relationship to the resource (see below schema). One thing I liked about this approach is doesn't pollute my resources with permission prototypes, owner, redundant read flags, and other properties just used for access control. Don't like how it uses CTI which I think I overuse. Also, just noticed that the resource (i.e. Project) has the access control primary key and not the other way around and should fix this. Possible changes to my other table approach are: Have a separate junction table for Project, Vendor, Asset, etc. Doesn't seem as "SQL proper" and just have some gut feel I will later run into issues. Not use a FK constraint but use the resource class and its PK as the identifier. I've always shied away from this approach and have the same guy feel concern, but maybe applicable. If option 1 (separate junction tables) or option 2 (no FKs) are used, think that I should locate the access control properties in the resource (project) and get rid of the extra tables? Use super/sub SQL schema so that Project, Vendor, Asset, etc all share a single PK. Makes the join easy and no gut feel concern about issues, but definitely way more difficult to later add some resource, and will also complicate adding a resource which is already using CTI (like User). I am on uncharted territory and typically when so in the past, never ended well and I needed to later do differently.
  12. Look into both using relative time formats such as new DateTime('monday midnight') to get your boundaries and datetime::diff() to get your durations.
  13. Thanks requnix, Deep down, I knew what you are saying is true and that my desire to make it look pretty and compact should not be the goal. Still it is still tempting... Since it is complete and working, probably will give it a try for a bit and if issues, go back to a transitional approach. While not the purpose of my post, if you have a better word than "public", I would like to hear it. It does not mean the public in general but users who have logged on and authenticated using their username and password, but are not the owner of the resource and do not belong to the resource's group. While not exact, pretty close to Unix permissions and maybe other or world is more appropriate...
  14. Currently, I implemented adding additional columns which are redundant to the single integer stored in the DB but can be used only when filtering on the ability to read. This permission integer is follows and has contains some enums and binary flags: class Permission { public function __construct( private PermissionEnum $read, private PermissionEnum $create, private PermissionEnum $modify, private bool $restrictToOwner, private bool $coworkers, ) { } } enum PermissionEnum: int { case public = 0b00; case owner = 0b01; case restrict = 0b10; case override = 0b11; //currently not used. } Schema is as follows. As you can see, separate copies of the permission values are stored multiple places. tenant - id - tenant_default_permission_prototype (integer defining multiple boolean and enum properties) - vendor_default_permission_prototype (same as for tenant but will be used when vendor's log on) - tenant_member_permission_prototype (integer defining multiple boolean and enum properties for any members that are added) - vendor_member_permission_prototype (same as for tenant but will be used when vendor's log on) project - id project_access_control - id (one-to-one to project.id) - tenant_default_permission (integer defining multiple boolean and enum properties. Will be copied from tenant.tenant_default_permission_prototype) - vendor_default_permission (same as for tenant but will be used when vendor's log on) - tenant_member_permission_prototype (integer defining multiple boolean and enum properties for any members that are added) - vendor_member_permission_prototype (same as for tenant but will be used when vendor's log on. Will be copied from tenant.tenant_member_permission_prototype) - tenant_read_permission (options are public, owner, or restrict. duplicates information in tenant_default_permission.read) - vendor_read_permission (same as for tenant but will be used when vendor's log on) project_member - project_access_control_id (or maybe project_id?) - user_id - permission (integer defining multiple boolean and enum properties for any members that are added. Will be copied from the applicable project permission prototype) - read_permission (options are public, owner, or restrict. duplicates information in permission.read) user - id - descriminator tenant_user - id (one-to-one to user.id) vendor_user - id (one-to-one to user.id) A query when a tenant user logs on looks like the following when using my redundant read permissions, and will be almost the same except will use vendor_read_permission instead of tenant_read_permission. SELECT * FROM project p INNER JOIN project_access_control pac ON p.access_control_id = pac.id LEFT JOIN project_member pm ON pac.id = pm.access_control_id WHERE p.tenant_id = 123 AND ( pac.tenant_read_permission = 0 OR pm.read_permission = 0 OR (pac.tenant_read_permission = 2 AND pac.owned_by_id = 321) OR (pm.read_permission = 1 AND pac.owned_by_id = 321) ) A fictional query which I want to use (or maybe I just keep with the previous) looks like the following. Instead of filtering based on discrete columns, my integer permission columns would be used and the SQL would need to define which bit or bits to use. Not sure if it works this way but envision an index being a bunch of numbers from small to large, and since the read operations are the ones which would value from this index, thinking the read bits should be the bits on the left (or big values or whatever they are called). SELECT * FROM project p INNER JOIN project_access_control pac ON p.access_control_id = pac.id LEFT JOIN project_member pm ON pac.id = pm.access_control_id WHERE p.tenant_id = 123 AND ( areBitsSet(pac.tenant_default_permission, 0, 0) OR areBitsSet(pm.permission, 1, 0) OR (areBitsSet(pac.tenant_default_permission, 2, 2) AND pac.owned_by_id = 321) OR (areBitsSet(pm.permission, 3, 1) AND pac.owned_by_id = 321) )
  15. Instead of using Laminas or Symfony's ACL component, I ended up coming up with my own, and only providing the following to provide context: For each entity where access control is needed, permissions for read, edit, and create can be public, owner, or restrict. If restrict, then it will check if a user is a member of the specific entity in question, and if so, get the same read/entity/create permissions for the member. There are also two types of users: those that belong to the tenant and those who belong to vendors who in turn belong to a given tenant. In addition, I wish to allow each tenant to define their own sitewide permissions which are then cloned when adding a new entity which is under access control, and also something similar when adding members. Bottom line is I have a bunch of SQL tables which have a bunch of columns which are either boolean or some string with limited enum options. In addition, I am using doctrine's value object solution (embeddables) which givens the columns some long name (granted, sure I can fix this part). I expect I should let it go but it is just damn ugly, so I created a class which takes a small integer stored in the DB and creates my permission object, and all works as desired. When dealing with a single resource (read, delete, edit, etc), I have no concerns about performance as PHP only has to deal with a single record provided by the DB. But when requesting a collection of resources, I wish SQL to do the work, and it will need to filter based on this small integer described in the previous paragraph. Obviously, I will want to index it, but I don't know how indexing works when using a bitwise operator in the WHERE clause. How does indexing work when using bitwise operators in the WHERE clause? Is there anything that can be done to improve performance if an issue? For instance: Locating the read permission bits on the high side of the bit string so the index reflects them more than edit/delete/etc which won't need an index. Add additional columns which are redundant to the single integer stored in the DB but can be used only when filtering on the ability to read.
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.