联系方式

  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-23:00
  • 微信:codinghelp

您当前位置:首页 >> Web作业Web作业

日期:2024-06-27 04:45

DISTRIBUTED SYSTEMS API

Distributed Systems ACW

2023/24

You have been hired as a distributed systems programmer after Gribbald, their previous expert disappeared one foggy night. You’ve been promised that your first job for them won’t be very hard and, thankfully, it’s really not. The task is to develop a client/server, using ASP.NET Core Web API, which provides a number of extra secret security and encryption services … sort of.

You’re lucky though. Gribbald was working on this project before you and has already created an outline solution with some‘TODO’regions, based on the original specification. They have also broken down the specification into tasks for you, which should make your life even easier!

On your first day you were handed this specification:

•     The server must be able to handle multiple client requests simultaneously.

•     The server must use Entity Framework, Code First, to create and manage a local database of Users (that will, in the future be moved over to a production database).

•     The client must be able to get a TalkBack/Hello message from the server. The server will respond with“Hello World”.

•     The client must be able to send a TalkBack/Sort message as a get request to the server with an array of integers as parameters. The server will sort the integers into ascending order and return the sorted array to the client, because – well, sorting data is tedious.

•     The client must be able to send a User/New get request to the server which has a username string as a parameter. The server must return a string identifying if the username already exists in the database.

•     The client must be able to send a User/New post request to the server which has a username string in the request body. The server must create a new user, generate a new GUID as an API Key, save the user to the database and return the API Key to the user. If this is the first user they should be saved as Admin role, otherwise just with User role.

•     The client must be able to send a User/RemoveUser delete request to the server with an API Key in the header and a string username in the URI. If the server receives this request, it must check to see if the API Key is in the database and, if it is, it must check that the username and API Key are the same user and if they are, it must delete this user from the database.

•     The client must be able to send a User/ChangeRole Post request to the server with an API Key in the header which links to an Admin role user, a string username in the body and a string role in the body. If the server receives this request, it must check to see if the API Key is in the database and whether the role is Admin, if it is, it must update the role for the given username to the role provided (User or Admin)

•     The client must be able to send a Protected/Hello get request to the server with an API Key in the header. If the server receives this request, it must check to see if the API Key is in the database and, if it is, it must get the username associated with the API Key and send back“Hello <username>”(e.g.“Hello UserOne”).

•     The client must be able to send a Protected/SHA1 get request to the server with an API Key in the header and a string message as a parameter. If the server receives this request, it must check to see if the API Key is in the database and, if it is, it must compute the SHA1 hash of the message and return it in hexadecimal form. to the client.

•     Somebody told the  boss that SHA256  is  more secure than SHA1 so the client  must  be able to send a Protected/SHA256 get request to the server with an API Key in the header and a string message as a parameter. If the server receives this request, it must check to see if the API Key is in the database and, if it is, it must compute the SHA256 hash of the message and return it in hexadecimal form. to the client.

•     The client must be able to send a Protected/GetPublicKey get request to the server with an API Key in the header. If the server receives this request, it must check to see if the API Key is in the database and, if it is, it must send back its RSA public key.

•     The client must be able to send a Protected/Sign request with an API Key in the header and a string message as a parameter. If the server receives this request, it must check to see if the API Key is in the database and, if it is, it must digitally sign the message with its private RSA key and send the signed message back to the client. The client must then be able to verify that the server signed the message by using the server’s public RSA key.

•     Finally, the client must be able to send a Protected/Mashify get request to the server with an API Key in the header which links to an Admin role user, and three parameters comprising:

o  A string of text, encrypted using the server’s public RSA key

o  A symmetric key (using AES encryption), encrypted using the server’s public RSA key

o  An IV (initialization vector) for the symmetric key, also encrypted using the server’s public RSA key

If the server receives this request, it must…

o  check to see if the API Key is in the database and whether the role is Admin,

o  if it is, it must decrypt all three parameters using its private RSA key.

o  It must then‘mashify’the string it was given (see task  14), encrypt the mashified string using the client’s symmetric (AES) key and IV and

o  finally, it must send the newly encrypted string back to the client.

The client must then be able to …

o  decrypt the string using its symmetric (AES) key and IV and

o  finally, output the new mashified string to the console.

Your task is to follow the next instructions carefully and develop a console-based client and Web API-Based server with a Code First Entity Framework Database.

   You must use the skeleton solution as your starting point, and

   You must ensure that you carefully follow instructions on  request types and names, parameter naming, responses and response types – if you do not conform. to these instructions, some of the marking tools will not be able to find your code and you will receive a mark of 0 in affected areas.

