1- using System ;
1+ using System . Linq ;
22using System . Collections . Generic ;
3- using System . Linq ;
43using System . Threading . Tasks ;
54using Microsoft . AspNetCore . Http ;
65using Microsoft . AspNetCore . Mvc ;
76using Microsoft . AspNetCore . Authorization ;
87using Microsoft . EntityFrameworkCore ;
9- using System . Security . Claims ;
8+ using Microsoft . Identity . Web ;
109using Microsoft . Identity . Web . Resource ;
1110using TodoListAPI . Models ;
1211using TodoListAPI . Utils ;
@@ -18,12 +17,11 @@ namespace TodoListAPI.Controllers
1817 [ ApiController ]
1918 public class TodoListController : ControllerBase
2019 {
21- // The Web API will only accept tokens 1) for users, and
22- // 2) having the access_as_user scope for this API
23- static readonly string [ ] scopeRequiredByApi = new string [ ] { "access_as_user" } ;
24-
2520 private readonly TodoContext _context ;
2621
22+ private const string _todoListRead = "TodoList.Read" ;
23+ private const string _todoListReadWrite = "TodoList.ReadWrite" ;
24+
2725 public TodoListController ( TodoContext context )
2826 {
2927 _context = context ;
@@ -33,117 +31,142 @@ public TodoListController(TodoContext context)
3331 [ HttpGet ]
3432 [ Route ( "getAll" ) ]
3533 [ Authorize ( Policy = AuthorizationPolicies . AssignmentToTaskAdminRoleRequired ) ]
34+ [ RequiredScopeOrAppPermission (
35+ AcceptedScope = new string [ ] { _todoListRead }
36+ ) ]
3637 public async Task < ActionResult < IEnumerable < TodoItem > > > GetAll ( )
3738 {
38- HttpContext . VerifyUserHasAnyAcceptedScope ( scopeRequiredByApi ) ;
3939 return await _context . TodoItems . ToListAsync ( ) ;
4040 }
4141
42- // GET: api/todolist
42+ // GET: api/TodoItems
4343 [ HttpGet ]
4444 [ Authorize ( Policy = AuthorizationPolicies . AssignmentToTaskUserRoleRequired ) ]
45+ /// <summary>
46+ /// Access tokens that have neither the 'scp' (for delegated permissions) nor
47+ /// 'roles' (for application permissions) claim are not to be honored.
48+ ///
49+ /// An access token issued by Azure AD will have at least one of the two claims. Access tokens
50+ /// issued to a user will have the 'scp' claim. Access tokens issued to an application will have
51+ /// the roles claim. Access tokens that contain both claims are issued only to users, where the scp
52+ /// claim designates the delegated permissions, while the roles claim designates the user's role.
53+ ///
54+ /// To determine whether an access token was issued to a user (i.e delegated) or an application
55+ /// more easily, we recommend enabling the optional claim 'idtyp'. For more information, see:
56+ /// https://docs.microsoft.com/azure/active-directory/develop/access-tokens#user-and-application-tokens
57+ /// </summary>
58+ [ RequiredScopeOrAppPermission (
59+ AcceptedScope = new string [ ] { _todoListRead , _todoListReadWrite }
60+ ) ]
4561 public async Task < ActionResult < IEnumerable < TodoItem > > > GetTodoItems ( )
4662 {
47- HttpContext . VerifyUserHasAnyAcceptedScope ( scopeRequiredByApi ) ;
48- string owner = User . FindFirst ( "preferred_username" ) ? . Value ;
49- return await _context . TodoItems . Where ( item => item . Owner == owner ) . ToListAsync ( ) ;
63+ /// <summary>
64+ /// The 'oid' (object id) is the only claim that should be used to uniquely identify
65+ /// a user in an Azure AD tenant. The token might have one or more of the following claim,
66+ /// that might seem like a unique identifier, but is not and should not be used as such:
67+ ///
68+ /// - upn (user principal name): might be unique amongst the active set of users in a tenant
69+ /// but tend to get reassigned to new employees as employees leave the organization and others
70+ /// take their place or might change to reflect a personal change like marriage.
71+ ///
72+ /// - email: might be unique amongst the active set of users in a tenant but tend to get reassigned
73+ /// to new employees as employees leave the organization and others take their place.
74+ /// </summary>
75+ return await _context . TodoItems . Where ( x => x . Owner == HttpContext . User . GetObjectId ( ) ) . ToListAsync ( ) ;
5076 }
5177
52- // GET: api/todolist /5
78+ // GET: api/TodoItems /5
5379 [ HttpGet ( "{id}" ) ]
5480 [ Authorize ( Policy = AuthorizationPolicies . AssignmentToTaskUserRoleRequired ) ]
81+ [ RequiredScopeOrAppPermission (
82+ AcceptedScope = new string [ ] { _todoListRead , _todoListReadWrite }
83+ ) ]
5584 public async Task < ActionResult < TodoItem > > GetTodoItem ( int id )
5685 {
57- HttpContext . VerifyUserHasAnyAcceptedScope ( scopeRequiredByApi ) ;
58-
59- var todoItem = await _context . TodoItems . FindAsync ( id ) ;
60-
61- if ( todoItem == null )
62- {
63- return NotFound ( ) ;
64- }
65-
66- return todoItem ;
86+ return await _context . TodoItems . FirstOrDefaultAsync ( t => t . Id == id && t . Owner == HttpContext . User . GetObjectId ( ) ) ;
6787 }
6888
69- // PUT: api/todolist /5
89+ // PUT: api/TodoItems /5
7090 // To protect from overposting attacks, please enable the specific properties you want to bind to, for
7191 // more details see https://aka.ms/RazorPagesCRUD.
7292 [ HttpPut ( "{id}" ) ]
7393 [ Authorize ( Policy = AuthorizationPolicies . AssignmentToTaskUserRoleRequired ) ]
94+ [ RequiredScopeOrAppPermission (
95+ AcceptedScope = new string [ ] { _todoListReadWrite }
96+ ) ]
7497 public async Task < IActionResult > PutTodoItem ( int id , TodoItem todoItem )
7598 {
76- HttpContext . VerifyUserHasAnyAcceptedScope ( scopeRequiredByApi ) ;
77-
78- if ( id != todoItem . Id )
99+ if ( id != todoItem . Id || ! _context . TodoItems . Any ( x => x . Id == id ) )
79100 {
80- return BadRequest ( ) ;
101+ return NotFound ( ) ;
81102 }
82103
83- _context . Entry ( todoItem ) . State = EntityState . Modified ;
84104
85- try
86- {
87- await _context . SaveChangesAsync ( ) ;
88- }
89- catch ( DbUpdateConcurrencyException )
105+ if ( _context . TodoItems . Any ( x => x . Id == id && x . Owner == HttpContext . User . GetObjectId ( ) ) )
90106 {
91- if ( ! TodoItemExists ( id ) )
107+ _context . Entry ( todoItem ) . State = EntityState . Modified ;
108+
109+ try
92110 {
93- return NotFound ( ) ;
111+ await _context . SaveChangesAsync ( ) ;
94112 }
95- else
113+ catch ( DbUpdateConcurrencyException )
96114 {
97- throw ;
115+ if ( ! _context . TodoItems . Any ( e => e . Id == id ) )
116+ {
117+ return NotFound ( ) ;
118+ }
119+ else
120+ {
121+ throw ;
122+ }
98123 }
99124 }
100125
101126 return NoContent ( ) ;
102127 }
103128
104- // POST: api/todolist
129+ // POST: api/TodoItems
105130 // To protect from overposting attacks, please enable the specific properties you want to bind to, for
106131 // more details see https://aka.ms/RazorPagesCRUD.
107132 [ HttpPost ]
108133 [ Authorize ( Policy = AuthorizationPolicies . AssignmentToTaskUserRoleRequired ) ]
134+ [ RequiredScopeOrAppPermission (
135+ AcceptedScope = new string [ ] { _todoListReadWrite }
136+ ) ]
109137 public async Task < ActionResult < TodoItem > > PostTodoItem ( TodoItem todoItem )
110138 {
111- HttpContext . VerifyUserHasAnyAcceptedScope ( scopeRequiredByApi ) ;
112-
113- string owner = User . FindFirst ( "preferred_username" ) ? . Value ;
114- todoItem . Owner = owner ;
115-
139+ todoItem . Owner = HttpContext . User . GetObjectId ( ) ;
116140 todoItem . Status = false ;
117141
118-
119142 _context . TodoItems . Add ( todoItem ) ;
120143 await _context . SaveChangesAsync ( ) ;
121144
122145 return CreatedAtAction ( "GetTodoItem" , new { id = todoItem . Id } , todoItem ) ;
123146 }
124147
125- // DELETE: api/todolist /5
148+ // DELETE: api/TodoItems /5
126149 [ HttpDelete ( "{id}" ) ]
127150 [ Authorize ( Policy = AuthorizationPolicies . AssignmentToTaskUserRoleRequired ) ]
151+ [ RequiredScopeOrAppPermission (
152+ AcceptedScope = new string [ ] { _todoListReadWrite }
153+ ) ]
128154 public async Task < ActionResult < TodoItem > > DeleteTodoItem ( int id )
129155 {
130- HttpContext . VerifyUserHasAnyAcceptedScope ( scopeRequiredByApi ) ;
156+ TodoItem todoItem = await _context . TodoItems . FindAsync ( id ) ;
131157
132- var todoItem = await _context . TodoItems . FindAsync ( id ) ;
133158 if ( todoItem == null )
134159 {
135160 return NotFound ( ) ;
136161 }
137162
138- _context . TodoItems . Remove ( todoItem ) ;
139- await _context . SaveChangesAsync ( ) ;
140-
141- return todoItem ;
142- }
163+ if ( _context . TodoItems . Any ( x => x . Id == id && x . Owner == HttpContext . User . GetObjectId ( ) ) )
164+ {
165+ _context . TodoItems . Remove ( todoItem ) ;
166+ await _context . SaveChangesAsync ( ) ;
167+ }
143168
144- private bool TodoItemExists ( int id )
145- {
146- return _context . TodoItems . Any ( e => e . Id == id ) ;
169+ return NoContent ( ) ;
147170 }
148171 }
149172}
0 commit comments