Browse Source

Reader role.

pull/95/head
Sebastian Stehle 9 years ago
parent
commit
213774d352
  1. 3
      src/Squidex.Domain.Apps.Core/Apps/PermissionLevel.cs
  2. 6
      src/Squidex.Infrastructure/Security/Extensions.cs
  3. 2
      src/Squidex.Shared/Identity/SquidexRoles.cs
  4. 7
      src/Squidex/Controllers/Api/Assets/AssetsController.cs
  5. 35
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  6. 22
      src/Squidex/Pipeline/Extensions.cs
  7. 21
      src/Squidex/Pipeline/MustBeAppReaderAttribute.cs
  8. 8
      src/Squidex/app/shared/services/contents.service.spec.ts
  9. 2
      src/Squidex/app/shared/services/contents.service.ts

3
src/Squidex.Domain.Apps.Core/Apps/PermissionLevel.cs

@ -12,6 +12,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{
Owner,
Developer,
Editor
Editor,
Reader
}
}

6
src/Squidex.Infrastructure/Security/Extensions.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Security.Claims;
@ -42,5 +43,10 @@ namespace Squidex.Infrastructure.Security
{
return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.Email)?.Value;
}
public static bool IsInClient(this ClaimsPrincipal principal, string client)
{
return principal.Claims.Any(x => x.Type == OpenIdClaims.ClientId && string.Equals(x.Value, client, StringComparison.OrdinalIgnoreCase));
}
}
}

2
src/Squidex.Shared/Identity/SquidexRoles.cs

@ -16,6 +16,8 @@ namespace Squidex.Shared.Identity
public static readonly string AppEditor = "APP-EDITOR";
public static readonly string AppReader = "APP-READER";
public static readonly string AppDeveloper = "APP-DEVELOPER";
}
}

7
src/Squidex/Controllers/Api/Assets/AssetsController.cs

@ -30,7 +30,6 @@ namespace Squidex.Controllers.Api.Assets
/// <summary>
/// Uploads and retrieves assets.
/// </summary>
[MustBeAppEditor]
[ApiExceptionFilter]
[AppApi]
[SwaggerTag("Assets")]
@ -71,6 +70,7 @@ namespace Squidex.Controllers.Api.Assets
/// <remarks>
/// Get all assets for the app. Mime types can be comma-separated, e.g. application/json,text/html.
/// </remarks>
[MustBeAppReader]
[HttpGet]
[Route("apps/{app}/assets/")]
[ProducesResponseType(typeof(AssetsDto), 200)]
@ -123,6 +123,7 @@ namespace Squidex.Controllers.Api.Assets
/// 200 => Asset found.
/// 404 => Asset or app not found.
/// </returns>
[MustBeAppReader]
[HttpGet]
[Route("apps/{app}/assets/{id}")]
[ProducesResponseType(typeof(AssetsDto), 200)]
@ -156,6 +157,7 @@ namespace Squidex.Controllers.Api.Assets
/// <remarks>
/// You can only upload one file at a time. The mime type of the file is not calculated by Squidex and must be defined correctly.
/// </remarks>
[MustBeAppEditor]
[HttpPost]
[Route("apps/{app}/assets/")]
[ProducesResponseType(typeof(AssetCreatedDto), 201)]
@ -184,6 +186,7 @@ namespace Squidex.Controllers.Api.Assets
/// 404 => Asset or app not found.
/// 400 => Asset exceeds the maximum size.
/// </returns>
[MustBeAppEditor]
[HttpPut]
[Route("apps/{app}/assets/{id}/content")]
[ProducesResponseType(typeof(AssetReplacedDto), 201)]
@ -213,6 +216,7 @@ namespace Squidex.Controllers.Api.Assets
/// 400 => Asset name not valid.
/// 404 => Asset or app not found.
/// </returns>
[MustBeAppReader]
[HttpPut]
[Route("apps/{app}/assets/{id}")]
[ProducesResponseType(typeof(ErrorDto), 400)]
@ -235,6 +239,7 @@ namespace Squidex.Controllers.Api.Assets
/// 204 => Asset has been deleted.
/// 404 => Asset or app not found.
/// </returns>
[MustBeAppEditor]
[HttpDelete]
[Route("apps/{app}/assets/{id}/")]
[ApiCosts(1)]

