Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/TwoWeeksReady/Authorization/AuthorizationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using AzureFunctions.OidcAuthentication;
using System.Linq;

namespace TwoWeeksReady.Authorization
{
public static class AuthorizationExtensions
{
public static bool IsInRole(this ApiAuthenticationResult authenticationResult, string roleName)
{
var roles = authenticationResult.User.FindAll("https://schemas.2wradmin.com/role");
return roles.Any(r => r.Value == roleName);
}
}
}
7 changes: 7 additions & 0 deletions api/TwoWeeksReady/Authorization/Roles.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TwoWeeksReady.Authorization
{
public static class Roles
{
public static string Admin = "admin";
}
}
335 changes: 174 additions & 161 deletions api/TwoWeeksReady/Hazards/HazardApiBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,168 +11,181 @@
using Microsoft.Azure.Documents.Linq;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using TwoWeeksReady.Authorization;

namespace TwoWeeksReady.Hazards
{
public abstract class HazardApiBase<T> where T: HazardBaseInfo
{
protected const string DatabaseName = "2wr";
protected const string ConnectionStringKey = "CosmosDBConnection";

private readonly IApiAuthentication _apiAuthentication;

protected HazardApiBase(IApiAuthentication apiAuthentication)
{
_apiAuthentication = apiAuthentication;
}

protected async Task<IActionResult> GetDocuments(HttpRequest req, DocumentClient client, ILogger log, string collectionName)
{
log.LogInformation($"Getting {collectionName} list");

var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);
var query = client.CreateDocumentQuery<T>(collectionUri).AsDocumentQuery();
var documents = new List<T>();

while (query.HasMoreResults)
{
var result = await query.ExecuteNextAsync<T>();
documents.AddRange(result);
}

return new OkObjectResult(documents);
}

protected async Task<IActionResult> GetDocument(
HttpRequest req, string id, DocumentClient client, ILogger log, string collectionName)
{
var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

if (String.IsNullOrWhiteSpace(id))
{
return new BadRequestObjectResult($"{collectionName}: id was not specified.");
}

log.LogInformation($"Getting {collectionName} document, id = {id}");
Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);
var feedOptions = new FeedOptions {EnableCrossPartitionQuery = false};
var document = client.CreateDocumentQuery<T>(collectionUri, feedOptions)
.Where(d => d.Id == id)
.AsEnumerable().FirstOrDefault();

if (document == null)
{
log.LogWarning($"{collectionName}: {id} not found.");
return new BadRequestObjectResult($"{collectionName} not found.");
}

return new OkObjectResult(document);
}

protected async Task<IActionResult> CreateDocument(HttpRequest req, DocumentClient client, ILogger log, string collectionName)
{
log.LogInformation($"Creating an {collectionName} document");

var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

var content = await new StreamReader(req.Body).ReadToEndAsync();
var newDocument = JsonConvert.DeserializeObject<T>(content);

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);
ResourceResponse<Document> createdDocument = await client.CreateDocumentAsync(collectionUri, newDocument);

return new OkObjectResult(createdDocument.Resource);
}

protected async Task<IActionResult> UpdateDocument(
HttpRequest req, DocumentClient client, ILogger log, string collectionName)
{
log.LogInformation($"Updating an {collectionName} document");
var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

var content = await new StreamReader(req.Body).ReadToEndAsync();
var documentUpdate = JsonConvert.DeserializeObject<T>(content);

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);

//verify existing document (not upserting as this is an update only function)
var feedOptions = new FeedOptions {EnableCrossPartitionQuery = false};
var existingDocument = client.CreateDocumentQuery<T>(collectionUri, feedOptions)
.Where(d => d.Id == documentUpdate.Id)
.AsEnumerable().FirstOrDefault();

if (existingDocument == null)
{
log.LogWarning($"{collectionName}: {documentUpdate.Id} not found.");
return new BadRequestObjectResult($"{collectionName} not found.");
}

var documentUri = UriFactory.CreateDocumentUri(DatabaseName, collectionName, documentUpdate.Id);
await client.ReplaceDocumentAsync(documentUri, documentUpdate);

return new OkObjectResult(documentUpdate);
}

protected async Task<IActionResult> DeleteDocument(
HttpRequest req, string id, DocumentClient client, ILogger log, string collectionName)
{
log.LogInformation($"Deleting {collectionName} document: id = {id}");
var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

if (String.IsNullOrWhiteSpace(id))
{
return new BadRequestObjectResult($"{collectionName}: id was not specified.");
}

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);

//verify existing document (not upserting as this is an update only function)
var feedOptions = new FeedOptions {EnableCrossPartitionQuery = false};
var existingDocument = client.CreateDocumentQuery<T>(collectionUri, feedOptions)
.Where(d => d.Id == id)
.AsEnumerable().FirstOrDefault();

if (existingDocument == null)
{
log.LogWarning($"{collectionName}: {id} not found.");
return new BadRequestObjectResult($"{collectionName} not found.");
}

var documentUri = UriFactory.CreateDocumentUri(DatabaseName, collectionName, id);

await client.DeleteDocumentAsync(documentUri, new RequestOptions
{
PartitionKey = new PartitionKey(id)
});