Please do not, under any circumstances, publish your work on a public source code repository  even after you leave the University. We have had issues in the past with students using public repositories (e.g. on GitHub) and their work being plagairised. In these circumstances it becomes very difficult to ascertain who was cheating and both parties may be penalised for collusion under the academic misconduct policy, which can have severe academic consequences. Furthermore, the skeleton code remains the intellectual property of the University of Hull and you are not permitted to share this publicly. You may, however, use any of the solution in your future work and share your solution (including the skeleton code) privately (e.g. a repository set to private) with prospective employers, etc., as you wish.

Instructions

Download the skeleton solution from Canvas and unzip it. You may delete or modify any of the code that has already been written if you want, but for the most part you will only need to add to it. Note how there are a number of regions and comments that identify tasks by number – use these as a guide to identify where you might want to add your code.

The Server

The server that you have been given in the skeleton solution is an ASP.NET Web API. You may find it useful to refer to the Microsoft documentation on Web API. You should also recognise it from labs and lecture material.

There are a few things you should look at before you start working on the server:

1.   Open TalkBackController.cs – Gribbald started writing this controller before they mysteriously disappeared. It contains two get request actions for /API/TalkBack/Hello and /API/Talkback/Sort. Task1 will be to complete these methods, but they should also be a guide to help you get started – you may change any of the code you feel needs to be changed.

2.   Open Auth->CustomAuthenticationHandler.cs – this is custom authentication middleware that is added inside the ConfigureServices method inside Startup.cs. This means that whenever an HTTP request is made, this method will run before the request gets to your controller. You will need to modify this method in TASK5 to verify  that  the  API   Key   in  the   header   is  correct  and  authenticate  the   user.  There   is   a  similar CustomAuthorizationHandler – middleware that runs immediately after authentication if the authentication succeeds. You will need to make changes to this code in TASK6. The use of these handlers is directly linked to the Authorize attribute.

3.   Open BaseController.cs – you should note four things:

a.   This is an abstract class. You cannot make an instance of this controller.

b.   It inherits from ControllerBase and has the attribute ApiController – all Controllers need to descend from ControllerBase and have the attribute ApiController.

c.    It stores a protected instance of the Database Context that you’ll need to use for your data access.

d.   The route mapping has been changed to api/{controller}/{action} which will allow you to call actions inside your controller (e.g. /API/TalkBack/Hello, where TalkBack is the controller and Hello is the action).

BaseController is an abstract base class you should inherit from to retain the above functionality in your controllers. If you open TalkbackController.cs you can see that it inherits from BaseController. To make your life easier use this as a template for any new controllers you create.

Before you start working on your server, you may find it useful to use a tool which allows you to craft requests (with control over the header/body/URI/etc.) send them to your server for debugging purposes and receive RESTful responses. This means you won’t need to have a working client to test your server out. A suitable (and free) tool is PostMan:https://www.getpostman.com/apps

Finally,  I strongly suggest you  use  the test server  (prototype). This server  responds  as  per this  coursework specification. If your server responds in the same way as the test then you are likely to get a good mark for the server elements of this coursework and if your client works properly with the test server then you can be confident it is working properly too. You may use Postman to analyse the responses from the test server before you start producing your server. There’s more info at the end of TASK10 but the test server can be accessed by pointing your client to:

http://150.237.94.9/<myUniqueCode>/

e.g. http://150.237.94.9/1234567/Api/TalkBack/Hello

Your unique 7 digit code should have been distributed to you via email already but if you have not received this then email me:john.dixon@hull.ac.uk

Tasks

The following tasks are designed to help you identify exactly what to do to meet the specification and they give a logical order for doing it too. You must ensure that your server and client respond exactly as specified to get the marks for that section, so read this specification very carefully. Most tasks also have a section dedicated to testing so that you can test your solution against expected results. If your server and client do not respond exactly as specified you will not receive marks for the corresponding marking criteria.

Task1: [3 marks]

When the client makes a get request for api/TalkBack/Hello or api/TalkBack/Sort, the talkback controller must handle the responses.

Complete these methods so that they offer the responses detailed in the specification.

Once you have created these methods, right-click the WebAPI project, select Debug and click Start New Instance. This should fire up IIS, open your browser and take you to a web page with an address similar to localhost:24702 where 24702 is your portnumber. Identify what this port number is as it will be required in your client and for testing.

For your personal testing you can identify if your server is working by sending a get request with the URI (replace <portnumber> with your port number) of:

For Hello:       localhost:<portnumber>/api/talkback/hello

Should return "Hello World" in the body of the result with a status code of OK (200)

For Sort:        localhost:<portnumber>/api/talkback/sort?integers=8&integers=2&integers=5

