membermatters / MemberMatters

An open source membership, access and payments portal for makerspaces and community groups.
https://membermatters.org
MIT License
51 stars 25 forks source link

It should be possible to only open interlocks/doors when a particular induction course has taken place #225

Open proffalken opened 1 year ago

proffalken commented 1 year ago

Is your feature request related to a problem? Please describe.

With the new Moodle integration coming into prod soon, it would be great to be able to only allow access to specific doors or interlocks based on the member having completed an induction course on that specific piece of equipment.

Describe the solution you'd like

When creating or editing a interlock or door, I should be able to specify a Moodle or Canvas course and only allow that lock to be opened by members who have passed that course.

Describe alternatives you've considered N/A

Additional context Add any other context or screenshots about the feature request here.

peterdroberts commented 1 year ago

I think it would be great to just be able to have arbitrary badges you could link to anything.

So there could be badges for example:

board member 3d printing maintainer 3d printing trained level 1

And these can be linked to event invitations, tool access etc, etc.

I'd also like if you could set badge status with an api so you can easily scan a fob to mark someone inducted, for example.

Essentially just unix groups, but you could brand them 'virtual scout badges' or something

proffalken commented 1 year ago

OK, so how about this as a model:

Badges (or awards or accolades or featuredAuthenticationRequirements or whatever!)

Field Description Type Required?
Name The name of the badge such as 3D Printer trained or I can use the bandsaw! VARCHAR Y
Description A brief description of the badge and how to achieve it LONGTEXT Y
Training Required Is training required to earn this badge? BOOLEAN (default: True) Y
Moodle Course ID The (optional) Moodle Course required to be completed INT N

UserBadges

Field Description Type Required?
BadgeID The ID of the badge FOREIGNKEY Y
UserID The ID of the User FOREIGNKEY Y
Awarded By This links to another user within the platform or a system user to allow for automated or manual awarding of badges FOREIGNKEY Y
Awarded On The date/time the badge was awarded DATETIME Y
Expires On Does the badge need to be renewed? If so, when? DATETIME N

The Moodle code could then run every few minutes and check if a course has been completed by a user, before automatically awarding the badge if appropriate.

We then update https://github.com/membermatters/MemberMatters/blob/dev/memberportal/access/models.py#L48 with the following fields:

Field Description Type Required?
RequiredBadgeID The ID of the badge required to "unlock" the door/interlock FOREIGNKEY N

and then when an ID card is swiped we do a lookup against the UserBadges table to see if that badge has been awarded to the user and act appropriately, perhaps with a message to their phone/email saying "You tried to access 'x', but you've not earned that badge yet, click here to learn more" or similar?

As far as "board members" are concerned, I've just set up a "hidden" membership tier for that in the platform that we can manually assign people to via the UI, because the board members have to pay just like everyone else, but I can see that having a "board member" badge that is manually awarded could be beneficial.

I see the two things as separate but connected, but happy to be persuaded otherwise.

peterdroberts commented 1 year ago

Generally my thought would be to keep it as generic as possible, like unix groups. People can be in any number of groups and groups can have different permissions.

Might even be worth it being actually unix groups with each member being a unix user on a shared server and the members' system just an interface around that so the same system can be used for git access etc.

My training example would be a really useful place to have this; but I think it would be bad architecture to have a field for moodle in the schema, rather than another table with linking to group id.

peterdroberts commented 1 year ago

so in my thinking you'd have

chris18890 commented 1 year ago

Might even be worth it being actually unix groups with each member being a unix user on a shared server and the members' system just an interface around that so the same system can be used for git access etc.

I like this idea, plus in true hackerspace over-engineering fashion, have it tie into radius for Wifi access etc...

proffalken commented 1 year ago

have it tie into radius for Wifi access etc...

I'm looking into doing exactly this using the existing OAuth2 solution within MemberMatters (MM).

Might even be worth it being actually unix groups with each member being a unix user on a shared server and the members' system just an interface around that so the same system can be used for git access etc.