35
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -26,7 +26,6 @@ using Squidex.Pipeline;
namespace Squidex.Controllers.ContentApi
{
[MustBeAppEditor]
[ApiExceptionFilter]
[AppApi]
[SwaggerIgnore]
@ -47,7 +46,8 @@ namespace Squidex.Controllers.ContentApi
this.schemas = schemas;
this.contentRepository = contentRepository;
}
[MustBeAppReader]
[HttpGet]
[Route("content/{app}/graphql")]
[ApiCosts(2)]
@ -57,7 +57,8 @@ namespace Squidex.Controllers.ContentApi
return Ok(result);
}
[MustBeAppReader]
[HttpPost]
[Route("content/{app}/graphql")]
[ApiCosts(2)]
@ -67,11 +68,12 @@ namespace Squidex.Controllers.ContentApi
return Ok(result);
}
[MustBeAppReader]
[HttpGet]
[Route("content/{app}/{name}")]
[ApiCosts(2)]
public async Task<IActionResult> GetContents(string name, [FromQuery] bool nonPublished = false, [FromQuery] bool hidden = false, [FromQuery] string ids = null)
public async Task<IActionResult> GetContents(string name, [FromQuery] string ids = null)
{
var schemaEntity = await FindSchemaAsync(name);
@ -88,10 +90,12 @@ namespace Squidex.Controllers.ContentApi
}
}
var isFrontendClient = User.IsFrontendClient();
var query = Request.QueryString.ToString();
var taskForItems = contentRepository.QueryAsync(App, schemaEntity.Id, nonPublished, idsList, query);
var taskForCount = contentRepository.CountAsync(App, schemaEntity.Id, nonPublished, idsList, query);
var taskForItems = contentRepository.QueryAsync(App, schemaEntity.Id, isFrontendClient, idsList, query);
var taskForCount = contentRepository.CountAsync(App, schemaEntity.Id, isFrontendClient, idsList, query);
await Task.WhenAll(taskForItems, taskForCount);
@ -104,7 +108,7 @@ namespace Squidex.Controllers.ContentApi
if (x.Data != null)
{
itemModel.Data = x.Data.ToApiModel(schemaEntity.Schema, App.LanguagesConfig);
itemModel.Data = x.Data.ToApiModel(schemaEntity.Schema, App.LanguagesConfig, null, !isFrontendClient);
}
return itemModel;
@ -113,11 +117,12 @@ namespace Squidex.Controllers.ContentApi
return Ok(response);
}
[MustBeAppReader]
[HttpGet]
[Route("content/{app}/{name}/{id}")]
[ApiCosts(1)]
public async Task<IActionResult> GetContent(string name, Guid id, bool hidden = false)
public async Task<IActionResult> GetContent(string name, Guid id)
{
var schemaEntity = await FindSchemaAsync(name);
@ -137,7 +142,9 @@ namespace Squidex.Controllers.ContentApi
if (entity.Data != null)
{
response.Data = entity.Data.ToApiModel(schemaEntity.Schema, App.LanguagesConfig, null, hidden);
var isFrontendClient = User.IsFrontendClient();
response.Data = entity.Data.ToApiModel(schemaEntity.Schema, App.LanguagesConfig, null, !isFrontendClient);
}
Response.Headers["ETag"] = new StringValues(entity.Version.ToString());
@ -145,6 +152,7 @@ namespace Squidex.Controllers.ContentApi
return Ok(response);
}
[MustBeAppEditor]
[HttpPost]
[Route("content/{app}/{name}/")]
[ApiCosts(1)]
@ -160,6 +168,7 @@ namespace Squidex.Controllers.ContentApi
return CreatedAtAction(nameof(GetContent), new { id = response.Id }, response);
}
[MustBeAppEditor]
[HttpPut]
[Route("content/{app}/{name}/{id}")]
[ApiCosts(1)]
@ -172,6 +181,7 @@ namespace Squidex.Controllers.ContentApi
return NoContent();
}
[MustBeAppEditor]
[HttpPatch]
[Route("content/{app}/{name}/{id}")]
[ApiCosts(1)]
@ -184,6 +194,7 @@ namespace Squidex.Controllers.ContentApi
return NoContent();
}
[MustBeAppEditor]
[HttpPut]
[Route("content/{app}/{name}/{id}/publish")]
[ApiCosts(1)]
@ -196,6 +207,7 @@ namespace Squidex.Controllers.ContentApi
return NoContent();
}
[MustBeAppEditor]
[HttpPut]
[Route("content/{app}/{name}/{id}/unpublish")]
[ApiCosts(1)]
@ -208,6 +220,7 @@ namespace Squidex.Controllers.ContentApi
return NoContent();
}
[MustBeAppEditor]
[HttpDelete]
[Route("content/{app}/{name}/{id}")]
[ApiCosts(1)]

22
src/Squidex/Pipeline/Extensions.cs

@ -0,0 +1,22 @@
// ==========================================================================
// Extensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Security.Claims;
using Squidex.Config;
using Squidex.Infrastructure.Security;
namespace Squidex.Pipeline
{
public static class Extensions
{
public static bool IsFrontendClient(this ClaimsPrincipal principal)
{
return principal.IsInClient(Constants.FrontendClient);
}
}
}

21
src/Squidex/Pipeline/MustBeAppReaderAttribute.cs

@ -0,0 +1,21 @@
// ==========================================================================
// MustBeAppReaderAttribute.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Microsoft.AspNetCore.Authorization;
using Squidex.Shared.Identity;
namespace Squidex.Pipeline
{
public sealed class MustBeAppReaderAttribute : AuthorizeAttribute
{
public MustBeAppReaderAttribute()
{
Roles = SquidexRoles.AppReader;
}
}
}

8
src/Squidex/app/shared/services/contents.service.spec.ts

@ -45,7 +45,7 @@ describe('ContentsService', () => {
contents = result;
});
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$top=17&$skip=13');
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?$top=17&$skip=13');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
@ -100,7 +100,7 @@ describe('ContentsService', () => {
contents = result;
});
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$search="my-query"&$top=17&$skip=13');
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?$search="my-query"&$top=17&$skip=13');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
@ -117,7 +117,7 @@ describe('ContentsService', () => {
contents = result;
});
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$top=17&$skip=13&ids=id1,id2');
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?$top=17&$skip=13&ids=id1,id2');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
@ -134,7 +134,7 @@ describe('ContentsService', () => {
contents = result;
});
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?nonPublished=true&hidden=true&$filter=my-filter&$top=17&$skip=13');
const req = httpMock.expectOne('http://service/p/api/content/my-app/my-schema?$filter=my-filter&$top=17&$skip=13');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();

2
src/Squidex/app/shared/services/contents.service.ts

@ -73,7 +73,7 @@ export class ContentsService {
fullQuery += `&ids=${ids.join(',')}`;
}
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?nonPublished=true&hidden=true${fullQuery}`);
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?${fullQuery}`);
return HTTP.getVersioned(this.http, url)
.map(response => {

Loading…
Cancel
Save