Should return [2,5,8] in the body of the result with a status code of OK (200)

If there are no integers submitted, the result should be  [] with a status code of OK (200)

If the submitted integers are invalid (e.g. a char is submitted) the server should return with a with a status code of BAD REQUEST (400)

Task2:

In order to work with databases, you have been asked to use Entity Framework Core.

Gribbald has already put all of the references, etc. that you will need into the project. You just need to define a User class.

Open User.cs, where you will find the Task2 region. Complete the User class with:

•     An empty constructor

•     A public string ApiKey property (which is the unique database key and is the API Key for the user)

•     A public string UserName property (which is the username of the user)

•     A public string or enum Role property (which saves the role of the user)

UserContext.cs has been created for you already, but to create the database correctly you’ll need to generate a migration*.

*You may first need to set the project directory by typing cd DistSysAcw into the Package Manager Console

Task3:

You should keep your controller classes only loosely coupled to the database access code. Ensuring your code is nicely abstracted is good coding practice as it will allow you to easily swap out the data access logic in the future without editing your controllers. You may like to create these data access functions now.

For the next tasks, you may need database access such as:

1.   Create a new user, using a username given as a parameter and creating a new GUID which is saved as a string to the database as the ApiKey. This must return the ApiKey or the User object so that the server can pass the Key back to the client.

2.   Check if a user with a given ApiKey string exists in the database, returning true or false.

3.   Check if a user with a given ApiKey and UserName exists in the database, returning true or false.

4.   Check if a user with a given ApiKey string exists in the database, returning the User object.

5.   Delete a user with a given ApiKey from the database

Hint: The BaseController already contains an instance of UserContext

Consider: What happens in your solution if two admin users simultaneously try to change a user’s role?

Task4:

When the client makes a api/User/New get request or api/User/New post request, the server must be able to handle them and provide a response.

Create a new controller called UserController. The easiest way to do this is by right-clicking on Controllers and adding a new, Empty API Controller. You may want to inspect the existing TalkBack Controller to help set up your new controller – note it’s base type, constructor and routing.

Both of these actions have the name‘New’but one is a GET request that takes its parameter from the URI and the other is a POST request that takes a JSON string parameter from the body. The POST request must use a content-type of application/json. Note the difference between a JSON string and a JSON Object with a string.

For your personal testing you can identify if your server is working by sending a get request with the URI (replace <portnumber> with your port number) of:

For GET:        localhost:<portnumber>/api/user/new?username=UserOne

If a user with the username‘UserOne’exists in the database, the server should return "True - User Does Exist! Did you mean to do a POST to create a new user?" in the body of the result with a status code of OK (200)

If a user with the username‘UserOne’does not exist in the database, the server should return "False - User Does Not Exist! Did you mean to do a POST to create a new user?" in the body of the result with a status code of OK (200).

If there is no string submitted, the server should return "False - User Does Not Exist! Did you mean to do a POST to create a new user?" in the body of the result with a status code of OK (200).

For POST:       localhost:<portnumber>/api/user/new with only “UserOne” in the body of the request

Should create a new User with the username‘UserOne’, generate a new GUID as the user’s API Key, and then add the new user to the database. Finally, the server should return the API Key as a string to the client with a status code of OK (200). If this is the first user they should be saved as Admin role, otherwise just with User role.

If there is no string submitted in the body, the result should be "Oops. Make sure your body contains a string with your username and your Content-Type is Content-Type:application/json" with a status code of BAD REQUEST (400)

If the username is alrady taken, the result should be "Oops. This username is already in use. Please try again with a new username." with a status code of FORBIDDEN (403)

Task5:

The client now has the ability to ask for an API Key, so our server will now need to be able to determine if a request has a valid API Key in its header. A useful way to do this is to use an Authentication Scheme. Ours is a custom Authentication Schemesowe can investige its functionality. Return to CustomAuthenticationHandler.cs. You must add code to HandleAuthenticateAsync which tries to get the header‘ApiKey’, and if it does exist, checks the database to determine if the given API Key is valid. You should use your UserDatabaseAccess class that you created in TASK3 to do the database access to loosen your coupling.

If the API Key is valid, you must get the relevant User from your database and set up a:

•     Claim of type ClaimTypes.Name, using the user’s UserName as the string value

•     Claim of type ClaimTypes.Role, using the user’s Role as the string value

•     ClaimsIdentity with an authentication type of“ApiKey”, using an array containing both of your Claims

•     ClaimsPrinciple, using the ClaimsIdentity above.

