In my Azure AD tenant, I have few users which I get using https://graph.microsoft.com/beta/users and I get Education users for a school using https://graph.microsoft.com/beta/education/schools/<id>/users or https://graph.microsoft.com/beta/education/users
What is the difference here and how do we differentiate them in the portal panel?
Can we make a general user an education user under a specific school?
I actually am not an admin on the portal.
At the AAD level (which is what you're surfacing in the Azure Portal), there isn't a difference between the objects.
What is different is how the User resource is rendered. When you query Users via /education/users, you get some additional metadata. For example, if the User is a Student you will get the educationStudent properties. If they're a Teacher, you'll see the educationTeacher properties.
The educationUser resources also have additional navigation properties for educationClass and educationScool. For example, you can call v1.0/education/users/{id}/classes but not v1.0/users/{id}/classes because the standard User resource lacks the classes navigational property.
This same model carries over to other areas:
educationUser extends the user resource
educationClass extends the group resource
educationSchool extends the administrativeUnit resource
You can add an educationUser to an educationSchool like this:
POST https://graph.microsoft.com/v1.0/education/schools/{schoolId}/users/$ref
Content-type: application/json
{
"#odata.id":"https://graph.microsoft.com/v1.0/education/users/{userId}"
}
As an aside, I strongly caution against using the /beta version. The Beta version can and will break from time to time. Breaking changes happen without warning and, as such, is simply not reliable enough to use in a production application.
Related
I've been using /d2l/api/lp/1.4/enrollments/myenrollments/ to get a list of enrollments for the current user. Now, I want to just get the enrollments where the user is in an instructor role. So, I'm trying to use:
/d2l/api/lp/1.4/enrollments/users/{userId}/orgUnits/?roleId=105
When I use that, I get an empty list of Items back, with or without the roleId specified.
My expectation is that just calling it without the roleId would return the same list as /d2l/api/lp/1.4/enrollments/myenrollments/. But, I always get an empty list, except when I log in as a system administrator. Only in that case do I get anything back.
Does anyone have any suggestions on what I might be doing wrong?
The various my* API calls specifically exist to provide end users to fetch back details about the system that they should know, but segregated from information they shouldn't (that's available through the more general routes for a particular area). Enrollments is a good example of this. And end-user should be able to see their own enrollments, but they should not have generalized access to enrollment records. In particular, the D2L system treats the D2L user role belonging to an enrollment as fairly privileged information, and a side effect of this is that it's not generally visible to end users.
One way that applications and services can cope with achieving goals that the end-user cannot themselves perform is to have set up a "service account" that the app can use to make calls of an administrative nature, to fetch back data that they can use in the business logic around presenting information to end users. In this particular case, you could, for example use the service account to make calls about a user's enrollments, and then present the user with logic that could filter the list of their enrollments by "these are the student ones, and these are the ones where you're a teacher, and a tutor, and so forth".
But you'd also need to carefully consider the implications of this type of activity in balance against the intentions of the client LMS's policies and administration. Even this level of information may be giving away too much to end users, in the eyes of a client LMS administrator.
Using a service account to let an app make administrative level calls must always be done with great care around the issue of information/functional leakage to end users.
I'm working on migrating from SOAP to valence API. I'm having some problems with a few calls like getting users (All users, based on role, by userID, etc) and grades (All variations).
I see an empty list returned with no items. There's no error of any kind. When I try to get the data using SOAP, I get the desired data.
I've checked the permissions for "Search for Students", so technically, I should be getting something when I try to get all users or by role id of students. What else am I missing here...
The D2LWS service's authentication mechanism puts the API caller in the position of being a privileged caller. The Valence Learning Framework APIs use a different auth model: the user ID/Key tokens that identify a user get employed by the back-end to restrict the functionality of calls. That is: the authenticated user should have access to the same functionality and data as the user would get through the web UI, and no more.
In this particular case, the calls succeed: they send back all the elements in the result set that your calling user has privileges to see -- none of them.
This is almost certainly an issue with the role privileges afforded to your calling user, and debugging the permissions around calls can be challenging. The Valence project's documentation provides a walkthrough topic on investigating role permissions that might shed let on a possible approach here, especially with respect to the calls to gain access to user records (or properties that appear in user records).
As the walkthrough discusses, there are various aspects to making the general call to /d2l/api/lp/{version}/users/ that bring permissions into play:
If you're trying to filter with a query parameter, does the calling user context have permissions to use the data on which you want to filter
Does the calling user context have permission to see properties affected by User Information Privacy settings
Does the calling user have permission to search for all the user roles they need to, in order find users in the result set
The users call operates on the root organization unit, so the permissions the calling user requires must be set on the organization org unit type.
By contrast, the Grades-related API calls operate not on the root organization unit, but typically on course offerings, sections, or groups. The permissions surrounding the calls there will get checked in the associated org unit types, so the calling user will need the right permissions against those types. Additionally, many of the calls related to course offerings (also sections and groups) require that the calling user be enrolled in the org unit in question (and in some cases, explicitly enrolled, not merely enrolled by cascading enrollment).
If you're sure that your calling user context does give you access to these things (and allows you access to this data through the web UI), and you still see a mismatch like this when you're calling through the API, then you may have uncovered a defect of some kind and you should please ask your organization's support contact, or your account manager, to open a support ticket to report that through Desire2Learn's support desk.
I'm contemplating a multi tenant application - shared database, shared schema. A tenant identifier (tenant key) associates every row with the right tenant.
What i'm not sure about is how to go about loading the tenant_id into some sort of global scope. How should this happen? I assume that I would parse the domain and then look up the tenant_id based on the domain.
My questions:
Where would the lookup happen in a rails application? In an initializer? Is there a better point to do this?
Once I have determined the tenant_id, what is the best way to persist it - a simple session_id?
I use a before filter for the controllers for this function.
You can also sub-class the controller class to DRY out duplicate code in the controllers.
Be careful to remember that access to a given tenant's information needs to be authenticated on a per-user basis. You need to decide if a given user will have access to more than one tenant. Eg should user "joe" have access to tenants 1 and 2? Or should Joe need a per-tenant login?
The login's authorization should control access to the tenant info. Don't rely on the domain name to grant authorization.
Re: where to persist the tenant_id? Store in the session. If access to the session is expensive (stored in DBMS), then make an in-memory copy as an instance variable during the controller startup. Google for how user_ids are often stored.
You also should determine the user experience for if/when a user wants to access a different tenant.
Added To see which welcome screen to load before the user has logged in, looking at the subdomain name is a good choice. To see which subdomain the incoming request used, parse request.fullpath() Docs. Do this in a controller filter.
Since the authorization comes from the user_id, remember to test the case where joe logs in at tenant1.app.com but only has access to tenant2.app.com
Bonus answer Looking for a templating system that will enable your customers to have full control over their tenancy's user interface? Check out Liquid templates. I was very successful in using them to enable my customers to have full control over their look and feel in a safe way.
Re additional questions in comment
See superuser for configuring the web server. The config is web server-specific.
If you want the welcome screen to not be generic, then you must know from the request url how to customize it. Tenant-specific subdomain is the nicest. If no subdomain, then show the generic welcome--when the person logs in you can determine the tenant and how to customize.
Re helper--if you mean a view helper, then I would not recommend it as the primary place where the tenant is determined. Make the #user and #tenant be light-weight models that you look up once and then retrieve from the session during additional requests for the same session. The models will be used by the controllers and, perhaps, passed to the models. The View layer will also be able to see them and use them as necessary.
If the UI may look/will look completely different for the different tenants, then add a "tenant-display" layer in addition to the view. Eg have the view gather the instance variables, find the right Liquid template, then express the view via the template.
You don't want the view to be computing "if tenant_a then x else y"
I am creating a Grails website where users will have access to the resources they create. Till here everything is clear to me. I define ROLE_USER and lock down my controllers and actions using the Config.groovy file.
The issue I am facing is that I have requirement to support group of users such that some resources created by a user can be edited/updated/deleted by other users of the same group. How do I associate a user with a "group" in spring security, what is the design/library I should use here?
What you will need to do is to have your users' roles (the authorizations) come from the database. Once that is the case, you can then easily adjust the roles a user (or set of users) has and create/remove them on the fly. The docs have some pretty good info on how to get the roles to come from the database, so I won't go any more into that here.
Once the dynamic roles are in place, however, you still need to be able to connect roles to the objects that are created. There are essentially two ways you can go about doing this:
Access Control Lists
Custom logic
Depending on the granularity you need and the flexibility you want, one option may be more appealing than another.
Access Control Lists essentially allow you to have a permission mapping between each user and each entity instance. As you can imagine, it's a fair bit of overhead and can perform poorly if you have a large number of entities and users.
Putting together your own logic, on the other hand, is much more flexible because you can set up your own scheme to connect entity instances or entity classes to users and their roles.
I dont think that spring-security provides such functionality out of the box so you will have to do that manually.
For each domain class that you this kind of functionality, store the user name of current logged in user
def authenticateService
def user = authenticateService.principal()
entity.setUser(user?.getUsername())
Then in the update/delete method of the contoller you should check if the role of the current logged in user matches
the role of the user that created the entity. If you have a match you should proceed with the update/delete otherwise throw an exception
/redirect the user to an error page
As role you can use the spring security roles or you can create a property on the user object you have created
Please consider the following setup:
Multi-tenant webapp.
Tenants create company accounts and company accounts have user accounts under them.
Users have roles, there's a special role "Owner" (the user who created the company account).
I'd like to have users to edit other user accounts (some admin tasks), but two conditions must be met:
Noone may edit owner's data (except for owner, when he's editing his own profile, and own profile editing is the same user editing).
Users may access user data and edit users only within their company account.
The app uses MVC architecture. Currently I check for those two conditions in the web layer and it works for me, but I have some concerns. If I go with some sort of API or some other type of data consumer, I may "forget" to re-inforce these conditions. Also, there will be other objects in the app with similar functionality requirements and which will have similar restrictions on them, so it's better for me to come up with some sort of pattern which will enforce my restrictions on data access level.
Could anyone recommend some approach worth looking into?
Thanks!
I beleive aspects or interceptors should be able to help you. If you work with objects you should be able to intercept requests containing your business data and check wether your user is allowed to work on it. The interceptor could then stop or proceed the execution.