return new OkObjectResult(true);
}
}
public abstract class HazardApiBase<T> where T : HazardBaseInfo
{
protected const string DatabaseName = "2wr";
protected const string ConnectionStringKey = "CosmosDBConnection";

private readonly IApiAuthentication _apiAuthentication;

protected HazardApiBase(IApiAuthentication apiAuthentication)
{
_apiAuthentication = apiAuthentication;
}

protected async Task<IActionResult> GetDocuments(HttpRequest req, DocumentClient client, ILogger log, string collectionName)
{
log.LogInformation($"Getting {collectionName} list");

var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);
var query = client.CreateDocumentQuery<T>(collectionUri).AsDocumentQuery();
var documents = new List<T>();

while (query.HasMoreResults)
{
var result = await query.ExecuteNextAsync<T>();
documents.AddRange(result);
}

return new OkObjectResult(documents);
}

protected async Task<IActionResult> GetDocument(
HttpRequest req, string id, DocumentClient client, ILogger log, string collectionName)
{
var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

if (String.IsNullOrWhiteSpace(id))
{
return new BadRequestObjectResult($"{collectionName}: id was not specified.");
}

log.LogInformation($"Getting {collectionName} document, id = {id}");
Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);
var feedOptions = new FeedOptions { EnableCrossPartitionQuery = false };
var document = client.CreateDocumentQuery<T>(collectionUri, feedOptions)
.Where(d => d.Id == id)
.AsEnumerable().FirstOrDefault();

if (document == null)
{
log.LogWarning($"{collectionName}: {id} not found.");
return new BadRequestObjectResult($"{collectionName} not found.");
}

return new OkObjectResult(document);
}

protected async Task<IActionResult> CreateDocument(HttpRequest req, DocumentClient client, ILogger log, string collectionName, string requiredRole = null)
{
log.LogInformation($"Creating an {collectionName} document");

var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

if (!string.IsNullOrEmpty(requiredRole) && !authorizationResult.IsInRole(requiredRole))
{
log.LogWarning($"User is not in the {requiredRole} role");
return new UnauthorizedResult();
}

var content = await new StreamReader(req.Body).ReadToEndAsync();
var newDocument = JsonConvert.DeserializeObject<T>(content);

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);
ResourceResponse<Document> createdDocument = await client.CreateDocumentAsync(collectionUri, newDocument);

return new OkObjectResult(createdDocument.Resource);
}

protected async Task<IActionResult> UpdateDocument(
HttpRequest req, DocumentClient client, ILogger log, string collectionName, string requiredRole = null)
{
log.LogInformation($"Updating an {collectionName} document");
var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

if (!string.IsNullOrEmpty(requiredRole) && !authorizationResult.IsInRole(requiredRole))
{
log.LogWarning($"User is not in the {requiredRole} role");
return new UnauthorizedResult();
}

var content = await new StreamReader(req.Body).ReadToEndAsync();
var documentUpdate = JsonConvert.DeserializeObject<T>(content);

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);

//verify existing document (not upserting as this is an update only function)
var feedOptions = new FeedOptions { EnableCrossPartitionQuery = false };
var existingDocument = client.CreateDocumentQuery<T>(collectionUri, feedOptions)
.Where(d => d.Id == documentUpdate.Id)
.AsEnumerable().FirstOrDefault();

if (existingDocument == null)
{
log.LogWarning($"{collectionName}: {documentUpdate.Id} not found.");
return new BadRequestObjectResult($"{collectionName} not found.");
}

var documentUri = UriFactory.CreateDocumentUri(DatabaseName, collectionName, documentUpdate.Id);
await client.ReplaceDocumentAsync(documentUri, documentUpdate);

return new OkObjectResult(documentUpdate);
}

protected async Task<IActionResult> DeleteDocument(
HttpRequest req, string id, DocumentClient client, ILogger log, string collectionName)
{
log.LogInformation($"Deleting {collectionName} document: id = {id}");
var authorizationResult = await _apiAuthentication.AuthenticateAsync(req.Headers);
if (authorizationResult.Failed)
{
log.LogWarning(authorizationResult.FailureReason);
return new UnauthorizedResult();
}

if (String.IsNullOrWhiteSpace(id))
{
return new BadRequestObjectResult($"{collectionName}: id was not specified.");
}

Uri collectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseName, collectionName);

//verify existing document (not upserting as this is an update only function)
var feedOptions = new FeedOptions { EnableCrossPartitionQuery = false };
var existingDocument = client.CreateDocumentQuery<T>(collectionUri, feedOptions)
.Where(d => d.Id == id)
.AsEnumerable().FirstOrDefault();

if (existingDocument == null)
{
log.LogWarning($"{collectionName}: {id} not found.");
return new BadRequestObjectResult($"{collectionName} not found.");
}

var documentUri = UriFactory.CreateDocumentUri(DatabaseName, collectionName, id);

await client.DeleteDocumentAsync(documentUri, new RequestOptions
{
PartitionKey = new PartitionKey(id)
});

return new OkObjectResult(true);
}
}
}
5 changes: 3 additions & 2 deletions api/TwoWeeksReady/Hazards/HazardInfoApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using AzureFunctions.OidcAuthentication;
using TwoWeeksReady.Authorization;

namespace TwoWeeksReady.Hazards
{
Expand Down Expand Up @@ -52,7 +53,7 @@ public async Task<IActionResult> CreateDocument(
DocumentClient client,
ILogger log)
{
return await CreateDocument(req, client, log, CollectionName);
return await CreateDocument(req, client, log, CollectionName, Roles.Admin);
}


Expand All @@ -64,7 +65,7 @@ public async Task<IActionResult> UpdateDocument(
DocumentClient client,
ILogger log)
{
return await UpdateDocument(req, client, log, CollectionName);
return await UpdateDocument(req, client, log, CollectionName, Roles.Admin);
}


Expand Down