At this point, we're talking less about a membership platform and more about a membership operating system that is tied specifically to *nix (and quite possibly a specific distribution depending on where we want to start group ID's etc). I'd be against this for all kinds of reasons, not least because it feels like making a relatively simple problem massively complex having tried to write a shim that used user attributes in Hubspot to allocate individuals to LDAP groups on a separate system!

Unix groups do not play well with other platforms that are not hosted on the same server, and frequently don't even play well with shared file systems within containers or from other local servers because there is no mapping between systems of what the ID should be. They serve their purpose well for local authentication, but anything more than that needs to be handed to something like OAuth, LDAP, or SAML for centralisation.

The OAuth2 model already in place inside MM means that MM can become the "source of truth" for membership authentication both inside and outside the space (assuming MM is accessible from the WWW).

Users can be set as "active" or "inactive" based on their current billing cycle, and the OAuth model will deal with it accordingly based on the account status, so extending this somehow within MM itself makes far more sense to me than trying to shoehorn Unix-style groups into somewhere they shouldn't be (IMO) in the first place.

My training example would be a really useful place to have this; but I think it would be bad architecture to have a field for moodle in the schema, rather than another table with linking to group id.

That's why I've made that field optional - it doesn't need to be filled in, but if it is then MM can check Moodle to see if the user has completed the necessary course.

As a "first pass", I think this is fine. The next iteration would be something like you mention with an intermediate table of "badge requirements" along these kinds of lines:

Field Description Type Required
BadgeID The ID of the badge relating to this requirement FOREIGNKEY Y
RequirementName The name of the requirement such as pass 3D printing 101 or similar VARCHAR Y
courseID OPTIONAL: The ID Of the course in the virtual learning environment (removing the dependency on moodle) VARCHAR N

The Virtual Learning Environment (VLE) could then be set either via the CONSTANCE config options or we could go even further and add a table for VLE's, but we're deep into finding a "perfect" solution when actually a "good" one would suffice for now.

The proposal I made above would give you the ability to award badges (manually or via Moodle, which at present is the main supported VLE), to authorise users based on those badges, and to see which users have a specific badge.

This feels to me like a "first step" that solves the bulk of both my original approach and most of what you're looking for @peterdroberts - I'd welcome clarification if I'm incorrect here! :)

peterdroberts commented 12 months ago

This seems like a great addition and the removal of the explicit moodle link is an improvement.

Tangent on unix stuff

I'm coming up with ideas from the point of view of an embedded devices engineer thinking how easiest to integrate devices with the members system. The easiest thing would be to be able to run daemons on the server with the same user information available as the members' system. I think your consideration of how hackspace systems interact with multiple services in different geographical locations may not be the most important because is the best solution for hackers multiple servers and services in various locations or one server with a backup solution that members can interface homebrew stuff with whilst learning network protocols? The hackspace is a physical location that in almost all cases contains a server. Almost all the people wanting to access the information on the server live in the same city. The intention of a members' system is to improve the experience of a single physical geographical location so it makes sense members' systems would be self-hosted on the same thing or something on the same network as the thing that controls device access. Using *nix stuff to manage users means we can use anything anyone has ever done to interface with a *nix server to interact with hackspace stuff like LED signs, robots etc. etc. and access equipment can be a networked device with a driver. This also opens a ton of opportunities for the good type of hacking because server access can be managed with standard sysadmin tools and people can be given permission to access various networked hackspace devices in a standard way. I accept this is a totally different approach and so the bit not hidden in this drawer is the only thing relevant to the issue. The other thing is I don't know what I'd want to build to interact with the information on whether or not someone's done what induction etc, so I just want to suggest stuff that lets you have as many protocols usable as possible.

chris18890 commented 12 months ago

This seems like a great addition and the removal of the explicit moodle link is an improvement. Tangent on unix stuff

Reply on tangents