Finally, you must create an AuthenticationTicket using the ClaimsPrinciple and the scheme name (you can get this using the property: this.Scheme.Name). You can now use the ticket to create a Success AuthenticateResult and return as a Task.

If  this  is  working,  you  should   be  able  to   use  attributes  such  as   [Authorize(Roles  =   "Admin")] or [Authorize(Roles = "Admin, User")] to authorise requests which require a valid API key to be present.

If a user is not found, return a Fail AuthenticateResult and pass a 401 error with the JSON string "Unauthorized. Check ApiKey in Header is correct." back to the user (see HandleChallengeAsync).

Task6:

So far we haven’t used any of our Roles. Before we can do this we’ll need to check that our users have the roles they are supposed to. We are already looking at whether or not they have a valid API key (Task5), and we’re using that easily by applying an attribute, so we’ll use a filter to modify the response.

Most of the filter has already been written. Open CustomAuthorizationHandler.cs and modify the code so that, when the action requires a user to be in Admin role ONLY (e.g.  [Authorize(Roles =  "Admin")]) and the user does not have the Admin role, you return a Forbidden status (403) with the message: "Forbidden. Admin access only.". You will need the injected IHttpContextAccessor for this.

Task7:

Add to your UserController a method to handle an api/User/RemoveUser DELETE request.

•     A user with role User or Admin should be able to use this request.

•     The client must send its API Key in the header and a string username in the URI.

If the server receives this request, it must extract the ApiKey string from the header to see if the API Key is in the database and, if it is, it must check that the username and API Key are the same user and if they are, it must delete this user from the database. You should probably use your UserDatabaseAccess class that you created in TASK3 to do the database access.

This method must return a Boolean value only. If a user has been deleted, the server must return true, otherwise, the server must return false. In both cases, the server must return a status code of OK (200).

For your personal testing you can identify if your server is working by sending a delete request with the URI (replace <portnumber> with your port number and <username> with a username) of:

RemoveUser:      localhost:<portnumber>/api/user/removeuser?username=<username> with an ApiKey in the header of the request

Should return true if the ApiKey and username match, are valid, and a user has been deleted or false if not.

Task8:

Inside UserController Add the api/User/ChangeRole method.

This method must only be accessible to users who are authorised by API key and have the role of Admin. Update the role for the given username to the role provided (User or Admin).

The body should contain a JSON object in the form (where <username> is the given username string and <role> is the given role string):

{   "username":<username>, "role":<role>   }

For your personal testing you can identify if your server is working by sending a post request with the URI (replace <portnumber> with your port number) of:

ChangeRole:  localhost:<portnumber>/api/user/changerole with an ApiKey in the header of the request, a string username in the body and a string role in the body

If success: Should return "DONE" in the body of the result, with a status code of OK (200)

If username does not exist: Should return "NOT DONE: Username does not exist" in the body of the result, with a status code of BAD REQUEST (400)

If role is not User or Admin: Should return "NOT DONE: Role does not exist" in the body of the result, with a status code of BAD REQUEST (400)

In all other error cases: Should return "NOT DONE: An error occured" in the body of the result, with a status code of BAD REQUEST (400)

Task9:

Create  a  new  ProtectedController.  Add the  api/Protected/Hello  method,  api/Protected/SHA1  method  and api/Protected/SHA256 method.

All of these requests must be authorised (User or Admin role) and all three must return strings to the client. You may use the .NET SHA1 and SHA256 types for SHA1 and SHA256 hashing respectively.

Both SHA1 and SHA256 methods must take a string message from the URI and both must return the hexadecimal hash as a string with no additional characters (e.g. no delimiters like -)

For your personal testing you can identify if your server is working by sending a get request with the URI (replace <portnumber> with your port number) of:

For Hello:       localhost:<portnumber>/api/protected/hello with an ApiKey in the header of the request

Should return "Hello <UserName>" in the body of the result, where UserName is the User’s UserName from the database, with a status code of OK (200). E.g.“Hello UserOne”.

For SHA1:      localhost:<portnumber>/api/protected/sha1?message=hello

Should return "AAF4C61DDCC5E8A2DABEDE0F3B482CD9AEA9434D" in the body of the result with a status code of OK (200)

If there is no message string submitted, the result should be  "Bad Request" with a status code of BAD REQUEST (400)

For SHA256:  localhost:<portnumber>/api/protected/sha256?message=hello

Should return "2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824" in the body of the result with a status code of OK (200)

If there is no message string submitted, the result should be  "Bad Request" with a status code of BAD REQUEST (400)





版权所有:留学生编程辅导网 2020 All Rights Reserved 联系方式:QQ:99515681 微信:codinghelp 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。 站长地图

python代写
微信客服:codinghelp