This project implements OneRoster v1.1 behaviors.
Specifically, it targets requirements for:
- OneRoster v1.1 Rostering REST Provider
- OneRoster v1.1 Gradebook REST Provider
Table of contents
- Sample Goals
- Prerequisites
- Build and debug locally
- Deploy the sample to Azure
- Sync with SDS
- Understand the code
- Supported OneRoster Endpoints
- Unsupported Features
- Questions and comments
- Contributing
The sample demonstrates:
- Data models supporting OneRoster entities
- Action controllers supporting OneRoster service endpoints
- Authorization middleware supporting OneRoster Core Security
- Generation of OneRoster CSV bundles
- Consumption of SDS Profile Management APIs to create and start SDS sync profiles
OneRosterProviderDemo is based on ASP.NET Core Web.
Deploying and running this sample requires:
- An Azure subscription with permissions to register a new application, and deploy the web app.
- An O365 Education tenant with Microsoft School Data Sync enabled
- One of the following browsers: Edge, Internet Explorer 9, Safari 5.0.6, Firefox 5, Chrome 13, or a later version of one of these browsers.
- A tool to generate OAuth1 signatures, such as Postman
- Visual Studio 2017 (any edition), Visual Studio 2017 Community is available for free.
- Familiarity with C#, .NET Web applications and web services.
-
Sign into the new azure portal: https://portal.azure.com/.
-
Click Azure Active Directory -> App registrations -> +Add.
-
Input a Name, and select Web app / API as Application Type.
Input Sign-on URL: https://localhost:44344/
Click Create.
-
Once completed, the app will show in the list.
-
Click it to view its details.
-
Click All settings, if the setting window did not show.
-
Click Properties, then set Multi-tenanted to Yes.
Copy aside Application ID, then Click Save.
-
Click Required permissions. Add the following permissions:
API Delegated Permissions Microsoft Graph Read directory data
Read and write directory data
Access directory as the signed in user
Read education app settings
Manage education app settingsWindows Azure Active Directory Sign in and read user profile -
Click Keys, then add a new key:
Click Save, then copy aside the VALUE of the key.
Close the Settings window.
-
This project can be opened with the edition of Visual Studio 2017 you already have, or download and install the Community edition to run, build and/or develop this application locally.
Debug OneRosterProviderDemo:
-
Configure appsettings.json.
- AzureAd:Authority: "https://login.microsoftonline.com/{your-ad-tenant}.onmicrosoft.com/"
- AzureAd:ClientId: use the Client Id of the app registration you created earlier.
- AzureAd:ClientSecret: use the Key value of the app registration you created earlier.
- AzureDomain: use your AD tenant domain, which should agree with the AzureAd:Authority entry
-
In the Package Manager Console, run the command
EntityFrameworkCore\Update-Database
to generate the initial database. If this causes an error, try running the commandImport-Package Microsoft.EntityFrameworkCore
. -
Set OneRosterProviderDemo as StartUp project, and press F5.
-
Visit
/seeds
to populate your database with sample entities. Make sure to access the endpoint using HTTPS.
- Create a Publish Profile
- Select Build > Publish OneRosterProviderDemo
- Click Create new profile
- Select Microsoft Azure App Service and Create New
- Click Publish
- Sign into your Azure account
- Select a Resource Group for the deploy, or create a new one by clicking New
- Select an App Service Plan for the deploy, or create a new one by clicking New
- Click Create
- Select Build > Publish OneRosterProviderDemo
- Click Publish
Add REPLY URL to the app registration
-
After the deployment, open the resource group in Azure Portal
-
Click the web app.
Copy the URL aside and change the schema to https, and add /signin-oidc to the end. This is the reply URL and will be used in next step.
-
Navigate to the app registration in the new azure portal, then open the setting windows.
Add the reply URL:
Note: to debug the sample locally, make sure that https://localhost:44344/signin-oidc is in the reply URLs.
-
Click SAVE.
There are two synchronization options, via the OneRoster REST endpoints, and by uploading SDS compliant CSV files.
- Visit your deploy at /sds
- Sign in with an admin account on your education tenant.
- Verify that the "OneRoster endpoint" value agrees with your deployed instance
- Note that this will not work unless your URL is publicly accessible
- Click the "Sync via OneRoster REST" button
- Visit your deploy at /sds
- Sign in with an admin account on your education tenant.
- Select your SDS CSV files
- Click the "Sync via SDS CSV" button
This web application is based on an ASP.NET Core Web project template.
The Middlewares/OAuth.cs
file defines a middleware that validates the OAuth1 or OAuth2 signature for each incoming request with a OneRoster route. This file also contains the hard-coded client ID and secret.
In order to make OAuth2 requests, first visit the /token endpoint using OAuth1 credentials to get the access token.
The Startup.cs
file configures the app to use this middleware.
The Startup.cs
file configures the app to use .NET Core's OpenIDConnect (oidc) library. This flow is handled by AccountController
.
Most of the OneRoster models have a corresponding model class in the Models
directory. Due to language naming conventions, some of these have been renamed. The mapping of models is shown in the table below.
OneRoster Model | EFCore Model |
---|---|
Base | BaseModel |
Academic Session | AcademicSession |
Class | IMSClass |
Course | Course |
Demographic Data | Demographic |
Enrollment | Enrollment |
Line Item | LineItem |
Line Item Category | LineItemCategory |
Org | Org |
Resource | Resource |
Result | Result |
User, Student, Teacher | User |
Additional models were created to support various needs:
EFCore Model | Purpose |
---|---|
IMSClassAcademicSession | Join table for Class, Term (Academic Session) |
UserAgent | Join table for User, User |
UserOrg | Join table for User, Org |
UserId | Shape of user ids |
SeedData | Holds seed data for initial database population |
OauthNonce | Stores nonce values to disallow reuse |
Validation requirements are defined in the OneRoster model specification.
In addition to the validation attributes supplied by .NET and ASP Core, there are four custom validators for properties with specific requirements in the Validators
folder.
Vocabularies used by OneRoster, including enumerations, are provided by the Vocabulary/Vocabulary
class.
The subject code vocabulary "SCEDv4.0" is published by NCES; a conversion of the published spreadsheet is present in the Vocabulary/sced-v4.csv
file, which is parsed at startup time.
All response json objects are written using the NewtonSoft JSON nuget package, for the purposes of matching the prescribed json bindings.
All entities that can be serialized into a service endpoint response support two serialization methods, AsJson
and AsJsonReference
.
AsJson is used when the entity is the primary entity, or a member of the primary collection, that was requested.
AsJsonReference is used to express the entity as a GUIDRef.
The Controllers/BaseController
class hooks in paging, filtering, and sorting.
Filtering and sorting are implemented in the model classes via reflection in order to support the requirement that any data field be usable for filtering or sorting.
An SQLite database is assumed and configured in Startup.cs
.
You can create a new, empty database by running the EntityFrameworkCore\Update-Database
command in the Package Manager console, and seed it with sample data by visiting the /seeds
endpoint.
There are three OneRoster v1.1 service subsets, defined here. Of these, the Rostering (read-only) and Gradebook (read-and-write) subsets are implemented by this sample.
Service Call | Endpoint | HTTP Verb |
---|---|---|
getAllAcademicSessions | /academicSessions | GET |
getAcademicSession | /academicSessions/{id} | GET |
getAllClasses | /classes | GET |
getClass | /classes/{id} | GET |
getStudentsForClass | /classes/{class_id}/students | GET |
getTeachersForClass | /classes/{class_id}/teachers | GET |
getAllCourses | /courses | GET |
getCourse | /courses/{id} | GET |
getClassesForCourse | /courses/{course_id}/classes | GET |
getAllDemographics | /demographics | GET |
getDemographics | /demographics/{id} | GET |
getAllEnrollments | /enrollments | GET |
getEnrollment | /enrollments/{id} | GET |
getAllGradingPeriods | /gradingPeriods | GET |
getGradingPeriod | /gradingPeriods/{id} | GET |
getAllOrgs | /orgs | GET |
getOrg | /orgs/{id} | GET |
getAllSchools | /schools | GET |
getSchool | /schools/{id} | GET |
getCoursesForSchool | /schools/{id}/courses | GET |
getClassesForSchool | /schools/{school_id}/classes | GET |
getEnrollmentsForSchool | /schools/{school_id}/enrollments | GET |
getEnrollmentsForClassInSchool | /schools/{school_id}/classes/class_id}/enrollments | GET |
getStudentsForClassInSchool | /schools/{school_id}/classes/{class_id}/students | GET |
getStudentsForSchool | /schools/{school_id}/students | GET |
getTeachersForClassInSchool | /schools/{school_id}/classes/{class_id}/teachers | GET |
getTeachersForSchool | /schools/{school_id}/teachers | GET |
getTermsForSchool | /schools/{school_id}/terms | GET |
getAllStudents | /students | GET |
getStudent | /students/{id} | GET |
getClassesForStudent | /students/{id}/classes | GET |
getAllTeachers | /teachers | GET |
getTeacher | /teachers/{id} | GET |
getClassesForTeacher | /teachers/{id}/classes | GET |
getAllTerms | /terms | GET |
getTerm | /terms/{id} | GET |
getClassesForTerm | /terms/{id}/classes | GET |
getGradingPeriodsForTerm | /terms/{id}/gradingPeriods | GET |
getAllUsers | /users | GET |
getUser | /users/{id} | GET |
getClassesForUser | /users/{id}/classes | GET |
Service Call | Endpoint | HTTP Verb |
---|---|---|
getAllCategories | /categories | GET |
getCategory | /categories/{id} | GET |
getAllLineItems | /lineItems | GET |
getLineItem | /lineItems/{id} | GET |
getAllResults | /results | GET |
getResult | /results/{id} | GET |
getResultsForClass | /classes/{class_id}/results | GET |
getLineItemsForClass | /classes/{class_id}/lineItems | GET |
getResultsForLineItemForClass | /classes/{class_id}/lineItems/{li_id}/results | GET |
getResultsForStudentForClass | /classes/{class_id}/students/{student_id}/results | GET |
Service Call | Endpoint | HTTP Verb |
---|---|---|
deleteCategory | /categories/{id} | DELETE |
putCategory | /categories/{id} | PUT |
deleteLineItem | /lineItems/{id} | DELETE |
putLineItem | /lineItems/{id} | PUT |
deleteResult | /results/{id} | DELETE |
putResult | /results/{id} | PUT |
Currently, the following behaviors are not up to spec:
- If you have any trouble running this sample, please log an issue.
- Questions about ASP Core development in general should be posted to Stack Overflow. Make sure that your questions or comments are tagged with [asp.net-core].
- Questions about OneRoster development in general should be posted to IMS Global.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
Copyright (c) 2018 Microsoft. All rights reserved.