> The hackspace is a physical location that in almost all cases contains a server. Almost all the people wanting to access the information on the server live in the same city. The intention of a members' system is to improve the experience of a single physical geographical location so it makes sense members' systems would be self-hosted on the same thing or something on the same network as the thing that controls device access. Using *nix stuff to manage users means we can use anything anyone has ever done to interface with a *nix server to interact with hackspace stuff like LED signs, robots etc. etc. and access equipment can be a networked device with a driver. This also opens a ton of opportunities for the good type of hacking because server access can be managed with standard sysadmin tools and people can be given permission to access various networked hackspace devices in a standard way. I'd say not always, eg to use the HacMan/SoMakeIt examples, the server hosting the member management system is hosted offsite/in the cloud, with onsite things like doors etc driven by a RPi or similar

proffalken commented 12 months ago

Thanks both, FWIW I agree on the tangents and understand where you're coming from, however I also want to limit scope and if I can hand-off to other systems as a result then I'm absolutely going to do that.

Work is very busy at the moment, but if I get a chance I'll try and pull together a PR for this in the coming weeks.

jabelone commented 11 months ago

The intention of a members' system is to improve the experience of a single physical geographical location so it makes sense members' systems would be self-hosted on the same thing or something on the same network as the thing that controls device access.

@peterdroberts yes, but our instance sits on a cloud VPS in a different city so that is not a safe assumption ;). The reason all the AccessControlledDevices use web sockets is so that you can put your MM instance anywhere (local or cloud). Members need to access it externally and it relies on a good uptime to receive Stripe web hooks so a managed (linux) cloud server is probably best for most. All of our access control devices are ESP32 based which connect to our instance via web sockets over the internet.

jabelone commented 11 months ago

Sorry I've been a bit MIA, very much still actively working on this project, just been a bit sporadic with work becoming crazy before the holiday period and me taking a much needed holiday! I'll leave a few comments below :)

Whilst contributions are always welcome, someone needs to be become responsible for maintaining anything that is added and that someone usually ends up being me. In the context of unix groups - that is out of scope and likely won't ever happen. MM can be an Oauth provider which should handle most authentication use cases and I'm hesitant to add features that don't improve the core scope.

jabelone commented 11 months ago

I like the idea of generic badges (but let's call them tags) in addition to a more generic "member notes for admins only" field. I think Tags should be predefined-by-an-admin strings that you could add/remove and assign to any user (and maybe AccessControlledDevices). This is a separate use case to automating access controls permissions.

when an ID card is swiped we do a lookup against the UserBadges table to see if that badge has been awarded to the user and act appropriately

Automating access to certain AccessControlledDevice entities is a bit more tricky than this. Most of our tools for example require an online and in person competency check to become "inducted". This also breaks the whole design of the swipe system which is all authorised tags are synced in realtime to each device so they always have a local cache and work offline.

To make this work well, I think it would be best to link each interlock to a moodle course, and have another flag that sets whether or not they get access to it if the course is completed. This lets us handle the "automatically give access" and "complete then get signed off to get access" use cases. Then we can setup a cron that checks each moodle course completions for each interlock and updates access appropriately (which will then trigger the syncing etc.)

proffalken commented 11 months ago

Hey @jabelone - first of all, glad you could get a break, and totally understand that if we contribute, it should be maintainable by anyone not just you or us.

To make this work well, I think it would be best to link each interlock to a moodle course, and have another flag that sets whether or not they get access to it if the course is completed. This lets us handle the "automatically give access" and "complete then get signed off to get access" use cases. Then we can setup a cron that checks each moodle course completions for each interlock and updates access appropriately (which will then trigger the syncing etc.)

This sounds like a very sensible approach and was the kind of thing my ramblings were attempting to explain.

I'd not thought about the fact this could break the swipe cache, so yes, let's ensure that doesn't happen.

There's a few things going on at the moment at work and in the space, but once I have time (and have built the test interlock!) I'll start working on this.