Bento support + upgrade security (filter by instance mandatory)

This commit is contained in:
Thomas Fransolet 2026-07-14 17:21:10 +02:00
parent 3cd5f7573d
commit 19a0157b19
14 changed files with 3452 additions and 47 deletions

View File

@ -31,6 +31,12 @@ namespace ManagerService.Controllers
_logger = logger;
}
private string? GetCallerInstanceId() =>
User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
private bool IsSuperAdmin() =>
User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
/// <summary>
/// Traduit un texte HTML vers plusieurs langues via IA
/// </summary>
@ -42,6 +48,9 @@ namespace ManagerService.Controllers
{
try
{
if (!IsSuperAdmin() && instanceId != GetCallerInstanceId())
return Forbid();
var instance = _context.Instances.FirstOrDefault(i => i.Id == instanceId);
if (instance == null || !instance.IsAssistant)
return Forbid();
@ -81,6 +90,9 @@ namespace ManagerService.Controllers
{
try
{
if (!IsSuperAdmin() && request.InstanceId != GetCallerInstanceId())
return Forbid();
// Vérifie que l'instance a activé la fonctionnalité assistant
var instance = _context.Instances
.FirstOrDefault(i => i.Id == request.InstanceId);

View File

@ -34,6 +34,12 @@ namespace ManagerService.Controllers
_myInfoMateDbContext = myInfoMateDbContext;
}
private string? GetCallerInstanceId() =>
User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
private bool IsSuperAdmin() =>
User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
/// <summary>
/// Get a list of all devices
/// </summary>
@ -45,10 +51,13 @@ namespace ManagerService.Controllers
{
try
{
//List<OldDevice> devices = _deviceService.GetAll(instanceId);
List<Device> devices = _myInfoMateDbContext.Devices.Include(d => d.Configuration).ToList();
var scopedInstanceId = IsSuperAdmin() ? instanceId : GetCallerInstanceId();
return new OkObjectResult(devices.Select(d => d.ToDTO()));
var query = _myInfoMateDbContext.Devices.Include(d => d.Configuration).AsQueryable();
if (scopedInstanceId != null)
query = query.Where(d => d.InstanceId == scopedInstanceId);
return new OkObjectResult(query.ToList().Select(d => d.ToDTO()));
}
catch (Exception ex)
{
@ -61,7 +70,6 @@ namespace ManagerService.Controllers
/// Get a specific device
/// </summary>
/// <param name="id">id device</param>
[AllowAnonymous]
[ProducesResponseType(typeof(DeviceDetailDTO), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
@ -73,7 +81,7 @@ namespace ManagerService.Controllers
//OldDevice device = _deviceService.GetById(id);
Device device = _myInfoMateDbContext.Devices.Include(d => d.Configuration).FirstOrDefault(i => i.Id == id);
if (device == null)
if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("This device was not found");
return new OkObjectResult(device.ToDetailDTO());
@ -92,7 +100,6 @@ namespace ManagerService.Controllers
/// Create a new device
/// </summary>
/// <param name="newDevice">New device info</param>
[AllowAnonymous]
[ProducesResponseType(typeof(DeviceDetailDTO), 200)]
[ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 404)]
@ -106,6 +113,9 @@ namespace ManagerService.Controllers
if (newDevice == null)
throw new ArgumentNullException("Device param is null");
if (!IsSuperAdmin() && newDevice.instanceId != GetCallerInstanceId())
throw new UnauthorizedAccessException("Cannot create a device for another instance");
//var configuration = _configurationService.GetById(newDevice.configurationId);
var configuration = _myInfoMateDbContext.Configurations.FirstOrDefault(c => c.Id == newDevice.configurationId);
@ -144,6 +154,9 @@ namespace ManagerService.Controllers
ApplicationInstance applicationInstance = _myInfoMateDbContext.ApplicationInstances.FirstOrDefault(ai => ai.InstanceId == newDevice.instanceId && ai.AppType == AppType.Tablet);
if (applicationInstance == null)
throw new KeyNotFoundException("Application instance does not exist");
//OldDevice deviceCreated = _deviceService.IsExistIdentifier(newDevice.identifier) ? _deviceService.Update(device.Id, device) : _deviceService.Create(device);
if (deviceDB != null)
{
@ -172,6 +185,10 @@ namespace ManagerService.Controllers
{
return new BadRequestObjectResult(ex.Message) { };
}
catch (UnauthorizedAccessException ex)
{
return new ObjectResult(ex.Message) { StatusCode = 403 };
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
@ -206,10 +223,12 @@ namespace ManagerService.Controllers
//OldDevice device = _deviceService.GetById(updatedDevice.id);
Device device = _myInfoMateDbContext.Devices.FirstOrDefault(d => d.Id == updatedDevice.id);
if (device == null)
if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("Device does not exist");
// Todo add some verification ?
if (!IsSuperAdmin() && updatedDevice.instanceId != device.InstanceId)
throw new UnauthorizedAccessException("Cannot move a device to another instance");
device.Name = updatedDevice.name;
device.InstanceId = updatedDevice.instanceId;
device.Identifier = updatedDevice.identifier;
@ -230,6 +249,10 @@ namespace ManagerService.Controllers
{
return new BadRequestObjectResult(ex.Message) { };
}
catch (UnauthorizedAccessException ex)
{
return new ObjectResult(ex.Message) { StatusCode = 403 };
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };
@ -259,7 +282,7 @@ namespace ManagerService.Controllers
//OldDevice device = _deviceService.GetById(deviceIn.id);
Device device = _myInfoMateDbContext.Devices.FirstOrDefault(d => d.Id == deviceIn.id);
if (device == null)
if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("Device does not exist");
//var configuration = _configurationService.GetById(deviceIn.configurationId);
@ -314,7 +337,7 @@ namespace ManagerService.Controllers
Device device = _myInfoMateDbContext.Devices.FirstOrDefault(d => d.Id == id);
if (device == null)
if (device == null || (!IsSuperAdmin() && device.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("Device does not exist");
_myInfoMateDbContext.Remove(device);

View File

@ -25,6 +25,12 @@ namespace ManagerService.Controllers
_db = db;
}
private string? GetCallerInstanceId() =>
User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
private bool IsSuperAdmin() =>
User.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission, ManagerService.Service.Security.Permissions.SuperAdmin);
/// <summary>Track a single visit event (anonymous)</summary>
[AllowAnonymous]
[HttpPost("event")]
@ -81,6 +87,9 @@ namespace ManagerService.Controllers
if (string.IsNullOrEmpty(instanceId))
return BadRequest("instanceId is required");
if (!IsSuperAdmin() && instanceId != GetCallerInstanceId())
return Forbid();
var instance = _db.Instances
.FirstOrDefault(i => i.Id == instanceId);

View File

@ -85,7 +85,7 @@ namespace ManagerService.Controllers
{
User user = _myInfoMateDbContext.Users.FirstOrDefault(i => i.Id == id);
if (user == null)
if (user == null || (!IsSuperAdmin() && user.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("This user was not found");
return new OkObjectResult(user.ToDTO());
@ -127,7 +127,7 @@ namespace ManagerService.Controllers
throw new UnauthorizedAccessException("Cannot assign a role higher than your own");
User newUser = new User();
newUser.InstanceId = newUserDTO.instanceId;
newUser.InstanceId = IsSuperAdmin() ? newUserDTO.instanceId : GetCallerInstanceId();
newUser.Email = newUserDTO.email;
newUser.FirstName = newUserDTO.firstName;
newUser.LastName = newUserDTO.lastName;
@ -182,9 +182,12 @@ namespace ManagerService.Controllers
User user = _myInfoMateDbContext.Users.FirstOrDefault(u => u.Id == updatedUser.id);
if (user == null)
if (user == null || (!IsSuperAdmin() && user.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("User does not exist");
if (!IsSuperAdmin() && user.Role < GetCallerRole())
throw new UnauthorizedAccessException("Cannot modify a user with a higher role than your own");
user.FirstName = updatedUser.firstName;
user.LastName = updatedUser.lastName;
@ -235,9 +238,12 @@ namespace ManagerService.Controllers
User user = _myInfoMateDbContext.Users.FirstOrDefault(u => u.Id == id);
if (user == null)
if (user == null || (!IsSuperAdmin() && user.InstanceId != GetCallerInstanceId()))
throw new KeyNotFoundException("User does not exist");
if (!IsSuperAdmin() && user.Role < GetCallerRole())
throw new UnauthorizedAccessException("Cannot delete a user with a higher role than your own");
_myInfoMateDbContext.Remove(user);
_myInfoMateDbContext.SaveChanges();
@ -247,6 +253,10 @@ namespace ManagerService.Controllers
{
return new BadRequestObjectResult(ex.Message) { };
}
catch (UnauthorizedAccessException ex)
{
return new ObjectResult(ex.Message) { StatusCode = 403 };
}
catch (KeyNotFoundException ex)
{
return new NotFoundObjectResult(ex.Message) { };

View File

@ -16,7 +16,9 @@ namespace ManagerService.DTOs
public bool isActive { get; set; } = true;
public int? weightMasonryGrid { get; set; } // Specific Mobile
public int? gridColSpan { get; set; } // Specific Mobile & Web — nb de colonnes occupées (bento)
public int? gridRowSpan { get; set; } // Specific Mobile & Web — nb de lignes occupées (bento)
public bool isDate { get; set; } // Specific Kiosk
@ -32,8 +34,6 @@ namespace ManagerService.DTOs
public DeviceDTO device { get; set; } // Specific Kiosk
public LayoutMainPageType layoutMainPage { get; set; } // Specific Kiosk
public string loaderImageId { get; set; } // Specific Kiosk
public string loaderImageUrl { get; set; } // Specific Kiosk

View File

@ -26,8 +26,6 @@ namespace ManagerService.DTOs
public string secondaryColor { get; set; }
public LayoutMainPageType layoutMainPage { get; set; }
public List<string> languages { get; set; }
public string sectionEventId { get; set; }

View File

@ -28,7 +28,9 @@ namespace ManagerService.Data
public bool IsActive { get; set; } = true; // Specific Mobile
public int? WeightMasonryGrid { get; set; } // Specific Mobile
public int? GridColSpan { get; set; } // Specific Mobile & Web — nb de colonnes occupées (bento)
public int? GridRowSpan { get; set; } // Specific Mobile & Web — nb de lignes occupées (bento)
public bool IsDate { get; set; } // Specific Kiosk
@ -40,8 +42,6 @@ namespace ManagerService.Data
public bool IsSectionImageBackground { get; set; } // => Chose layout of main page // Specific Kiosk
public LayoutMainPageType LayoutMainPage { get; set; } = LayoutMainPageType.MasonryGrid; // Specific Kiosk and mobile
public string LoaderImageId { get; set; } // Specific Kiosk
public string LoaderImageUrl { get; set; } // Specific Kiosk
@ -70,7 +70,8 @@ namespace ManagerService.Data
applicationInstanceId = ApplicationInstanceId,
order = Order,
isActive = IsActive,
weightMasonryGrid = WeightMasonryGrid,
gridColSpan = GridColSpan,
gridRowSpan = GridRowSpan,
primaryColor = PrimaryColor,
secondaryColor = SecondaryColor,
loaderImageId = LoaderImageId,
@ -91,7 +92,8 @@ namespace ManagerService.Data
ApplicationInstanceId = appConfigurationLinkDTO.applicationInstanceId;
Order = appConfigurationLinkDTO.order;
IsActive = appConfigurationLinkDTO.isActive;
WeightMasonryGrid = appConfigurationLinkDTO?.weightMasonryGrid;
GridColSpan = appConfigurationLinkDTO?.gridColSpan;
GridRowSpan = appConfigurationLinkDTO?.gridRowSpan;
PrimaryColor = appConfigurationLinkDTO.primaryColor;
SecondaryColor = appConfigurationLinkDTO.secondaryColor;
LoaderImageId = appConfigurationLinkDTO.loaderImageId;

View File

@ -38,8 +38,6 @@ namespace ManagerService.Data
public string SecondaryColor { get; set; } // Specific Mobile et web
public LayoutMainPageType LayoutMainPage { get; set; } = LayoutMainPageType.MasonryGrid; // Specific Mobile et web
public List<string> Languages { get; set; } // All app must support languages, if not, client's problem
public string? SectionEventId { get; set; } // Specific Mobile et web(?)
@ -71,7 +69,6 @@ namespace ManagerService.Data
loaderImageUrl = LoaderImageUrl,
primaryColor = PrimaryColor,
secondaryColor = SecondaryColor,
layoutMainPage = LayoutMainPage,
languages = Languages,
sectionEventId = SectionEventId,
sectionEventDTO = sectionEventDTO,
@ -89,7 +86,6 @@ namespace ManagerService.Data
LoaderImageUrl = dto.loaderImageUrl;
PrimaryColor = dto.primaryColor;
SecondaryColor = dto.secondaryColor;
LayoutMainPage = dto.layoutMainPage;
Languages = dto.languages;
Configurations = dto.configurations;
SectionEventId = dto.sectionEventId;
@ -108,10 +104,4 @@ namespace ManagerService.Data
VR,
Voice // Lunettes Ray-Ban Meta / interface vocale — pas de navigation UI
}
public enum LayoutMainPageType
{
SimpleGrid,
MasonryGrid
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ManagerService.Migrations
{
/// <inheritdoc />
public partial class RemoveLayoutMainPageType_WeightToDouble : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LayoutMainPage",
table: "ApplicationInstances");
migrationBuilder.DropColumn(
name: "LayoutMainPage",
table: "AppConfigurationLinks");
migrationBuilder.AlterColumn<double>(
name: "WeightMasonryGrid",
table: "AppConfigurationLinks",
type: "double precision",
nullable: true,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "LayoutMainPage",
table: "ApplicationInstances",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AlterColumn<int>(
name: "WeightMasonryGrid",
table: "AppConfigurationLinks",
type: "integer",
nullable: true,
oldClrType: typeof(double),
oldType: "double precision",
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "LayoutMainPage",
table: "AppConfigurationLinks",
type: "integer",
nullable: false,
defaultValue: 0);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ManagerService.Migrations
{
/// <inheritdoc />
public partial class ReplaceWeightWithGridSpans : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "WeightMasonryGrid",
table: "AppConfigurationLinks");
migrationBuilder.AddColumn<int>(
name: "GridColSpan",
table: "AppConfigurationLinks",
type: "integer",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "GridRowSpan",
table: "AppConfigurationLinks",
type: "integer",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "GridColSpan",
table: "AppConfigurationLinks");
migrationBuilder.DropColumn(
name: "GridRowSpan",
table: "AppConfigurationLinks");
migrationBuilder.AddColumn<double>(
name: "WeightMasonryGrid",
table: "AppConfigurationLinks",
type: "double precision",
nullable: true);
}
}
}

View File

@ -81,6 +81,12 @@ namespace ManagerService.Migrations
b.Property<string>("DeviceId")
.HasColumnType("text");
b.Property<int?>("GridColSpan")
.HasColumnType("integer");
b.Property<int?>("GridRowSpan")
.HasColumnType("integer");
b.Property<bool>("IsActive")
.HasColumnType("boolean");
@ -93,9 +99,6 @@ namespace ManagerService.Migrations
b.Property<bool>("IsSectionImageBackground")
.HasColumnType("boolean");
b.Property<int>("LayoutMainPage")
.HasColumnType("integer");
b.Property<string>("LoaderImageId")
.HasColumnType("text");
@ -117,9 +120,6 @@ namespace ManagerService.Migrations
b.Property<string>("SecondaryColor")
.HasColumnType("text");
b.Property<int?>("WeightMasonryGrid")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ApplicationInstanceId");
@ -149,9 +149,6 @@ namespace ManagerService.Migrations
b.PrimitiveCollection<List<string>>("Languages")
.HasColumnType("text[]");
b.Property<int>("LayoutMainPage")
.HasColumnType("integer");
b.Property<string>("LoaderImageId")
.HasColumnType("text");

View File

@ -63,7 +63,6 @@ namespace ManagerService.Security
new Claim(Service.Security.ClaimTypes.AppType, appType),
new Claim(Service.Security.ClaimTypes.Permission, Service.Security.Permissions.AppRead),
new Claim(Service.Security.ClaimTypes.Permission, Service.Security.Permissions.Viewer),
new Claim(Service.Security.ClaimTypes.Permission, Service.Security.Permissions.ContentEditor),
};
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));