Getting List of All Instructor Enrollments - desire2learn

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.

Related

How do I implement restricted access to subsets in my database

I can restrict entire controller action access with [Authorize(Roles=...)] however, what about if I want to allow a user to for example look at the data of the other users in their own department (only).
Specifically what I mean is when I give a user the details of another user, the URL will be like this:
myapp.com/user/details/45
And they could quite easily just edit the 45 to be whatever they want. I want to restrict the access if the user the requested is not part of their own company (their company is currently in their session but that can be changed)
I can see a few different ways to do this, but I am just wondering what everyone's preference is based on experience. Here are some options:
1.Change the way access is done by editing the routing. myapp.com/Company/4/User/4' where 4 is a psudoID which references an actual ID internally.
2.myapp.com/OurCompany/User/4' < same but with a controller for ourcompany instead of generically companies.
3. Just check it internally by checking the requested user is in the same company: `myapp.com/User/42345'. My issue is that this has to be customized for each kind of request (users, products, related companies). But this is what I'm looking at right now.
EntityFramework actually has the power to reference the company members of the current user because when they log in I get their company entry and store it in the session. This allows easy access to the members of the company like so:
SessionHandler.UserSession.CompanyTable.UserTable.Where(e=> e.UserID == id).FirstOrDefault();
This seems odd to those who don't know about EF, but basically the subtable UserTable is a subset of all users referenced by the Foreign Key of the CompanyTable, which does indeed return the correct set of entities. The issue with this is that updating the entry in this way actually updates the entry in the SESSION but not inside the DATABASE, which is really bazaar to watch because it means the logged in users can seemingly make changes to the database but infact they are just being saved into the session. (staring at the database whist making these changes confirmed it, logging out and logging in nothing was saved, but db.SaveChanges() was correctly being called, just there were no changes to be made)
First, for the love of everything good and holy, stop putting that in the session. The session shouldn't be used for this, regardless, and it's code smell of the worst kind.
What you're talking about is object-level permissions. The correct way to do that is to limit your queries with some identifying factor of the currently logged in user. Your entities need a concept of "ownership" for this work. In other words, they need a foreign key to some other entity that "owns" that particular instance. Since here you want to restrict by department, that means all your users need to be owned by departments. Then, for situations like this you can do something like:
db.Users.Where(m => m.DepartmentId == user.DepartmentId)
Where user here is the instance of the currently logged in user. By querying in this way, the subset of entities available to the user is restricted by the user, itself. Which means attempting to access a URL with id outside of that set will naturally result in a 404. A 404 is actually the best thing for this, because even though it's authorization based, returning a 403 lets you know something is there - you just can't access it. For a hacker, this kind of information is half the battle. A 404 being returned gives you zero information. It might not exist or it might just not be available to you.

MVC Web Api Differentiate between user and public data in RESTful approach

When it comes down to good RESTfull setup, what is the best practice for providing results that pertain to the owner as the requestor and results that pertain to a user wanting data owned by another user.
I have read that a resource should have max 2 base URLs so how to handle say,
Get all items for authenticated user
Get a single item for Authenticated user
Get all items for a particular user
Get a single item for a particular user
Although your question is a bit unclear, it seems to me you might mix up "Resources" as in HTTP resources, and Model objects or database rows.
The two do not necessarily have a 1-to-1 relationship, or even 1-to-2 relationship as you seem to imply. You can expose a database row in multiple "forms" as resources, there is no limitation how many times you can aggregate, transform or publish the same information, as long as those are all semantically different things.
So, back to your problem. You can publish resources pertaining to the authenticated user, and just users independently which might also contain the current user. With an URI structure for example like this:
/currentuser
/user/1
/user/2 <- might be the same as /currentuser
/user/3
...
There also could be a list of users recently logged in:
/recentuser/444
/recentuser/445 <- might be again /currentuser
...
That would be a third reference on the same user, but it is ok, because all of those have a different meaning, might even have different representations to offer (one might offer more information than others).

What are the permissions required in desire2learn (D2L) Valence PUT call for .../courses?

I continue to get a "HTTP/1.1 403 Forbidden" response from a PUT request to /d2l/api/lp/1.2/courses/7917 . This may be a permission problem with the user/role that I'm using, but I can't figure out what specific permissions may be required. Can anyone point me to a list or matrix of valence routes and required permissions? Or, answer for this specific one?
The same appid/userid/username works for the GETs associated with the same path.
confused...
cwt
The permissions associated with API calls should mirror the permissions you'd have to have if you were to perform the relevant function through the Learning Envrionment's web UI. You can think about this problem in two ways:
Frame the question in terms of a user role: identify the class of users you'd reserve this ability for in your existing configuration, and ensure that a user of that role can make the call through the API as you'd expect.
Frame the question in terms of an abstract single user: start with a role that has no privileges and add permissions until you arrive at only the ones required for the API call. This is not a trivial exercise, and the first way is far more useful in the long run.
In this particular case, because the API requires you provide a complete course offering set of properties when you want to update it, you have to have permission to alter all the properties in the set (under the Manage Courses tool). You also need to be able to see the course info in the first place, so you need to have Course Management Console > See Course Info as well.
You're probably safest to look at the permissions array in the Manage Courses and Course Management Console tools for the user roles that would do this thing in the web UI and make sure that the users employing your app also have a similar permissions array specified in those tools.

No result returned using Valence API desire2learn

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.

How can I Retrieve a UserRole with Valence query

Is there some way that I can retrieve the roleId of the current user context regardless of that user's role permissions within the LMS?
For example, I would like to programatically determine if a user is a 'student', 'teacher', etc. I know this can be done if the authenticated user has access to user roles but obviously a 'student' role would not possess such credentials.
Any insight into this matter would be greatly appreciated!
With many organizations the assigned role of a user's enrolment is considered sensitive information by the organization (for example, imagine the case where the organization wants to set up multiple roles for particular pay grades of instructors), and does not want the roles to be visible outside a small group of administrative users. Accordingly, there may not be a reliable way for a users to determine their own roleIds assigned to them within an organization unit (course, department, faculty, and so forth), let alone the roles assigned to other users.
Our typical recommendation is that client applications focus first on attempting actions that a user should be able to accomplish based on their access in the webUI: in some cases, this could involve a user characterizing their own general role in a context ("Are you a student in this course? An instructor? A Teaching Assistant?") or it could involve simply attempting actions and gracefully taking action based on results ("I'm sorry, you don't have permission to do/see that").
The various API calls that return an Enrollment.OrgUnitUser structure will contain role information for enrollments; notably the MyEnrollments API call does not return such a structure (it's intention was to be a "safe" call that any user could make to fetch back the list of their own enrollments with potentially privileged information redacted).

Resources