Add stub of v2 react ui
6
.gitignore
vendored
@ -8,3 +8,9 @@
|
|||||||
**.csproj.user
|
**.csproj.user
|
||||||
*spa.min.*
|
*spa.min.*
|
||||||
**spa.min.*
|
**spa.min.*
|
||||||
|
|
||||||
|
/FlexitimeUI/_ReSharper.Caches/*
|
||||||
|
*/_ReSharper.Caches/*
|
||||||
|
**/_ReSharper.Caches/**
|
||||||
|
**v3.ncrunchsolution.user
|
||||||
|
**UpgradeLog*.htm
|
||||||
|
|||||||
17
FlexitimeUI/DBTest/DBTest.csproj
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Flexitime.DataAccess\Flexitime.DataAccess.csproj" />
|
||||||
|
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
97
FlexitimeUI/DBTest/Program.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Flexitime.DataAccess;
|
||||||
|
using Flexitime.DataAccess.EF;
|
||||||
|
using Flexitime.DataAccess.Objects;
|
||||||
|
using Flexitime.Interfaces;
|
||||||
|
|
||||||
|
namespace DBTest
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var dbconnprovider = new TestDbConnProvider();
|
||||||
|
|
||||||
|
var db = new Database(dbconnprovider);
|
||||||
|
|
||||||
|
//var users = db.Users;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//var userDb = new FlexitimeDbContext(dbconnprovider);
|
||||||
|
|
||||||
|
//userDb.Database.EnsureCreated();
|
||||||
|
|
||||||
|
//var users = userDb.Users.ToList();
|
||||||
|
|
||||||
|
//AddUser(userDb);
|
||||||
|
//AddIdentifier( userDb);
|
||||||
|
|
||||||
|
//AddTimeLog(userDb);
|
||||||
|
|
||||||
|
//users = userDb.Users.ToList();
|
||||||
|
//var tlogs = userDb.TimeLogs.ToList();
|
||||||
|
//var idents = userDb.Identifiers.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddIdentifier(FlexitimeDbContext flexitimeDb)
|
||||||
|
{
|
||||||
|
var ident = new IdentifierDb
|
||||||
|
{
|
||||||
|
UniqueId = Guid.NewGuid(),
|
||||||
|
LastUsed = DateTime.UtcNow,
|
||||||
|
IsAssociatedToUser = true
|
||||||
|
};
|
||||||
|
var createdEntry = flexitimeDb.Add(ident).Entity;
|
||||||
|
|
||||||
|
var user = flexitimeDb.Users.First();
|
||||||
|
|
||||||
|
user.AssociatedIdentifiers.Add(createdEntry);
|
||||||
|
|
||||||
|
flexitimeDb.Update(user);
|
||||||
|
|
||||||
|
flexitimeDb.SaveChanges();
|
||||||
|
flexitimeDb.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddUser(FlexitimeDbContext flexitimeDb)
|
||||||
|
{
|
||||||
|
var user = new UserDb
|
||||||
|
{
|
||||||
|
FirstName = "admin",
|
||||||
|
LastName = "admin",
|
||||||
|
UserName = "admin",
|
||||||
|
Password = "P@ssw0rd!",
|
||||||
|
IsContractor = false,
|
||||||
|
HoursPerWeek = 37.0
|
||||||
|
};
|
||||||
|
|
||||||
|
flexitimeDb.Add(user);
|
||||||
|
flexitimeDb.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddTimeLog(FlexitimeDbContext flexitimeDbContext)
|
||||||
|
{
|
||||||
|
var timelog = new TimeLogDb
|
||||||
|
{
|
||||||
|
LogTime = DateTimeOffset.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
var ident = flexitimeDbContext.Identifiers.First();
|
||||||
|
|
||||||
|
timelog.Identifier = ident;
|
||||||
|
timelog.IdentifierId = ident.Id;
|
||||||
|
|
||||||
|
flexitimeDbContext.Add(timelog);
|
||||||
|
|
||||||
|
flexitimeDbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestDbConnProvider : IConnectionStringProvider
|
||||||
|
{
|
||||||
|
public string ConnectionString =>
|
||||||
|
"Data Source=.\\SQLSERVER2019;Initial Catalog=FlexitimeData;Integrated Security=SSPI;";
|
||||||
|
}
|
||||||
|
}
|
||||||
228
FlexitimeUI/Flexitime.DataAccess/Database.cs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
using Flexitime.DataAccess.EF;
|
||||||
|
using Flexitime.DataAccess.Objects;
|
||||||
|
using Flexitime.Interfaces;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using LogSourceDescriptor = Flexitime.Objects.LogSourceDescriptor;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess
|
||||||
|
{
|
||||||
|
public class Database : IDataAccess
|
||||||
|
{
|
||||||
|
private FlexitimeDbContext db;
|
||||||
|
private IMapper mapper;
|
||||||
|
|
||||||
|
public Database(IConnectionStringProvider connectionStringProvider)
|
||||||
|
{
|
||||||
|
db = new FlexitimeDbContext(connectionStringProvider);
|
||||||
|
db.Database.EnsureCreated();
|
||||||
|
|
||||||
|
db.InsertBaseData();
|
||||||
|
|
||||||
|
mapper = MappingHelper.CreateMapper(new UserProfile(), new TimeLogProfile(), new GroupProfile(),
|
||||||
|
new TeamProfile(), new PermissionsProfile(), new ApplicationProfile(), new IdentifierProfile(), new LogDescriptorProfile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<List<User>> GetUsers()
|
||||||
|
{
|
||||||
|
return Task.Run(() => mapper.Map<List<User>>(db.Users
|
||||||
|
.Include(x => x.AssociatedIdentifiers)
|
||||||
|
.Include(x => x.DirectReports)
|
||||||
|
//.Include(x=>x.Groups)
|
||||||
|
//.Include(x=>x.Permissions)
|
||||||
|
.Include(x => x.Team)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<User> GetUserById(Guid id)
|
||||||
|
{
|
||||||
|
return Task.Run(()=>mapper.Map<User>(
|
||||||
|
db.Users.Where(x => x.Id == id)
|
||||||
|
.Include(x => x.AssociatedIdentifiers)
|
||||||
|
.Include(x => x.DirectReports)
|
||||||
|
//.Include(x=>x.Groups)
|
||||||
|
.Include(x => x.Permissions)
|
||||||
|
.Include(x => x.Team)
|
||||||
|
.FirstOrDefault()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> AddUser(User newUser)
|
||||||
|
{
|
||||||
|
if (newUser.Id != Guid.Empty)
|
||||||
|
{
|
||||||
|
var existingUser = await GetUserById(newUser.Id);
|
||||||
|
if (existingUser != null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newUser.Id = Guid.Empty;
|
||||||
|
|
||||||
|
var userToAdd = mapper.Map<UserDb>(newUser);
|
||||||
|
userToAdd.Id = Guid.Empty;
|
||||||
|
|
||||||
|
var result = db.Users.Add(userToAdd);
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return mapper.Map<User>(result.Entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<User> GetUserByUsername(string userName)
|
||||||
|
{
|
||||||
|
return Task.Run(() => mapper.Map<User>(
|
||||||
|
db.Users.Where(x => x.UserName == userName)
|
||||||
|
.Include(x => x.AssociatedIdentifiers)
|
||||||
|
.Include(x => x.DirectReports)
|
||||||
|
//.Include(x=>x.Groups)
|
||||||
|
.Include(x => x.Permissions)
|
||||||
|
.Include(x => x.Team)
|
||||||
|
.FirstOrDefault()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> UpdateUser(User user)
|
||||||
|
{
|
||||||
|
var userToUpdate = mapper.Map<UserDb>(user);
|
||||||
|
|
||||||
|
var existing = db.Users.FirstOrDefault(x => x.Id == user.Id);
|
||||||
|
|
||||||
|
existing.AssociatedIdentifiers = userToUpdate.AssociatedIdentifiers;
|
||||||
|
existing.LineManager = userToUpdate.LineManager;
|
||||||
|
existing.Permissions = userToUpdate.Permissions;
|
||||||
|
existing.UserName = userToUpdate.UserName;
|
||||||
|
existing.DirectReports = userToUpdate.DirectReports;
|
||||||
|
existing.FirstName = userToUpdate.FirstName;
|
||||||
|
existing.LastName = userToUpdate.LastName;
|
||||||
|
existing.IsContractor = userToUpdate.IsContractor;
|
||||||
|
existing.Groups = userToUpdate.Groups;
|
||||||
|
existing.HoursPerWeek = userToUpdate.HoursPerWeek;
|
||||||
|
existing.Password = userToUpdate.Password;
|
||||||
|
existing.Team = userToUpdate.Team;
|
||||||
|
existing.TeamId = userToUpdate.Team.Id;
|
||||||
|
|
||||||
|
db.Users.Update(existing);
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return await GetUserById(user.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class MappingHelper
|
||||||
|
{
|
||||||
|
public static IMapper CreateMapper(params Profile[] profiles)
|
||||||
|
{
|
||||||
|
var _profiles = profiles.ToList();
|
||||||
|
|
||||||
|
var mapperCfg = new MapperConfiguration(x => _profiles.ForEach(x.AddProfile));
|
||||||
|
|
||||||
|
mapperCfg.AssertConfigurationIsValid();
|
||||||
|
|
||||||
|
return new Mapper(mapperCfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserProfile : Profile
|
||||||
|
{
|
||||||
|
public UserProfile()
|
||||||
|
{
|
||||||
|
CreateMap<User, UserDb>()
|
||||||
|
.ForMember(x => x.State,
|
||||||
|
opt => opt.MapFrom(x => (int)x.State))
|
||||||
|
.ReverseMap();
|
||||||
|
|
||||||
|
CreateMap<UserDb, User>()
|
||||||
|
.ForMember(dst => dst.State,
|
||||||
|
opt => opt.MapFrom(x => Enum.Parse(typeof(UserState), x.State.ToString())))
|
||||||
|
.ForMember(dst => dst.DirectReportIds,
|
||||||
|
opt => opt.MapFrom(src => src.DirectReports.Select(x => x.Id)))
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TimeLogProfile : Profile
|
||||||
|
{
|
||||||
|
public TimeLogProfile()
|
||||||
|
{
|
||||||
|
CreateMap<TimeLog, TimeLogDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
|
||||||
|
CreateMap<TimeLogDb, TimeLog>()
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GroupProfile : Profile
|
||||||
|
{
|
||||||
|
public GroupProfile()
|
||||||
|
{
|
||||||
|
CreateMap<Group, GroupDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
CreateMap<GroupDb, Group>()
|
||||||
|
.ForMember(dst => dst.UserCount, opt => opt.Ignore())
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TeamProfile : Profile
|
||||||
|
{
|
||||||
|
public TeamProfile()
|
||||||
|
{
|
||||||
|
CreateMap<Team, TeamDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
CreateMap<TeamDb, Team>()
|
||||||
|
.ForMember(dst => dst.Users, opt => opt.MapFrom(src => src.Members.Users))
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PermissionsProfile : Profile
|
||||||
|
{
|
||||||
|
public PermissionsProfile()
|
||||||
|
{
|
||||||
|
CreateMap<Permission, PermissionDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
CreateMap<PermissionDb, Permission>()
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ApplicationProfile : Profile
|
||||||
|
{
|
||||||
|
public ApplicationProfile()
|
||||||
|
{
|
||||||
|
CreateMap<Application, ApplicationDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
CreateMap<ApplicationDb, Application>()
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IdentifierProfile : Profile
|
||||||
|
{
|
||||||
|
public IdentifierProfile()
|
||||||
|
{
|
||||||
|
CreateMap<Identifier, IdentifierDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
CreateMap<IdentifierDb, Identifier>()
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LogDescriptorProfile : Profile
|
||||||
|
{
|
||||||
|
public LogDescriptorProfile()
|
||||||
|
{
|
||||||
|
CreateMap<LogSourceDescriptor, LogSourceDescriptorDb>()
|
||||||
|
.ReverseMap();
|
||||||
|
CreateMap<LogSourceDescriptorDb, LogSourceDescriptor>()
|
||||||
|
.ReverseMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
FlexitimeUI/Flexitime.DataAccess/EF/FlexitimeDbContext.cs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Flexitime.DataAccess.Objects;
|
||||||
|
using Flexitime.Interfaces;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.EF
|
||||||
|
{
|
||||||
|
public class FlexitimeDbContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly IConnectionStringProvider _connectionStringProvider;
|
||||||
|
|
||||||
|
public FlexitimeDbContext(IConnectionStringProvider connectionStringProvider)
|
||||||
|
{
|
||||||
|
_connectionStringProvider = connectionStringProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<UserDb> Users { get; set; }
|
||||||
|
public DbSet<TimeLogDb> TimeLogs { get; set; }
|
||||||
|
public DbSet<IdentifierDb> Identifiers { get; set; }
|
||||||
|
public DbSet<ApplicationDb> Applications { get; set; }
|
||||||
|
public DbSet<GroupDb> Groups { get; set; }
|
||||||
|
public DbSet<TeamDb> Teams { get; set; }
|
||||||
|
public DbSet<LogSourceDescriptorDb> LogSources { get; set; }
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
optionsBuilder.UseSqlServer(_connectionStringProvider.ConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.ApplyConfiguration(new UserMap());
|
||||||
|
modelBuilder.ApplyConfiguration(new IdentifierMap());
|
||||||
|
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertBaseData()
|
||||||
|
{
|
||||||
|
AddAdminUser();
|
||||||
|
AddRootApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRootApplication()
|
||||||
|
{
|
||||||
|
var systemApp = new ApplicationDb
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Name = "System",
|
||||||
|
Permissions = new List<PermissionDb>
|
||||||
|
{
|
||||||
|
CreatePermission("Add/Edit User", "s.u.e"),
|
||||||
|
CreatePermission("Delete User", "s.u.d"),
|
||||||
|
CreatePermission("Add/Edit Group", "s.g.e"),
|
||||||
|
CreatePermission("Delete Group", "s.g.d"),
|
||||||
|
CreatePermission("Associate Identifier", "s.i.e")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var existingSystemApp = Applications.Where(x => x.Name == "System")
|
||||||
|
.Include(x=>x.Permissions)
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (existingSystemApp == null)
|
||||||
|
{
|
||||||
|
Applications.Add(systemApp);
|
||||||
|
|
||||||
|
var adminUser = Users.FirstOrDefault(x => x.UserName == "admin");
|
||||||
|
adminUser?.Permissions.AddRange(systemApp.Permissions);
|
||||||
|
|
||||||
|
SaveChanges();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var missingPermissions = systemApp.Permissions
|
||||||
|
.Where(x=> existingSystemApp.Permissions.All(y => y.Tag != x.Tag))
|
||||||
|
.ToList();
|
||||||
|
if (missingPermissions.Any())
|
||||||
|
{
|
||||||
|
existingSystemApp.Permissions.AddRange(missingPermissions);
|
||||||
|
Applications.Update(existingSystemApp);
|
||||||
|
SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PermissionDb CreatePermission(string name, string tag)
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Name = name,
|
||||||
|
Tag = tag
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAdminUser()
|
||||||
|
{
|
||||||
|
if (!Users.Any())
|
||||||
|
{
|
||||||
|
Users.Add(new UserDb
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
FirstName = "Admin",
|
||||||
|
LastName = "Admin",
|
||||||
|
Password = "P@ssw0rd!",
|
||||||
|
UserName = "admin"
|
||||||
|
});
|
||||||
|
SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserMap : IEntityTypeConfiguration<UserDb>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<UserDb> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("UserDb")
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.HasOne(x => x.LineManager)
|
||||||
|
.WithOne().OnDelete(DeleteBehavior.ClientSetNull);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.HasOne(x => x.Team)
|
||||||
|
.WithOne();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.HasMany(x => x.AssociatedIdentifiers)
|
||||||
|
.WithOne().OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IdentifierMap : IEntityTypeConfiguration<IdentifierDb>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<IdentifierDb> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("IdentifierDb")
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TimeLogsMap : IEntityTypeConfiguration<TimeLogDb>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<TimeLogDb> builder)
|
||||||
|
{
|
||||||
|
builder.ToTable("TimeLogDb")
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
243
FlexitimeUI/Flexitime.DataAccess/EF/FlexitimeDbContext.dgml
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<DirectedGraph GraphDirection="LeftToRight" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
|
||||||
|
<Nodes>
|
||||||
|
<Node Id="ApplicationDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="20,611.62,171.696666666667,141.92" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="ApplicationDb" Name="ApplicationDb" />
|
||||||
|
<Node Id="ApplicationDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="40,651.62,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="ApplicationDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="120,651.62,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="ApplicationDb.Permissions" Category="Navigation Collection" Bounds="40,707.58,97.5066666666667,25.96" Dependent="PermissionDb" Field="" Inverse="Application" Label="Permissions (*)" Name="Permissions" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<PermissionDb>" />
|
||||||
|
<Node Id="GroupDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="834.158222459071,780.113078871474,200.160049641927,197.8804" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="GroupDb" Name="GroupDb" UseManualLocation="True" />
|
||||||
|
<Node Id="GroupDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="854.158192588721,820.113261937001,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="GroupDb.IsPrivate" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="854.158188519711,876.073344944814,64.2833333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IsPrivate" MaxLength="None" Name="IsPrivate" PropertyAccessMode="PropertyAccessMode.Default" Type="bool" ValueGenerated="None" />
|
||||||
|
<Node Id="GroupDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="934.158369997576,820.113261937001,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="GroupDb.Owners" Category="Navigation Property" Bounds="854.158186485206,932.033427952626,77.0500000000001,25.96" Dependent="GroupOwners" Field="" Inverse="Group" Label="Owners (1)" Name="Owners" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="GroupOwners" />
|
||||||
|
<Node Id="GroupDb.Users" Category="Navigation Property" Bounds="948.441752565935,876.073344944814,65.8766666666667,25.96" Dependent="" Field="" Inverse="Groups" Label="Users (1)" Name="Users" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<UserDb>" />
|
||||||
|
<Node Id="GroupDbUserDb" Category="EntityType" Annotations="" BaseClass="" Bounds="649.848354695638,306.756624112899,128,141.9203" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="GroupDbUserDb" Name="GroupDbUserDb" />
|
||||||
|
<Node Id="GroupDbUserDb.GroupsId" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="679.53169148763,402.716824112899,68.6333333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="GroupsId" MaxLength="None" Name="GroupsId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
|
||||||
|
<Node Id="GroupDbUserDb.UsersId" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="679.531684570312,346.756724112899,59.14,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="UsersId" MaxLength="None" Name="UsersId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
|
||||||
|
<Node Id="GroupOwners" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="541.465026855469,821.136654557235,210.766606445312,141.920344042969" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="GroupOwners" Name="GroupOwners" />
|
||||||
|
<Node Id="GroupOwners.Group" Category="Navigation Property" Bounds="561.465029703776,917.096898600204,70.2766666666666,25.96" Dependent="" Field="" Inverse="Owners" Label="Group (1)" Name="Group" Principal="GroupDb" PropertyAccessMode="PropertyAccessMode.Default" Type="GroupDb" />
|
||||||
|
<Node Id="GroupOwners.GroupId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="641.465015869141,861.136754557235,63.54,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="GroupId" MaxLength="None" Name="GroupId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
|
||||||
|
<Node Id="GroupOwners.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping] SqlServer:ValueGenerationStrategy: IdentityColumn" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="561.465026855469,861.136754557235,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="int" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="GroupOwners.Owner" Category="Navigation Collection" Bounds="661.741633300781,917.096898600204,70.49,25.96" Dependent="UserDb" Field="" Inverse="" Label="Owner (*)" Name="Owner" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<UserDb>" />
|
||||||
|
<Node Id="IModel" Category="Model" Annotations="Relational:MaxIdentifierLength: 128 Relational:RelationalModel: Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalModel SqlServer:ValueGenerationStrategy: IdentityColumn" Bounds="-1.13686837721616E-13,-5.06377059229567,1078.46166666667,1050.42406059802" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" Label="FlexitimeDbContext" ProductVersion="5.0.5" PropertyAccessMode="PropertyAccessMode.Default" />
|
||||||
|
<Node Id="IdentifierDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="411.92497639974,253.455354581649,207.846735432943,197.880413525391" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="IdentifierDb" Name="IdentifierDb" />
|
||||||
|
<Node Id="IdentifierDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="431.924987792969,293.455471573836,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="IdentifierDb.IsAssociatedToUser" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="431.92497639974,405.37566810704,121.143333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IsAssociatedToUser" MaxLength="None" Name="IsAssociatedToUser" PropertyAccessMode="PropertyAccessMode.Default" Type="bool" ValueGenerated="None" />
|
||||||
|
<Node Id="IdentifierDb.LastUsed" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="511.924994303385,293.455454581649,67.5966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LastUsed" MaxLength="None" Name="LastUsed" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTime" ValueGenerated="None" />
|
||||||
|
<Node Id="IdentifierDb.UniqueId" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="431.924992879232,349.415585099227,68.3333333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="UniqueId" MaxLength="None" Name="UniqueId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
|
||||||
|
<Node Id="IdentifierDb.UserDbId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="530.258378499349,349.41556810704,69.5133333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="UserDbId" MaxLength="None" Name="UserDbId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<Guid>" ValueGenerated="None" />
|
||||||
|
<Node Id="LogSourceDescriptor" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="807.906666666667,283.84,207.883333333333,197.88" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="LogSourceDescriptor" Name="LogSourceDescriptor" />
|
||||||
|
<Node Id="LogSourceDescriptor.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping] SqlServer:ValueGenerationStrategy: IdentityColumn" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="827.906666666667,323.84,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="int" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="LogSourceDescriptor.LocalDateTime" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="827.906666666667,435.76,97.9066666666666,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LocalDateTime" MaxLength="None" Name="LocalDateTime" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTimeOffset" ValueGenerated="None" />
|
||||||
|
<Node Id="LogSourceDescriptor.SourceApiKey" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="827.906666666667,379.8,92.7733333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="SourceApiKey" MaxLength="None" Name="SourceApiKey" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="LogSourceDescriptor.SourceName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="907.906666666667,323.84,87.8833333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="SourceName" MaxLength="None" Name="SourceName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="PermissionDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="130,253.455351981552,251.696660970052,197.880416992188" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="PermissionDb" Name="PermissionDb" />
|
||||||
|
<Node Id="PermissionDb.Application" Category="Navigation Property" Bounds="150.000003051758,405.375668973739,97.1,25.96" Dependent="" Field="" Inverse="Permissions" Label="Application (1)" Name="Application" Principal="ApplicationDb" PropertyAccessMode="PropertyAccessMode.Default" Type="ApplicationDb" />
|
||||||
|
<Node Id="PermissionDb.ApplicationId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="249.513340657552,349.41556810704,90.3633333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="ApplicationId" MaxLength="None" Name="ApplicationId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<Guid>" ValueGenerated="None" />
|
||||||
|
<Node Id="PermissionDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="150,293.455468973739,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="PermissionDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="309.999994303385,293.455454581649,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="PermissionDb.Tag" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="230,293.455454581649,49.9999999999999,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Tag" MaxLength="None" Name="Tag" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="PermissionDb.UserDbId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="150.000001424153,349.415568973739,69.5133333333334,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="UserDbId" MaxLength="None" Name="UserDbId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<Guid>" ValueGenerated="None" />
|
||||||
|
<Node Id="TeamDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="339,883.440000000001,171.696666666667,141.92" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="TeamDb" Name="TeamDb" />
|
||||||
|
<Node Id="TeamDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="359,923.440000000001,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="TeamDb.Name" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="439,923.440000000001,51.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Name" MaxLength="None" Name="Name" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="TeamDb.Users" Category="Navigation Collection" Bounds="359,979.400000000001,64.41,25.96" Dependent="UserDb" Field="" Inverse="Team" Label="Users (*)" Name="Users" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<UserDb>" />
|
||||||
|
<Node Id="TimeLogDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] Relational:TableName: TimeLogs ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="765.234983723958,34.9364225870201,293.226682942709,197.8804" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="TimeLogDb" Name="TimeLogDb" />
|
||||||
|
<Node Id="TimeLogDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="785.234985351562,74.9365225870201,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="TimeLogDb.Identifier" Category="Navigation Property" Bounds="785.235002441406,130.89662258702,84.41,25.96" Dependent="" Field="" Inverse="" Label="Identifier (1)" Name="Identifier" Principal="IdentifierDb" PropertyAccessMode="PropertyAccessMode.Default" Type="IdentifierDb" />
|
||||||
|
<Node Id="TimeLogDb.IdentifierId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="960.788333333334,74.9365225870201,77.6733333333331,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IdentifierId" MaxLength="None" Name="IdentifierId" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="None" />
|
||||||
|
<Node Id="TimeLogDb.LogTime" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="865.234991048177,74.9365225870201,65.5533333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LogTime" MaxLength="None" Name="LogTime" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTimeOffset" ValueGenerated="None" />
|
||||||
|
<Node Id="TimeLogDb.SourceDescriptor" Category="Navigation Property" Bounds="785.234983723958,186.85672258702,127.913333333333,25.96" Dependent="" Field="" Inverse="" Label="SourceDescriptor (1)" Name="SourceDescriptor" Principal="LogSourceDescriptor" PropertyAccessMode="PropertyAccessMode.Default" Type="LogSourceDescriptor" />
|
||||||
|
<Node Id="TimeLogDb.SourceDescriptorId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="899.645004069011,130.89662258702,121.176666666667,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="SourceDescriptorId" MaxLength="None" Name="SourceDescriptorId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<int>" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb" Category="EntityType" Annotations="ConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding Relational:DefaultMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMappingBase] Relational:TableMappings: System.Collections.Generic.List`1[Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping] Relational:TableName: Users ServiceOnlyConstructorBinding: Microsoft.EntityFrameworkCore.Metadata.ConstructorBinding" BaseClass="" Bounds="279.384977914315,481.335910514266,472.926718207467,309.800644042969" ChangeTrackingStrategy="ChangeTrackingStrategy.Snapshot" Group="Expanded" IsAbstract="False" Label="UserDb" Name="UserDb" />
|
||||||
|
<Node Id="UserDb.AssociatedIdentifiers" Category="Navigation Collection" Bounds="587.351693454319,745.176454557235,144.96,25.96" Dependent="IdentifierDb" Field="" Inverse="" Label="AssociatedIdentifiers (*)" Name="AssociatedIdentifiers" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<IdentifierDb>" />
|
||||||
|
<Node Id="UserDb.DirectReports" Category="Navigation Collection" Bounds="299.385004001194,745.176454557235,107.43,25.96" Dependent="UserDb" Field="" Inverse="" Label="DirectReports (*)" Name="DirectReports" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<UserDb>" />
|
||||||
|
<Node Id="UserDb.FirstName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="401.998318888346,577.296110514266,73.7966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="FirstName" MaxLength="None" Name="FirstName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.GroupOwnersId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="428.564997151693,689.216310514267,103.593333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="GroupOwnersId" MaxLength="None" Name="GroupOwnersId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<int>" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.Groups" Category="Navigation Property" Bounds="505.795026245117,577.296110514266,75.37,25.96" Dependent="" Field="" Inverse="Users" Label="Groups (1)" Name="Groups" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<GroupDb>" />
|
||||||
|
<Node Id="UserDb.HoursPerWeek" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="541.58833396629,633.256210514267,97.59,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="HoursPerWeek" MaxLength="None" Name="HoursPerWeek" PropertyAccessMode="PropertyAccessMode.Default" Type="float" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.Id" Category="Property Primary" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.384979248047,521.336010514266,50,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="True" IsRequired="True" IsShadow="False" IsUnicode="True" Label="Id" MaxLength="None" Name="Id" PropertyAccessMode="PropertyAccessMode.Default" Type="Guid" ValueGenerated="ValueGenerated.OnAdd" />
|
||||||
|
<Node Id="UserDb.IsContractor" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.385000632957,633.256210514266,84.6966666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="IsContractor" MaxLength="None" Name="IsContractor" PropertyAccessMode="PropertyAccessMode.Default" Type="bool" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.LastEventDateTime" Category="Property Required" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="436.815008070205,745.176454557235,120.536666666667,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="True" IsShadow="False" IsUnicode="True" Label="LastEventDateTime" MaxLength="None" Name="LastEventDateTime" PropertyAccessMode="PropertyAccessMode.Default" Type="DateTime" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.LastName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.384983723958,577.296110514266,72.6133333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="LastName" MaxLength="None" Name="LastName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.LineManager" Category="Navigation Property" Bounds="562.158335367839,689.216310514267,105.916666666667,25.96" Dependent="" Field="" Inverse="" Label="LineManager (1)" Name="LineManager" Principal="UserDb" PropertyAccessMode="PropertyAccessMode.Default" Type="UserDb" />
|
||||||
|
<Node Id="UserDb.LineManagerId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="299.385006103516,689.216310514266,99.18,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="LineManagerId" MaxLength="None" Name="LineManagerId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<Guid>" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.Password" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="661.828379652236,521.336010514266,69.5466666666666,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="Password" MaxLength="None" Name="Password" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.Permissions" Category="Navigation Collection" Bounds="414.081667299624,633.256210514266,97.5066666666667,25.96" Dependent="PermissionDb" Field="" Inverse="" Label="Permissions (*)" Name="Permissions" Principal="" PropertyAccessMode="PropertyAccessMode.Default" Type="List<PermissionDb>" />
|
||||||
|
<Node Id="UserDb.Team" Category="Navigation Property" Bounds="467.48166402181,521.336010514266,64.8333333333333,25.96" Dependent="" Field="" Inverse="Users" Label="Team (1)" Name="Team" Principal="TeamDb" PropertyAccessMode="PropertyAccessMode.Default" Type="TeamDb" />
|
||||||
|
<Node Id="UserDb.TeamId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="379.385016276042,521.336010514266,58.0966666666667,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="TeamId" MaxLength="None" Name="TeamId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<Guid>" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.UserDbId" Category="Property Foreign" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="562.315019124349,521.336010514266,69.5133333333333,25.96" Field="nofield" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="True" IsIndexed="True" IsPrimaryKey="False" IsRequired="False" IsShadow="True" IsUnicode="True" Label="UserDbId" MaxLength="None" Name="UserDbId" PropertyAccessMode="PropertyAccessMode.Default" Type="Nullable<Guid>" ValueGenerated="None" />
|
||||||
|
<Node Id="UserDb.UserName" Category="Property Optional" AfterSaveBehavior="PropertySaveBehavior.Save" Annotations="Relational:DefaultColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMappingBase] Relational:TableColumnMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ColumnMapping]" BeforeSaveBehavior="PropertySaveBehavior.Save" Bounds="611.165041097005,577.296110514267,75.4833333333333,25.96" Field="" IsAlternateKey="False" IsConcurrencyToken="False" IsForeignKey="False" IsIndexed="False" IsPrimaryKey="False" IsRequired="False" IsShadow="False" IsUnicode="True" Label="UserName" MaxLength="None" Name="UserName" PropertyAccessMode="PropertyAccessMode.Default" Type="string" ValueGenerated="None" />
|
||||||
|
</Nodes>
|
||||||
|
<Links>
|
||||||
|
<Link Source="ApplicationDb" Target="ApplicationDb.Id" Category="Contains" />
|
||||||
|
<Link Source="ApplicationDb" Target="ApplicationDb.Name" Category="Contains" />
|
||||||
|
<Link Source="ApplicationDb" Target="ApplicationDb.Permissions" Category="Contains" />
|
||||||
|
<Link Source="GroupDb" Target="GroupDb.Id" Category="Contains" />
|
||||||
|
<Link Source="GroupDb" Target="GroupDb.IsPrivate" Category="Contains" />
|
||||||
|
<Link Source="GroupDb" Target="GroupDb.Name" Category="Contains" />
|
||||||
|
<Link Source="GroupDb" Target="GroupDb.Owners" Category="Contains" />
|
||||||
|
<Link Source="GroupDb" Target="GroupDb.Users" Category="Contains" />
|
||||||
|
<Link Source="GroupDbUserDb" Target="GroupDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="745.042771395463,448.676924112899,142.078978384132,323.197119375579" From="GroupDbUserDb.GroupsId" IsUnique="False" Label="1:*" LabelBounds="783.503404761899,515.236310602442,14.0733333333333,15.96" Name="GroupDbUserDb -> GroupDb" To="GroupDb.Id" />
|
||||||
|
<Link Source="GroupDbUserDb" Target="GroupDbUserDb.GroupsId" Category="Contains" />
|
||||||
|
<Link Source="GroupDbUserDb" Target="GroupDbUserDb.UsersId" Category="Contains" />
|
||||||
|
<Link Source="GroupDbUserDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="639.958904952466,448.676924112899,19.5410806102647,25.5138844714087" From="GroupDbUserDb.UsersId" IsUnique="False" Label="1:*" LabelBounds="651.317245686478,462.64996247887,14.0733333333334,15.96" Name="GroupDbUserDb -> UserDb" To="UserDb.Id" />
|
||||||
|
<Link Source="GroupOwners" Target="GroupDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="752.231633300781,884.003590975451,72.9358444603664,3.31028372898231" From="GroupOwners.GroupId" IsUnique="True" Label="1:1" LabelBounds="780.838876398715,865.698730722688,15.54,15.96" Name="GroupOwners -> GroupDb" To="GroupDb.Id" />
|
||||||
|
<Link Source="GroupOwners" Target="GroupOwners.Group" Category="Contains" />
|
||||||
|
<Link Source="GroupOwners" Target="GroupOwners.GroupId" Category="Contains" />
|
||||||
|
<Link Source="GroupOwners" Target="GroupOwners.Id" Category="Contains" />
|
||||||
|
<Link Source="GroupOwners" Target="GroupOwners.Owner" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="ApplicationDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="GroupDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="GroupDbUserDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="GroupOwners" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="IdentifierDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="LogSourceDescriptor" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="PermissionDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="TeamDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="TimeLogDb" Category="Contains" />
|
||||||
|
<Link Source="IModel" Target="UserDb" Category="Contains" />
|
||||||
|
<Link Source="IdentifierDb" Target="IdentifierDb.Id" Category="Contains" />
|
||||||
|
<Link Source="IdentifierDb" Target="IdentifierDb.IsAssociatedToUser" Category="Contains" />
|
||||||
|
<Link Source="IdentifierDb" Target="IdentifierDb.LastUsed" Category="Contains" />
|
||||||
|
<Link Source="IdentifierDb" Target="IdentifierDb.UniqueId" Category="Contains" />
|
||||||
|
<Link Source="IdentifierDb" Target="IdentifierDb.UserDbId" Category="Contains" />
|
||||||
|
<Link Source="IdentifierDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="515.848341116795,451.33576810704,5.25162363373965E-07,21.0001424072267" From="IdentifierDb.UserDbId" IsUnique="False" Label="1:*" LabelBounds="519.848341379377,453.855839360668,14.0733333333333,15.96" Name="IdentifierDb -> UserDb" To="UserDb.Id" />
|
||||||
|
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.Id" Category="Contains" />
|
||||||
|
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.LocalDateTime" Category="Contains" />
|
||||||
|
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.SourceApiKey" Category="Contains" />
|
||||||
|
<Link Source="LogSourceDescriptor" Target="LogSourceDescriptor.SourceName" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="ApplicationDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="141.807363012455,451.335768973739,69.0932678957193,152.09014912077" From="PermissionDb.ApplicationId" IsUnique="False" Label="1:*" LabelBounds="178.174904050424,528.208066218285,14.0733333333333,15.96" Name="PermissionDb -> ApplicationDb" To="ApplicationDb.Id" />
|
||||||
|
<Link Source="PermissionDb" Target="PermissionDb.Application" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="PermissionDb.ApplicationId" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="PermissionDb.Id" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="PermissionDb.Name" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="PermissionDb.Tag" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="PermissionDb.UserDbId" Category="Contains" />
|
||||||
|
<Link Source="PermissionDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="346.478241515172,451.335768973739,21.4011939896662,23.3635735855307" From="PermissionDb.UserDbId" IsUnique="False" Label="1:*" LabelBounds="341.630712297784,464.36847618029,14.0733333333333,15.96" Name="PermissionDb -> UserDb" To="UserDb.Id" />
|
||||||
|
<Link Source="TeamDb" Target="TeamDb.Id" Category="Contains" />
|
||||||
|
<Link Source="TeamDb" Target="TeamDb.Name" Category="Contains" />
|
||||||
|
<Link Source="TeamDb" Target="TeamDb.Users" Category="Contains" />
|
||||||
|
<Link Source="TimeLogDb" Target="IdentifierDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="627.65160636805,214.780141063669,137.583377355908,75.92064405795" From="TimeLogDb.IdentifierId" IsUnique="False" Label="1:*" LabelBounds="697.409572416184,254.49155076717,14.0733333333333,15.96" Name="TimeLogDb -> IdentifierDb" To="IdentifierDb.Id" />
|
||||||
|
<Link Source="TimeLogDb" Target="LogSourceDescriptor" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="911.848328430212,232.81682258702,1.37396887112118E-06,42.0231774129799" From="TimeLogDb.SourceDescriptorId" IsUnique="False" Label="1:*" LabelBounds="915.848329117196,245.848411228119,14.0733333333333,15.96" Name="TimeLogDb -> LogSourceDescriptor" To="LogSourceDescriptor.Id" />
|
||||||
|
<Link Source="TimeLogDb" Target="TimeLogDb.Id" Category="Contains" />
|
||||||
|
<Link Source="TimeLogDb" Target="TimeLogDb.Identifier" Category="Contains" />
|
||||||
|
<Link Source="TimeLogDb" Target="TimeLogDb.IdentifierId" Category="Contains" />
|
||||||
|
<Link Source="TimeLogDb" Target="TimeLogDb.LogTime" Category="Contains" />
|
||||||
|
<Link Source="TimeLogDb" Target="TimeLogDb.SourceDescriptor" Category="Contains" />
|
||||||
|
<Link Source="TimeLogDb" Target="TimeLogDb.SourceDescriptorId" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="GroupOwners" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="595.15691982549,791.136554557235,11.258348353236,21.9890675588098" From="UserDb.GroupOwnersId" IsUnique="False" Label="1:*" LabelBounds="584.932531237399,803.042561414501,14.0733333333334,15.96" Name="UserDb -> GroupOwners" To="GroupOwners.Id" />
|
||||||
|
<Link Source="UserDb" Target="TeamDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="447.618951206691,791.136554557235,23.9253785164266,83.6504204238083" From="UserDb.TeamId" IsUnique="False" Label="1:*" LabelBounds="463.584732296623,826.238739302674,14.0733333333334,15.96" Name="UserDb -> TeamDb" To="TeamDb.Id" />
|
||||||
|
<Link Source="UserDb" Target="UserDb" Category="Foreign Key" Annotations="Relational:ForeignKeyMappings: System.Collections.Generic.SortedSet`1[Microsoft.EntityFrameworkCore.Metadata.Internal.ForeignKeyConstraint]" Bounds="419.513458251953,451.335906982422,186.029510498047,30" From="UserDb.UserDbId" IsUnique="False" Label="1:*" LabelBounds="500.600439099212,455.453098361185,14.0733333333333,15.96" Name="UserDb -> UserDb" To="UserDb.Id" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.AssociatedIdentifiers" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.DirectReports" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.FirstName" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.GroupOwnersId" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.Groups" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.HoursPerWeek" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.Id" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.IsContractor" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.LastEventDateTime" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.LastName" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.LineManager" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.LineManagerId" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.Password" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.Permissions" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.Team" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.TeamId" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.UserDbId" Category="Contains" />
|
||||||
|
<Link Source="UserDb" Target="UserDb.UserName" Category="Contains" />
|
||||||
|
</Links>
|
||||||
|
<Categories>
|
||||||
|
<Category Id="Contains" Label="Contains" Description="Whether the source of the link contains the target object" CanBeDataDriven="False" CanLinkedNodesBeDataDriven="True" IncomingActionLabel="Contained By" IsContainment="True" OutgoingActionLabel="Contains" />
|
||||||
|
<Category Id="EntityType" />
|
||||||
|
<Category Id="Foreign Key" />
|
||||||
|
<Category Id="Model" />
|
||||||
|
<Category Id="Navigation Collection" />
|
||||||
|
<Category Id="Navigation Property" />
|
||||||
|
<Category Id="Property Foreign" />
|
||||||
|
<Category Id="Property Optional" />
|
||||||
|
<Category Id="Property Primary" />
|
||||||
|
<Category Id="Property Required" />
|
||||||
|
</Categories>
|
||||||
|
<Properties>
|
||||||
|
<Property Id="AfterSaveBehavior" Group="Property Flags" DataType="System.String" />
|
||||||
|
<Property Id="Annotations" Description="Annotations" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="BaseClass" Description="Base class" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="BeforeSaveBehavior" Group="Property Flags" DataType="System.String" />
|
||||||
|
<Property Id="Bounds" DataType="System.Windows.Rect" />
|
||||||
|
<Property Id="CanBeDataDriven" Label="CanBeDataDriven" Description="CanBeDataDriven" DataType="System.Boolean" />
|
||||||
|
<Property Id="CanLinkedNodesBeDataDriven" Label="CanLinkedNodesBeDataDriven" Description="CanLinkedNodesBeDataDriven" DataType="System.Boolean" />
|
||||||
|
<Property Id="ChangeTrackingStrategy" Description="Change tracking strategy" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="Dependent" Description="Dependent entity" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="Expression" DataType="System.String" />
|
||||||
|
<Property Id="Field" Description="Backing field" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="From" Description="Target property" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="GraphDirection" DataType="Microsoft.VisualStudio.Diagrams.Layout.LayoutOrientation" />
|
||||||
|
<Property Id="Group" Label="Group" Description="Display the node as a group" DataType="Microsoft.VisualStudio.GraphModel.GraphGroupStyle" />
|
||||||
|
<Property Id="GroupLabel" DataType="System.String" />
|
||||||
|
<Property Id="IncomingActionLabel" Label="IncomingActionLabel" Description="IncomingActionLabel" DataType="System.String" />
|
||||||
|
<Property Id="Inverse" Description="Inverse entity" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="IsAbstract" Label="IsAbstract" Description="IsAbstract" Group="Model Properties" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsAlternateKey" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsConcurrencyToken" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsContainment" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsEnabled" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsForeignKey" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsIndexed" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsPrimaryKey" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsRequired" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsShadow" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsUnicode" Group="Property Flags" DataType="System.Boolean" />
|
||||||
|
<Property Id="IsUnique" Group="Model Properties" DataType="System.Boolean" />
|
||||||
|
<Property Id="Label" Label="Label" Description="Displayable label of an Annotatable object" DataType="System.String" />
|
||||||
|
<Property Id="LabelBounds" DataType="System.Windows.Rect" />
|
||||||
|
<Property Id="MaxLength" DataType="System.String" />
|
||||||
|
<Property Id="Name" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="OutgoingActionLabel" Label="OutgoingActionLabel" Description="OutgoingActionLabel" DataType="System.String" />
|
||||||
|
<Property Id="Principal" Description="Principal entity" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="ProductVersion" Label="Product Version" Description="EF Core product version" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="PropertyAccessMode" Group="Property Flags" DataType="System.String" />
|
||||||
|
<Property Id="TargetType" DataType="System.Type" />
|
||||||
|
<Property Id="To" Description="Source property" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="Type" Description="CLR data type" Group="Model Properties" DataType="System.String" />
|
||||||
|
<Property Id="UseManualLocation" DataType="System.Boolean" />
|
||||||
|
<Property Id="Value" DataType="System.String" />
|
||||||
|
<Property Id="ValueGenerated" Group="Property Flags" DataType="System.String" />
|
||||||
|
<Property Id="ValueLabel" DataType="System.String" />
|
||||||
|
</Properties>
|
||||||
|
<Styles>
|
||||||
|
<Style TargetType="Node" GroupLabel="EntityType" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('EntityType')" />
|
||||||
|
<Setter Property="Background" Value="#FFC0C0C0" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Property Primary" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Property Primary')" />
|
||||||
|
<Setter Property="Background" Value="#FF008000" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Property Optional" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Property Optional')" />
|
||||||
|
<Setter Property="Background" Value="#FF808040" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Property Foreign" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Property Foreign')" />
|
||||||
|
<Setter Property="Background" Value="#FF8080FF" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Property Required" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Property Required')" />
|
||||||
|
<Setter Property="Background" Value="#FFC0A000" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Navigation Property" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Navigation Property')" />
|
||||||
|
<Setter Property="Background" Value="#FF990000" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Navigation Collection" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Navigation Collection')" />
|
||||||
|
<Setter Property="Background" Value="#FFFF3232" />
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="Node" GroupLabel="Model" ValueLabel="True">
|
||||||
|
<Condition Expression="HasCategory('Model')" />
|
||||||
|
<Setter Property="Background" Value="#FFFFFFFF" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
|
</DirectedGraph>
|
||||||
21
FlexitimeUI/Flexitime.DataAccess/Flexitime.DataAccess.csproj
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AutoMapper" Version="10.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
21
FlexitimeUI/Flexitime.DataAccess/Objects/ApplicationDb.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class ApplicationDb
|
||||||
|
{
|
||||||
|
public ApplicationDb()
|
||||||
|
{
|
||||||
|
Permissions = new List<PermissionDb>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public List<PermissionDb> Permissions { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
25
FlexitimeUI/Flexitime.DataAccess/Objects/GroupDb.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class GroupDb
|
||||||
|
{
|
||||||
|
public GroupDb()
|
||||||
|
{
|
||||||
|
IsPrivate = false;
|
||||||
|
Users = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public bool IsPrivate { get; set; }
|
||||||
|
public List<UserDb> Users { get; set; }
|
||||||
|
public GroupOwners Owners { get; set; }
|
||||||
|
public List<PermissionDb> Permissions { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
FlexitimeUI/Flexitime.DataAccess/Objects/GroupOwners.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class GroupOwners
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Guid GroupId { get; set; }
|
||||||
|
public GroupDb Group { get; set; }
|
||||||
|
public List<UserDb> Owner { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
FlexitimeUI/Flexitime.DataAccess/Objects/IdentifierDb.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class IdentifierDb
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid UniqueId { get; set; }
|
||||||
|
public bool IsAssociatedToUser { get; set; }
|
||||||
|
public DateTime LastUsed { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class LogSourceDescriptorDb
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string SourceName { get; set; }
|
||||||
|
public DateTimeOffset LocalDateTime { get; set; }
|
||||||
|
public string SourceApiKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
FlexitimeUI/Flexitime.DataAccess/Objects/PermissionDb.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class PermissionDb
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Tag { get; set; }
|
||||||
|
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
FlexitimeUI/Flexitime.DataAccess/Objects/TeamDb.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class TeamDb
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public TeamMembers Members{ get; set; }
|
||||||
|
//team owner/manager?
|
||||||
|
public UserDb Manager { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
15
FlexitimeUI/Flexitime.DataAccess/Objects/TeamMembers.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class TeamMembers
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Guid TeamId { get; set; }
|
||||||
|
public TeamDb Team { get; set; }
|
||||||
|
public List<UserDb> Users { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
20
FlexitimeUI/Flexitime.DataAccess/Objects/TimeLogDb.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class TimeLogDb
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
public Guid IdentifierId { get; set; }
|
||||||
|
public IdentifierDb Identifier { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset LogTime { get; set; }
|
||||||
|
|
||||||
|
public LogSourceDescriptorDb SourceDescriptor { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
62
FlexitimeUI/Flexitime.DataAccess/Objects/UserDb.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Flexitime.DataAccess.Objects
|
||||||
|
{
|
||||||
|
public class UserDb
|
||||||
|
{
|
||||||
|
public UserDb()
|
||||||
|
{
|
||||||
|
AssociatedIdentifiers = new List<IdentifierDb>();
|
||||||
|
Groups = new List<GroupDb>();
|
||||||
|
DirectReports = new List<UserDb>();
|
||||||
|
Permissions = new List<PermissionDb>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Key]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
public double HoursPerWeek { get; set; }
|
||||||
|
|
||||||
|
public bool IsContractor { get; set; }
|
||||||
|
|
||||||
|
public int AssociatedIdentifierCount => AssociatedIdentifiers.Count;
|
||||||
|
|
||||||
|
public DateTime LastEventDateTime { get; set; }
|
||||||
|
|
||||||
|
public List<IdentifierDb> AssociatedIdentifiers { get; set; }
|
||||||
|
|
||||||
|
//TODO: Is this that the user is in the group or this is their groups?
|
||||||
|
public List<GroupDb> Groups { get; set; }
|
||||||
|
public int State { get; set; }
|
||||||
|
public Guid? TeamId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// user that belongs to team
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>differs from Groups in that a group is a symbolic collection where a team is a publicly identifiable entity</remarks>
|
||||||
|
public TeamDb Team { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id of the Users Line Manager
|
||||||
|
/// </summary>
|
||||||
|
public UserDb LineManager { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ids of the users direct reports
|
||||||
|
/// </summary>
|
||||||
|
public List<UserDb> DirectReports { get; set; }
|
||||||
|
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public List<PermissionDb> Permissions { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Flexitime.Objects\Flexitime.Objects.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Flexitime.Interfaces
|
||||||
|
{
|
||||||
|
public interface IConnectionStringProvider
|
||||||
|
{
|
||||||
|
string ConnectionString { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
FlexitimeUI/Flexitime.Interfaces/IDataAccess.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace Flexitime.Interfaces
|
||||||
|
{
|
||||||
|
public interface IDataAccess
|
||||||
|
{
|
||||||
|
Task<List<User>> GetUsers();
|
||||||
|
|
||||||
|
Task<User> GetUserById(Guid id);
|
||||||
|
Task<User> AddUser(User newUser);
|
||||||
|
Task<User> GetUserByUsername(string userName);
|
||||||
|
Task<User> UpdateUser(User user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,4 @@
|
|||||||
using System;
|
namespace Flexitime.Interfaces
|
||||||
|
|
||||||
namespace Flexitime.Interfaces
|
|
||||||
{
|
{
|
||||||
public interface ITokenFactory
|
public interface ITokenFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects.API
|
||||||
|
{
|
||||||
|
public class AssociateIdentifierRequest
|
||||||
|
{
|
||||||
|
public Identifier Identifier { get; set; }
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
namespace Flexitime.Objects.API
|
||||||
|
{
|
||||||
|
public class UsernameValidCheckRequest
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Flexitime.Objects
|
namespace Flexitime.Objects
|
||||||
{
|
{
|
||||||
@ -9,7 +10,7 @@ namespace Flexitime.Objects
|
|||||||
Permissions = new List<Permission>();
|
Permissions = new List<Permission>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Id { get; set; } //TODO make this GUID
|
public Guid Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public List<Permission> Permissions { get; set; }
|
public List<Permission> Permissions { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,19 @@
|
|||||||
namespace Flexitime.Objects
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
{
|
{
|
||||||
public class Group
|
public class Group
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public Group()
|
||||||
|
{
|
||||||
|
IsPrivate = false;
|
||||||
|
Users = new List<User>();
|
||||||
|
}
|
||||||
|
public Guid Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int UserCount { get; set; }
|
public int UserCount { get; set; }
|
||||||
public bool IsAssociatedToUser { get; set; }
|
public bool IsPrivate { get; set; }
|
||||||
|
public List<User> Users { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,7 +5,7 @@ namespace Flexitime.Objects
|
|||||||
public class Identifier
|
public class Identifier
|
||||||
{
|
{
|
||||||
public Identifier() { }
|
public Identifier() { }
|
||||||
public int Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public string UniqueId { get; set; }
|
public string UniqueId { get; set; }
|
||||||
public bool IsAssociatedToUser { get; set; }
|
public bool IsAssociatedToUser { get; set; }
|
||||||
public DateTime LastUsed { get; set; }
|
public DateTime LastUsed { get; set; }
|
||||||
|
|||||||
11
FlexitimeUI/Flexitime.Objects/LogSourceDescriptor.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
|
{
|
||||||
|
public class LogSourceDescriptor
|
||||||
|
{
|
||||||
|
public string SourceName { get; set; }
|
||||||
|
public DateTimeOffset LocalDateTime { get; set; }
|
||||||
|
public string SourceApiKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,11 +4,8 @@ namespace Flexitime.Objects
|
|||||||
{
|
{
|
||||||
public class LoginRequest
|
public class LoginRequest
|
||||||
{
|
{
|
||||||
public LoginRequest() { }
|
[Required] public string Username { get; set; }
|
||||||
[Required]
|
|
||||||
public string Username { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
[Required] public string Password { get; set; }
|
||||||
public string Password { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,25 +1,27 @@
|
|||||||
using Flexitime.Objects;
|
using System;
|
||||||
|
|
||||||
namespace Flexitime.Objects
|
namespace Flexitime.Objects
|
||||||
{
|
{
|
||||||
public class LoginResponse
|
public class LoginResponse
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public LoginResponse()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginResponse(User user, string token)
|
||||||
|
{
|
||||||
|
Id = user.Id;
|
||||||
|
FirstName = user.FirstName;
|
||||||
|
LastName = user.LastName;
|
||||||
|
Username = user.UserName;
|
||||||
|
Token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
public string FirstName { get; set; }
|
public string FirstName { get; set; }
|
||||||
public string LastName { get; set; }
|
public string LastName { get; set; }
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
public string RefreshToken { get; set; }
|
public string RefreshToken { get; set; }
|
||||||
|
|
||||||
public LoginResponse() { }
|
|
||||||
|
|
||||||
public LoginResponse(User user, string token)
|
|
||||||
{
|
|
||||||
Id = user.UserId;
|
|
||||||
FirstName = user.FirstName;
|
|
||||||
LastName = user.LastName;
|
|
||||||
Username = user.LoginId;
|
|
||||||
Token = token;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,15 +1,11 @@
|
|||||||
namespace Flexitime.Objects
|
using System;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
{
|
{
|
||||||
public class Permission
|
public class Permission
|
||||||
{
|
{
|
||||||
public Permission()
|
|
||||||
{
|
|
||||||
Application = new Application();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Application Application { get; set; }
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Tag { get; set; }
|
public string Tag { get; set; }
|
||||||
public int Id { get; set; } //TODO switch to guid
|
public Guid Id { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
FlexitimeUI/Flexitime.Objects/Policy.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
|
{
|
||||||
|
public class Policy
|
||||||
|
{
|
||||||
|
public Version Version { get; set; }
|
||||||
|
public string DescriptionOfChanges { get; set; }
|
||||||
|
public User Author { get; set; }
|
||||||
|
public DateTimeOffset DateOfChange { get; set; }
|
||||||
|
public string Markdown { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
FlexitimeUI/Flexitime.Objects/Team.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
|
{
|
||||||
|
public class Team
|
||||||
|
{
|
||||||
|
public Team()
|
||||||
|
{
|
||||||
|
Users = new List<User>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public List<User> Users { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
13
FlexitimeUI/Flexitime.Objects/TimeLog.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
|
{
|
||||||
|
public class TimeLog
|
||||||
|
{
|
||||||
|
public string IdentifierId { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset LogTime { get; set; }
|
||||||
|
|
||||||
|
public LogSourceDescriptor SourceDescriptor { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
FlexitimeUI/Flexitime.Objects/TokenLoginRequest.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Flexitime.Objects
|
||||||
|
{
|
||||||
|
public class TokenLoginRequest : LoginRequest
|
||||||
|
{
|
||||||
|
public TokenLoginRequest()
|
||||||
|
{
|
||||||
|
Attributes = new List<KeyValuePair<string, string>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SourceName { get; set; }
|
||||||
|
public string SourceAddress { get; set; }
|
||||||
|
public List<KeyValuePair<string, string>> Attributes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security;
|
|
||||||
|
|
||||||
namespace Flexitime.Objects
|
namespace Flexitime.Objects
|
||||||
{
|
{
|
||||||
@ -13,31 +12,50 @@ namespace Flexitime.Objects
|
|||||||
Permissions = new List<Permission>();
|
Permissions = new List<Permission>();
|
||||||
FirstName = string.Empty;
|
FirstName = string.Empty;
|
||||||
LastName = string.Empty;
|
LastName = string.Empty;
|
||||||
|
DirectReportIds = new List<int>();
|
||||||
|
Team = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LoginId;
|
public Guid Id { get; set; }
|
||||||
public int UserId { get; set; } //TODO make this guid
|
|
||||||
public string FirstName { get; set; }
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
public string LastName { get; set; }
|
public string LastName { get; set; }
|
||||||
|
|
||||||
public float HoursPerWeek { get; set; }
|
public float HoursPerWeek { get; set; }
|
||||||
|
|
||||||
public bool IsContractor { get; set; }
|
public bool IsContractor { get; set; }
|
||||||
public int AssociatedIdentifierCount
|
|
||||||
{
|
public int AssociatedIdentifierCount => AssociatedIdentifiers.Count;
|
||||||
get { return AssociatedIdentifiers.Count; }
|
|
||||||
}
|
|
||||||
public DateTime LastEventDateTime { get; set; }
|
public DateTime LastEventDateTime { get; set; }
|
||||||
|
|
||||||
public List<Identifier> AssociatedIdentifiers { get; set; }
|
public List<Identifier> AssociatedIdentifiers { get; set; }
|
||||||
|
|
||||||
public List<Group> Groups { get; set; }
|
public List<Group> Groups { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// user that belongs to team
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>differs from Groups in that a group is a symbolic collection where a team is a publically identifiable entity</remarks>
|
||||||
|
public Team Team { get; set; }
|
||||||
|
|
||||||
public UserState State { get; set; }
|
public UserState State { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Id of the Users Line Manager
|
/// Id of the Users Line Manager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int LineManagerId { get; set; }
|
public User LineManager { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ids of the users direct reports
|
/// Ids of the users direct reports
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int[] DirectReportIds { get; set; }
|
public List<int> DirectReportIds { get; set; }
|
||||||
|
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
public List<Permission> Permissions { get; set; }
|
public List<Permission> Permissions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,6 +7,6 @@
|
|||||||
],
|
],
|
||||||
"assembliesPath": "./bin/debug/netcoreapp2.2",
|
"assembliesPath": "./bin/debug/netcoreapp2.2",
|
||||||
"outputPath": "../flexitimeui/src/models",
|
"outputPath": "../flexitimeui/src/models",
|
||||||
"noClean": false,
|
"noClean": true,
|
||||||
"useNugetCacheResolver": true
|
"useNugetCacheResolver": true
|
||||||
}
|
}
|
||||||
13
FlexitimeUI/FlexitimeAPI/Constants/Constants.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Constants
|
||||||
|
{
|
||||||
|
public static class Constants
|
||||||
|
{
|
||||||
|
public const string InvalidAppSettings =
|
||||||
|
"There is a problem with the structure of the appsettings.json. Require sections or items may be missing or named incorrectly. Details: {0}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,7 @@
|
|||||||
using Flexitime.Objects;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Helpers;
|
||||||
using FlexitimeAPI.Services;
|
using FlexitimeAPI.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -9,15 +12,17 @@ namespace FlexitimeAPI.Controllers
|
|||||||
public class AuthenticationController : ControllerBase
|
public class AuthenticationController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public AuthenticationController(IUserService userService)
|
public AuthenticationController(IUserService userService)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("login")]
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Authenticate(LoginRequest model)
|
public async Task<IActionResult> Authenticate(LoginRequest model)
|
||||||
{
|
{
|
||||||
var response = _userService.Authenticate(model);
|
var response = await _userService.Authenticate(model);
|
||||||
|
|
||||||
if (response == null)
|
if (response == null)
|
||||||
return BadRequest(new { message = "Username or password is incorrect" });
|
return BadRequest(new { message = "Username or password is incorrect" });
|
||||||
@ -25,12 +30,26 @@ namespace FlexitimeAPI.Controllers
|
|||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[Authorize]
|
||||||
[Route("logout")]
|
[Route("logout")]
|
||||||
|
[HttpGet]
|
||||||
public IActionResult Logout()
|
public IActionResult Logout()
|
||||||
{
|
{
|
||||||
//should be able to get the id here and log a message of that user logging out..
|
//should be able to get the id here and log a message of that user logging out..
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("token")]
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult TokenAuthenticationRequest(TokenLoginRequest model)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
var response = _userService.Authenticate(model);
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
return BadRequest(new { message = "Username or password is incorrect" });
|
||||||
|
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
44
FlexitimeUI/FlexitimeAPI/Controllers/IdentifierController.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects.API;
|
||||||
|
using FlexitimeAPI.Exceptions;
|
||||||
|
using FlexitimeAPI.Helpers;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class IdentifierController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IIdentifierService _identifierService;
|
||||||
|
|
||||||
|
public IdentifierController(IIdentifierService identifierService)
|
||||||
|
{
|
||||||
|
_identifierService = identifierService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize] //permission?
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Associate(AssociateIdentifierRequest associateIdentifierRequest)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _identifierService.Associate(associateIdentifierRequest.Identifier,
|
||||||
|
associateIdentifierRequest.UserId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (InvalidUserIdException iUidEx)
|
||||||
|
{
|
||||||
|
var modelStateDictionary = new ModelStateDictionary();
|
||||||
|
modelStateDictionary.AddModelError(nameof(Flexitime.Objects.User.Id), iUidEx.Message);
|
||||||
|
|
||||||
|
return BadRequest(modelStateDictionary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
FlexitimeUI/FlexitimeAPI/Controllers/PolicyController.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Helpers;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class PolicyController : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public List<Policy> GetPolicies()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Permissions=new []{"p.w"})]
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult UpdatePolicy(Policy newPolicy)
|
||||||
|
{
|
||||||
|
//get the authenticated user and populate the author property
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
FlexitimeUI/FlexitimeAPI/Controllers/TeamsController.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Flexitime.Objects;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class TeamsController : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public List<Team> GetTeams()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("{id}")]
|
||||||
|
public Team GetTeam(int id)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public Team CreateTeam(Team team)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
public Team UpdateTeam(Team team)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
FlexitimeUI/FlexitimeAPI/Controllers/TimeController.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Controllers
|
||||||
|
{
|
||||||
|
[Route("[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class TimeController : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpPost]
|
||||||
|
[Route("log")]
|
||||||
|
public LoginResponse StoreLog(TimeLog log)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,37 +1,89 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Flexitime.Objects;
|
using Flexitime.Objects;
|
||||||
|
using Flexitime.Objects.API;
|
||||||
|
using FlexitimeAPI.Exceptions;
|
||||||
using FlexitimeAPI.Helpers;
|
using FlexitimeAPI.Helpers;
|
||||||
using FlexitimeAPI.Services;
|
using FlexitimeAPI.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FlexitimeAPI.Controllers
|
namespace FlexitimeAPI.Controllers
|
||||||
{
|
{
|
||||||
[Authorize]//(Permissions=new []{"u.v"})]
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("[controller]")]
|
[Route("[controller]")]
|
||||||
public class UsersController : ControllerBase
|
public class UsersController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<WeatherForecastController> _logger;
|
private readonly ILogger<UsersController> _logger;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public UsersController(ILogger<WeatherForecastController> logger, IUserService userService)
|
public UsersController(ILogger<UsersController> logger, IUserService userService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IEnumerable<User> Get()
|
public async Task<IActionResult> Get()
|
||||||
{
|
{
|
||||||
return _userService.GetAll();
|
var userList = await _userService.GetAll();
|
||||||
|
|
||||||
|
if (!userList.Any())
|
||||||
|
{
|
||||||
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Ok(userList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("{id}")]
|
[Route("{id}")]
|
||||||
public User Get(int id)
|
public async Task<IActionResult> Get(Guid id)
|
||||||
{
|
{
|
||||||
return _userService.GetById(id);
|
var user = await _userService.GetById(id);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize("s.u.e")]
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create([FromBody] User newUser)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var createdUser = await _userService.Add(newUser);
|
||||||
|
if (createdUser == null)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(Get), createdUser.Id);
|
||||||
|
}
|
||||||
|
catch (InvalidUserNameException iuex)
|
||||||
|
{
|
||||||
|
var modelStateDictionary = new ModelStateDictionary();
|
||||||
|
modelStateDictionary.AddModelError(nameof(Flexitime.Objects.User.UserName), iuex.Message);
|
||||||
|
|
||||||
|
return BadRequest(modelStateDictionary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize("s.u.e")]
|
||||||
|
[HttpPost]
|
||||||
|
[Route("usernamevalid")]
|
||||||
|
public Task<IActionResult> UsernameValid([FromBody] UsernameValidCheckRequest usernameValidCheck)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FlexitimeAPI.Controllers
|
namespace FlexitimeAPI.Controllers
|
||||||
{
|
{
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
namespace FlexitimeAPI.Exceptions
|
||||||
|
{
|
||||||
|
public static class ExceptionConstants
|
||||||
|
{
|
||||||
|
public const string InvalidUsername = "Invalid Username supplied, the username: {0} is a duplicate or banned.";
|
||||||
|
public const string InvalidUserId = "Invalid User Id supplied: {0}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Exceptions
|
||||||
|
{
|
||||||
|
public class InvalidUserIdException : Exception
|
||||||
|
{
|
||||||
|
public InvalidUserIdException(Guid userId)
|
||||||
|
: base(BuildExceptionMessage(userId))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidUserIdException(Guid userId, Exception exception)
|
||||||
|
: base(BuildExceptionMessage(userId), exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildExceptionMessage(Guid userId)
|
||||||
|
=> string.Format(ExceptionConstants.InvalidUserId, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Exceptions
|
||||||
|
{
|
||||||
|
public class InvalidUserNameException : Exception
|
||||||
|
{
|
||||||
|
public InvalidUserNameException(string username)
|
||||||
|
: base(BuildExceptionMessage(username))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidUserNameException(string username, Exception exception)
|
||||||
|
: base(BuildExceptionMessage(username), exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildExceptionMessage(string username)
|
||||||
|
=> string.Format(ExceptionConstants.InvalidUsername, username);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,14 +5,24 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||||
<PackageReference Include="RandomNameGeneratorLibrary" Version="1.2.2" />
|
<PackageReference Include="RandomNameGeneratorLibrary" Version="1.2.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Flexitime.DataAccess\Flexitime.DataAccess.csproj" />
|
||||||
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
|
<ProjectReference Include="..\Flexitime.Interfaces\Flexitime.Interfaces.csproj" />
|
||||||
<ProjectReference Include="..\Flexitime.Objects\Flexitime.Objects.csproj" />
|
<ProjectReference Include="..\Flexitime.Objects\Flexitime.Objects.csproj" />
|
||||||
|
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Repositories\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
25
FlexitimeUI/FlexitimeAPI/Functions/AppSettingsExtensions.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using FlexitimeAPI.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Functions
|
||||||
|
{
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
|
public static class AppSettingsExtensions
|
||||||
|
{
|
||||||
|
public static void Verify<TApplicationSettings>(this AppSettings appSettings)
|
||||||
|
where TApplicationSettings : AppSettings
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var applicationSettingsJson = JsonConvert.SerializeObject(appSettings);
|
||||||
|
JsonConvert.DeserializeObject<TApplicationSettings>(applicationSettingsJson);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
throw new Exception(string.Format(Constants.Constants.InvalidAppSettings, exception.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Flexitime.Interfaces;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Functions
|
||||||
|
{
|
||||||
|
public class ConnectionStringProvider : IConnectionStringProvider
|
||||||
|
{
|
||||||
|
private readonly IApplicationSettings _appSettings;
|
||||||
|
|
||||||
|
public ConnectionStringProvider(IApplicationSettings appSettings)
|
||||||
|
{
|
||||||
|
_appSettings = appSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ConnectionString => _appSettings.ConnectionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,95 +1,43 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Flexitime.Objects;
|
using Flexitime.Objects;
|
||||||
using FlexitimeAPI.Models;
|
|
||||||
using FlexitimeAPI.Services;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
|
|
||||||
namespace FlexitimeAPI.Helpers
|
namespace FlexitimeAPI.Helpers
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
|
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
|
||||||
{
|
{
|
||||||
|
public AuthorizeAttribute()
|
||||||
|
{
|
||||||
|
Permissions = new string[] { };
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorizeAttribute(params string[] permissions)
|
||||||
|
{
|
||||||
|
Permissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
public string[] Permissions { get; set; }
|
public string[] Permissions { get; set; }
|
||||||
|
|
||||||
public void OnAuthorization(AuthorizationFilterContext context)
|
public void OnAuthorization(AuthorizationFilterContext context)
|
||||||
{
|
{
|
||||||
var user = (User) context.HttpContext.Items["User"];
|
var user = (User) context.HttpContext.Items["User"];
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
|
||||||
// not logged in
|
// not logged in
|
||||||
context.Result = new JsonResult(
|
context.Result = new JsonResult(
|
||||||
new { message = "Unauthorized" })
|
new { message = "Unauthorized" })
|
||||||
{ StatusCode = StatusCodes.Status401Unauthorized };
|
{ StatusCode = StatusCodes.Status401Unauthorized };
|
||||||
}
|
|
||||||
else if (Permissions.Any()
|
else if (Permissions.Any()
|
||||||
|
&& user.Permissions != null
|
||||||
&& !user.Permissions.Select(y => y.Tag)
|
&& !user.Permissions.Select(y => y.Tag)
|
||||||
.Intersect(Permissions)
|
.Intersect(Permissions.ToList())
|
||||||
.Any()) //check we have permissions if they have been specified
|
.Any()) //check we have permissions if they have been specified
|
||||||
{
|
|
||||||
context.Result =
|
|
||||||
context.Result = new JsonResult(
|
context.Result = new JsonResult(
|
||||||
new { message = "Unauthorized" })
|
new { message = "Unauthorized" })
|
||||||
{ StatusCode = StatusCodes.Status401Unauthorized };
|
{ StatusCode = StatusCodes.Status401Unauthorized };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JwtMiddleware
|
|
||||||
{
|
|
||||||
private readonly RequestDelegate _next;
|
|
||||||
private readonly AppSettings _appSettings;
|
|
||||||
|
|
||||||
public JwtMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings)
|
|
||||||
{
|
|
||||||
_next = next;
|
|
||||||
_appSettings = appSettings.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Invoke(HttpContext context, IUserService userService)
|
|
||||||
{
|
|
||||||
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
|
||||||
|
|
||||||
if (token != null)
|
|
||||||
AttachUserToContext(context, userService, token);
|
|
||||||
|
|
||||||
await _next(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AttachUserToContext(HttpContext context, IUserService userService, string token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tokenHandler = new JwtSecurityTokenHandler();
|
|
||||||
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
|
||||||
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
|
||||||
{
|
|
||||||
ValidateIssuerSigningKey = true,
|
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
|
||||||
ValidateIssuer = false,
|
|
||||||
ValidateAudience = false,
|
|
||||||
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
|
|
||||||
ClockSkew = TimeSpan.Zero
|
|
||||||
}, out SecurityToken validatedToken);
|
|
||||||
|
|
||||||
var jwtToken = (JwtSecurityToken)validatedToken;
|
|
||||||
var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
|
|
||||||
|
|
||||||
// attach user to context on successful jwt validation
|
|
||||||
context.Items["User"] = userService.GetById(userId);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// do nothing if jwt validation fails
|
|
||||||
// user is not attached to context so request won't have access to secure routes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
64
FlexitimeUI/FlexitimeAPI/Helpers/JwtMiddleware.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
using FlexitimeAPI.Models;
|
||||||
|
using FlexitimeAPI.Services;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Helpers
|
||||||
|
{
|
||||||
|
public class JwtMiddleware
|
||||||
|
{
|
||||||
|
private readonly IApplicationSettings _appSettings;
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public JwtMiddleware(RequestDelegate next, IApplicationSettings appSettings)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_appSettings = appSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context, IUserService userService)
|
||||||
|
{
|
||||||
|
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||||
|
|
||||||
|
if (token != null)
|
||||||
|
await AttachUserToContext(context, userService, token);
|
||||||
|
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AttachUserToContext(HttpContext context, IUserService userService, string token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
||||||
|
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
}, out var validatedToken);
|
||||||
|
|
||||||
|
var jwtToken = (JwtSecurityToken) validatedToken;
|
||||||
|
var userId = Guid.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
|
||||||
|
// attach user to context on successful jwt validation
|
||||||
|
context.Items["User"] = await userService.GetById(userId);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
// do nothing if jwt validation fails
|
||||||
|
// user is not attached to context so request won't have access to secure routes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
FlexitimeUI/FlexitimeAPI/Interfaces/Interface.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Interfaces
|
||||||
|
{
|
||||||
|
public interface ITimePersistenceService
|
||||||
|
{
|
||||||
|
UserState StoreTime(TimeLog logEntry);
|
||||||
|
|
||||||
|
void UpdateTimeLog(Guid logToUpdate, TimeLog updatedLogEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IApplicationSettings
|
||||||
|
{
|
||||||
|
public string Secret { get; }
|
||||||
|
public string ConnectionString { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ILoginService
|
||||||
|
{
|
||||||
|
string GenerateJwtToken(User user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IIdentifierService
|
||||||
|
{
|
||||||
|
Task Associate(Identifier identifier, Guid userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,18 @@
|
|||||||
namespace FlexitimeAPI.Models
|
using FlexitimeAPI.Interfaces;
|
||||||
|
using Infrastructure.Functions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Models
|
||||||
{
|
{
|
||||||
public class AppSettings
|
public class AppSettings:IApplicationSettings
|
||||||
{
|
{
|
||||||
public AppSettings()
|
|
||||||
{
|
[JsonProperty(Required = Required.Always)]
|
||||||
Secret = "JiminiyCricket|{08EBD219-AABA-4C33-8312-AE93EECE77D2}";
|
[JsonConverter(typeof(NonEmptyStringConverter))]
|
||||||
}
|
|
||||||
public string Secret { get; set; }
|
public string Secret { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.Always)]
|
||||||
|
[JsonConverter(typeof(NonEmptyStringConverter))]
|
||||||
|
public string ConnectionString { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FlexitimeAPI
|
namespace FlexitimeAPI
|
||||||
{
|
{
|
||||||
|
|||||||
31
FlexitimeUI/FlexitimeAPI/Services/IdentifierService.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Exceptions;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Services
|
||||||
|
{
|
||||||
|
public class IdentifierService : IIdentifierService
|
||||||
|
{
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
|
public IdentifierService(IUserService userService)
|
||||||
|
{
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Associate(Identifier identifier, Guid userId)
|
||||||
|
{
|
||||||
|
var user = await _userService.GetById(userId);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new InvalidUserIdException(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.AssociatedIdentifiers.Add(identifier);
|
||||||
|
await _userService.Update(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
FlexitimeUI/FlexitimeAPI/Services/LoginService.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Services
|
||||||
|
{
|
||||||
|
public class LoginService : ILoginService
|
||||||
|
{
|
||||||
|
private readonly IApplicationSettings _appSettings;
|
||||||
|
|
||||||
|
public LoginService(IApplicationSettings appSettings)
|
||||||
|
{
|
||||||
|
_appSettings = appSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateJwtToken(User user)
|
||||||
|
{
|
||||||
|
// generate token that is valid for 7 days
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
||||||
|
|
||||||
|
var claims = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{"permissions",user.Permissions.Select(x=>x.Tag)}
|
||||||
|
};
|
||||||
|
|
||||||
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
{
|
||||||
|
Subject = new ClaimsIdentity(new[] {new Claim("id", user.Id.ToString())}),
|
||||||
|
Expires = DateTime.UtcNow.AddHours(2),
|
||||||
|
Issuer = "FlexitimeUI",
|
||||||
|
NotBefore = DateTime.UtcNow.AddSeconds(-5),
|
||||||
|
IssuedAt = DateTime.UtcNow,
|
||||||
|
Claims = claims,
|
||||||
|
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
|
||||||
|
SecurityAlgorithms.HmacSha256Signature)
|
||||||
|
};
|
||||||
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||||
|
//tokenHandler.
|
||||||
|
return tokenHandler.WriteToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
FlexitimeUI/FlexitimeAPI/Services/TimePersistenceService.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
|
||||||
|
namespace FlexitimeAPI.Services
|
||||||
|
{
|
||||||
|
public class TimePersistenceService:ITimePersistenceService
|
||||||
|
{
|
||||||
|
public UserState StoreTime(TimeLog logEntry)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateTimeLog(Guid logToUpdate, TimeLog updatedLogEntry)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,120 +4,101 @@ using System.IdentityModel.Tokens.Jwt;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
using Flexitime.DataAccess.Objects;
|
||||||
|
using Flexitime.Interfaces;
|
||||||
using Flexitime.Objects;
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Exceptions;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
using FlexitimeAPI.Models;
|
using FlexitimeAPI.Models;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using RandomNameGeneratorLibrary;
|
using RandomNameGeneratorLibrary;
|
||||||
|
|
||||||
namespace FlexitimeAPI.Services
|
namespace FlexitimeAPI.Services
|
||||||
{
|
{
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
LoginResponse Authenticate(LoginRequest model);
|
Task<LoginResponse> Authenticate(LoginRequest model);
|
||||||
IEnumerable<User> GetAll();
|
Task<IEnumerable<User>> GetAll();
|
||||||
User GetById(int id);
|
Task<User> GetById(Guid id);
|
||||||
|
Task<User> Add(User newUser);
|
||||||
|
Task<bool> UsernameIsValid(string username);
|
||||||
|
Task<User> Update(User user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserService : IUserService
|
public class UserService : IUserService
|
||||||
{
|
{
|
||||||
private PersonNameGenerator _personGenerator;
|
private readonly IDataAccess _dataSource;
|
||||||
// users hardcoded for simplicity, store in a db with hashed passwords in production applications
|
private readonly ILoginService _loginService;
|
||||||
private List<User> _users;
|
|
||||||
|
|
||||||
private readonly AppSettings _appSettings;
|
public UserService(IDataAccess dataSource, ILoginService loginService)
|
||||||
|
|
||||||
public UserService(IOptions<AppSettings> appSettings)
|
|
||||||
{
|
{
|
||||||
_appSettings = appSettings.Value;
|
_dataSource = dataSource;
|
||||||
_personGenerator = new PersonNameGenerator();
|
_loginService = loginService;
|
||||||
var random = new Random();
|
|
||||||
var vals = Enum.GetValues(typeof(UserState));
|
|
||||||
|
|
||||||
_users = Enumerable.Range(2, 6).Select(index =>
|
|
||||||
{
|
|
||||||
var first = _personGenerator.GenerateRandomFirstName();
|
|
||||||
var last = _personGenerator.GenerateRandomLastName();
|
|
||||||
|
|
||||||
return new User
|
|
||||||
{
|
|
||||||
UserId = index,
|
|
||||||
LoginId = $"{first}{last}",
|
|
||||||
Password = "12345",
|
|
||||||
FirstName = first,
|
|
||||||
LastName = last,
|
|
||||||
HoursPerWeek = 37,
|
|
||||||
IsContractor = false,
|
|
||||||
State = (UserState)vals.GetValue(random.Next(2, vals.Length))
|
|
||||||
};
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
//create default known admin user..
|
|
||||||
_users.Add(new User
|
|
||||||
{
|
|
||||||
UserId = 1,
|
|
||||||
LoginId = "admin",
|
|
||||||
Password = "P@ssw0rd!",
|
|
||||||
FirstName = "Admin",
|
|
||||||
LastName = "User",
|
|
||||||
HoursPerWeek = 37,
|
|
||||||
IsContractor = false,
|
|
||||||
State = UserState.In
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoginResponse Authenticate(LoginRequest model)
|
public async Task<LoginResponse> Authenticate(LoginRequest model)
|
||||||
{
|
{
|
||||||
var user = _users.SingleOrDefault(x => x.LoginId == model.Username && x.Password == model.Password);
|
var user = (await _dataSource.GetUsers()).SingleOrDefault(x => x.UserName == model.Username && x.Password == model.Password);
|
||||||
|
|
||||||
// return null if user not found
|
// return null if user not found
|
||||||
if (user == null) return null;
|
if (user == null) return null;
|
||||||
|
|
||||||
// authentication successful so generate jwt token
|
// authentication successful so generate jwt token
|
||||||
var token = GenerateJwtToken(user);
|
var token = _loginService.GenerateJwtToken(user);
|
||||||
|
|
||||||
return new LoginResponse(user, token);
|
return new LoginResponse(user, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<User> GetAll()
|
public async Task<IEnumerable<User>> GetAll()
|
||||||
{
|
{
|
||||||
return _users;
|
return await _dataSource.GetUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public User GetById(int id)
|
public async Task<User> GetById(Guid id)
|
||||||
{
|
{
|
||||||
return _users.FirstOrDefault(x => x.UserId == id);
|
return await _dataSource.GetUserById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper methods
|
public async Task<User> Add(User newUser)
|
||||||
|
|
||||||
private string GenerateJwtToken(User user)
|
|
||||||
{
|
{
|
||||||
// generate token that is valid for 7 days
|
var userNameIsValid = await UsernameIsValid(newUser.UserName);
|
||||||
var tokenHandler = new JwtSecurityTokenHandler();
|
if (!userNameIsValid)
|
||||||
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
|
||||||
|
|
||||||
List<Claim> claims = new List<Claim>()
|
|
||||||
{
|
{
|
||||||
|
throw new InvalidUserNameException(newUser.UserName);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
return await _dataSource.AddUser(newUser);
|
||||||
|
}
|
||||||
|
|
||||||
//var jwt = new JwtSecurityToken(issuer:"FlexitimeUI",claims:claims);
|
public async Task<bool> UsernameIsValid(string userName)
|
||||||
|
|
||||||
var tokenDescriptor = new SecurityTokenDescriptor
|
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(userName))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Subject = new ClaimsIdentity(new[] { new Claim("id", user.UserId.ToString()) }),
|
//TODO: blacklisted username check
|
||||||
Expires = DateTime.UtcNow.AddHours(2),
|
|
||||||
Issuer = "FlexitimeUI",
|
var existingUser = await _dataSource.GetUserByUsername(userName);
|
||||||
NotBefore = DateTime.UtcNow.AddSeconds(-5),
|
|
||||||
IssuedAt = DateTime.UtcNow,
|
return existingUser == null;
|
||||||
//Claims = claims,
|
}
|
||||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
|
||||||
};
|
public async Task<User> Update(User user)
|
||||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
{
|
||||||
//tokenHandler.
|
if (user == null) return null;
|
||||||
return tokenHandler.WriteToken(token);
|
|
||||||
|
if ((await GetById(user.Id)) == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return await _dataSource.UpdateUser(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,17 +1,18 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Flexitime.DataAccess;
|
||||||
|
using Flexitime.Interfaces;
|
||||||
|
using FlexitimeAPI.Functions;
|
||||||
|
using FlexitimeAPI.Helpers;
|
||||||
|
using FlexitimeAPI.Interfaces;
|
||||||
|
using FlexitimeAPI.Models;
|
||||||
|
using FlexitimeAPI.Services;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.HttpsPolicy;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using FlexitimeAPI.Helpers;
|
|
||||||
|
|
||||||
namespace FlexitimeAPI
|
namespace FlexitimeAPI
|
||||||
{
|
{
|
||||||
@ -27,14 +28,52 @@ namespace FlexitimeAPI
|
|||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
var settings = InitialiseAppSettings();
|
||||||
|
|
||||||
|
services.AddSingleton(typeof(IApplicationSettings), settings);
|
||||||
|
services.AddSingleton<ILoginService, LoginService>();
|
||||||
|
services.AddSingleton<IConnectionStringProvider, ConnectionStringProvider>();
|
||||||
|
services.AddSingleton<IDataAccess, Database>();
|
||||||
|
services.AddSingleton<IUserService, UserService>();
|
||||||
|
|
||||||
|
services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddDefaultPolicy(
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.WithOrigins("http://localhost:3000")
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddControllers().AddJsonOptions(opts =>
|
||||||
|
{
|
||||||
|
opts.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
|
||||||
|
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||||
|
});
|
||||||
|
|
||||||
services.AddControllers();
|
|
||||||
services.AddSwaggerGen(c =>
|
services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerDoc("v1", new OpenApiInfo {Title = "FlexitimeAPI", Version = "v1"});
|
c.SwaggerDoc("v1", new OpenApiInfo {Title = "FlexitimeAPI", Version = "v1"});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IApplicationSettings InitialiseAppSettings()
|
||||||
|
{
|
||||||
|
var appSettings= new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||||
|
.Build()
|
||||||
|
.GetSection("Settings")
|
||||||
|
.Get<AppSettings>();
|
||||||
|
|
||||||
|
appSettings.Verify<AppSettings>();
|
||||||
|
|
||||||
|
return appSettings;
|
||||||
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
{
|
{
|
||||||
@ -45,17 +84,14 @@ namespace FlexitimeAPI
|
|||||||
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "FlexitimeAPI v1"));
|
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "FlexitimeAPI v1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseCors();
|
||||||
|
//app.UseHttpsRedirection();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseMiddleware<JwtMiddleware>();
|
app.UseMiddleware<JwtMiddleware>();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||||
{
|
|
||||||
endpoints.MapControllers();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,5 +5,9 @@
|
|||||||
"Microsoft": "Warning",
|
"Microsoft": "Warning",
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Settings": {
|
||||||
|
"Secret": "JiminiyCricket|{08EBD219-AABA-4C33-8312-AE93EECE77D2}",
|
||||||
|
"ConnectionString": "Data Source=.\\SQLSERVER2019;Initial Catalog=FlexitimeData;Integrated Security=SSPI;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,5 +6,9 @@
|
|||||||
"Microsoft.Hosting.Lifetime": "Information"
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*",
|
||||||
|
"Settings": {
|
||||||
|
"Secret": "JiminiyCricket|{08EBD219-AABA-4C33-8312-AE93EECE77D2}",
|
||||||
|
"ConnectionString": "Data Source=.\\SQLSERVER2019;Initial Catalog=FlexitimeData;Integrated Security=SSPI;"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,95 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AutoFixture;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
using FlexitimeAPI.Helpers;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace FlexitimeApi.UnitTests
|
||||||
|
{
|
||||||
|
public class AuthorizeAttributeTests
|
||||||
|
{
|
||||||
|
private readonly Fixture _f = new();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuthorizeAttribute_Should_AuthorizeWithNoExplicitPermissions()
|
||||||
|
{
|
||||||
|
var sut = new AuthorizeAttribute();
|
||||||
|
var mockUser = _f.Build<User>()
|
||||||
|
.Without(x => x.Permissions)
|
||||||
|
.Without(x => x.Groups)
|
||||||
|
.Without(x => x.Team)
|
||||||
|
.Create();
|
||||||
|
var ctx = SetupContext(mockUser);
|
||||||
|
sut.OnAuthorization(ctx);
|
||||||
|
|
||||||
|
ctx.Result.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuthorizeAttribute_ShouldNot_AuthorizeWhenUserHasInvalidPermission()
|
||||||
|
{
|
||||||
|
var mockUser = _f.Build<User>()
|
||||||
|
.With(x => x.Permissions,
|
||||||
|
_f.Build<Permission>().With(y => y.Tag, "g.x").Without(y => y.Application).CreateMany(1).ToList())
|
||||||
|
.Without(x => x.Groups)
|
||||||
|
.Without(x => x.Team)
|
||||||
|
.Create();
|
||||||
|
|
||||||
|
var ctx = SetupContext(mockUser);
|
||||||
|
|
||||||
|
var sut = new AuthorizeAttribute {Permissions = new[] {"u.w"}};
|
||||||
|
|
||||||
|
sut.OnAuthorization(ctx);
|
||||||
|
|
||||||
|
ctx.Result.Should().BeOfType<JsonResult>();
|
||||||
|
var actual = ctx.Result as JsonResult;
|
||||||
|
actual.StatusCode.Should().Be(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuthorizeAttribute_Should_AuthorizeWithCorrectExplicitPermission()
|
||||||
|
{
|
||||||
|
var mockUser = _f.Build<User>()
|
||||||
|
.With(x => x.Permissions,
|
||||||
|
_f.Build<Permission>().With(y => y.Tag, "g.x").Without(y => y.Application).CreateMany(1).ToList())
|
||||||
|
.Without(x => x.Groups)
|
||||||
|
.Without(x => x.Team)
|
||||||
|
.Create();
|
||||||
|
|
||||||
|
var ctx = SetupContext(mockUser);
|
||||||
|
|
||||||
|
var sut = new AuthorizeAttribute {Permissions = new[] {"g.x"}};
|
||||||
|
|
||||||
|
sut.OnAuthorization(ctx);
|
||||||
|
|
||||||
|
ctx.Result.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorizationFilterContext SetupContext(User mockUser)
|
||||||
|
{
|
||||||
|
var httpContextMock = new Mock<HttpContext>();
|
||||||
|
httpContextMock
|
||||||
|
.Setup(a => a.Request.Headers["Authorization"])
|
||||||
|
.Returns("mock WRONG apikey");
|
||||||
|
|
||||||
|
httpContextMock.SetupGet(x => x.Items)
|
||||||
|
.Returns(new Dictionary<object, object?> {{"User", mockUser}});
|
||||||
|
|
||||||
|
ActionContext fakeActionContext =
|
||||||
|
new ActionContext(httpContextMock.Object,
|
||||||
|
new Microsoft.AspNetCore.Routing.RouteData(),
|
||||||
|
new Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor());
|
||||||
|
|
||||||
|
var ret = new AuthorizationFilterContext(fakeActionContext,
|
||||||
|
new List<IFilterMetadata>());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Autofixture" Version="4.15.0" />
|
||||||
|
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||||
|
<PackageReference Include="Moq" Version="4.16.1" />
|
||||||
|
<PackageReference Include="XUnit" Version="2.4.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FlexitimeAPI\FlexitimeAPI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
1236
FlexitimeUI/FlexitimeTaskbarUtility/Annotations.cs
Normal file
19
FlexitimeUI/FlexitimeTaskbarUtility/App.xaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<prism:PrismApplication x:Class="FlexitimeTaskbarUtility.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:FlexitimeTaskbarUtility.Converters"
|
||||||
|
ShutdownMode="OnExplicitShutdown"
|
||||||
|
xmlns:prism="http://prismlibrary.com/">
|
||||||
|
<prism:PrismApplication.Resources>
|
||||||
|
|
||||||
|
<!-- merge NotifyIcon and related stuff into the application -->
|
||||||
|
<ResourceDictionary>
|
||||||
|
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="NotifyIconResources.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
|
||||||
|
</prism:PrismApplication.Resources>
|
||||||
|
</prism:PrismApplication>
|
||||||
60
FlexitimeUI/FlexitimeTaskbarUtility/App.xaml.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using FlexitimeTaskbarUtility.Extensions;
|
||||||
|
using FlexitimeTaskbarUtility.Interfaces;
|
||||||
|
using FlexitimeTaskbarUtility.Services;
|
||||||
|
using FlexitimeTaskbarUtility.ViewModels;
|
||||||
|
using Hardcodet.Wpf.TaskbarNotification;
|
||||||
|
using Prism.DryIoc;
|
||||||
|
using Prism.Events;
|
||||||
|
using Prism.Ioc;
|
||||||
|
using Prism.Regions;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : PrismApplication
|
||||||
|
{
|
||||||
|
private TaskbarIcon notifyIcon;
|
||||||
|
|
||||||
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnStartup(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
|
||||||
|
{
|
||||||
|
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
|
||||||
|
regionAdapterMappings.RegisterMapping(typeof(ContextMenu), Container.Resolve<ContextMenuRegionAdapter>());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RegisterTypes(IContainerRegistry containerRegistry)
|
||||||
|
{
|
||||||
|
containerRegistry.Register<ILoginService, LoginService>();
|
||||||
|
containerRegistry.Register<ISignInService, SignInService>();
|
||||||
|
containerRegistry.Register<NotifyIconViewModel>();
|
||||||
|
containerRegistry.Register<LoginControlViewModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Window CreateShell()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
|
||||||
|
notifyIcon = (TaskbarIcon)FindResource("NotifyIcon");
|
||||||
|
notifyIcon.DataContext = Container.Resolve<NotifyIconViewModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnExit(ExitEventArgs e)
|
||||||
|
{
|
||||||
|
notifyIcon.Dispose(); //the icon would clean up automatically, but this is cleaner
|
||||||
|
base.OnExit(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
FlexitimeUI/FlexitimeTaskbarUtility/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
<UserControl x:Class="FlexitimeTaskbarUtility.Components.LoginControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:viewModels="clr-namespace:FlexitimeTaskbarUtility.ViewModels"
|
||||||
|
xmlns:components="clr-namespace:FlexitimeTaskbarUtility.Components"
|
||||||
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
|
d:DataContext="{d:DesignInstance Type=viewModels:LoginControlViewModel}"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
MinHeight="150"
|
||||||
|
MinWidth="300"
|
||||||
|
prism:ViewModelLocator.AutoWireViewModel="True">
|
||||||
|
<Grid Background="Azure">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
FontWeight="Bold"
|
||||||
|
FontSize="22"
|
||||||
|
Margin="10,10,0,0">
|
||||||
|
Login
|
||||||
|
</TextBlock>
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition />
|
||||||
|
<RowDefinition />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
FontSize="18"
|
||||||
|
Margin="10,0,0,0">
|
||||||
|
Username
|
||||||
|
</TextBlock>
|
||||||
|
<TextBox Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
FontSize="18"
|
||||||
|
Text="{Binding UserName}"
|
||||||
|
Margin="0,0,10,0"/>
|
||||||
|
<TextBlock Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
FontSize="18"
|
||||||
|
Margin="10,0,0,0">
|
||||||
|
Password
|
||||||
|
</TextBlock>
|
||||||
|
<PasswordBox Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Name="PasswordBox"
|
||||||
|
components:PasswordBoxAssistant.BindPassword="True"
|
||||||
|
components:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Margin="0,0,10,0"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Button Grid.Row="2"
|
||||||
|
Height="30"
|
||||||
|
Width="120"
|
||||||
|
FontSize="18"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,15,0,0"
|
||||||
|
Command="{Binding LoginCommand}"
|
||||||
|
CommandParameter="{Binding ElementName=PasswordBox}">
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for LoginControl.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class LoginControl : UserControl
|
||||||
|
{
|
||||||
|
public LoginControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Components
|
||||||
|
{
|
||||||
|
public static class PasswordBoxAssistant
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty BoundPassword =
|
||||||
|
DependencyProperty.RegisterAttached(nameof(BoundPassword), typeof(string), typeof(PasswordBoxAssistant), new PropertyMetadata(string.Empty, OnBoundPasswordChanged));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached(
|
||||||
|
nameof(BindPassword), typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged));
|
||||||
|
|
||||||
|
private static readonly DependencyProperty UpdatingPassword =
|
||||||
|
DependencyProperty.RegisterAttached(nameof(UpdatingPassword), typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false));
|
||||||
|
|
||||||
|
private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
PasswordBox box = d as PasswordBox;
|
||||||
|
|
||||||
|
// only handle this event when the property is attached to a PasswordBox
|
||||||
|
// and when the BindPassword attached property has been set to true
|
||||||
|
if (d == null || !GetBindPassword(d))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid recursive updating by ignoring the box's changed event
|
||||||
|
box.PasswordChanged -= HandlePasswordChanged;
|
||||||
|
|
||||||
|
string newPassword = (string)e.NewValue;
|
||||||
|
|
||||||
|
if (!GetUpdatingPassword(box))
|
||||||
|
{
|
||||||
|
box.Password = newPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
box.PasswordChanged += HandlePasswordChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// when the BindPassword attached property is set on a PasswordBox,
|
||||||
|
// start listening to its PasswordChanged event
|
||||||
|
|
||||||
|
PasswordBox box = dp as PasswordBox;
|
||||||
|
|
||||||
|
if (box == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasBound = (bool)(e.OldValue);
|
||||||
|
bool needToBind = (bool)(e.NewValue);
|
||||||
|
|
||||||
|
if (wasBound)
|
||||||
|
{
|
||||||
|
box.PasswordChanged -= HandlePasswordChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needToBind)
|
||||||
|
{
|
||||||
|
box.PasswordChanged += HandlePasswordChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
PasswordBox box = sender as PasswordBox;
|
||||||
|
|
||||||
|
// set a flag to indicate that we're updating the password
|
||||||
|
SetUpdatingPassword(box, true);
|
||||||
|
// push the new password into the BoundPassword property
|
||||||
|
SetBoundPassword(box, box.Password);
|
||||||
|
SetUpdatingPassword(box, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetBindPassword(DependencyObject dp, bool value)
|
||||||
|
{
|
||||||
|
dp.SetValue(BindPassword, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GetBindPassword(DependencyObject dp)
|
||||||
|
{
|
||||||
|
return (bool)dp.GetValue(BindPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetBoundPassword(DependencyObject dp)
|
||||||
|
{
|
||||||
|
return (string)dp.GetValue(BoundPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetBoundPassword(DependencyObject dp, string value)
|
||||||
|
{
|
||||||
|
dp.SetValue(BoundPassword, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool GetUpdatingPassword(DependencyObject dp)
|
||||||
|
{
|
||||||
|
return (bool)dp.GetValue(UpdatingPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetUpdatingPassword(DependencyObject dp, bool value)
|
||||||
|
{
|
||||||
|
dp.SetValue(UpdatingPassword, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<UserControl x:Class="FlexitimeTaskbarUtility.Components.SigninControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Height="100"
|
||||||
|
Width="200">
|
||||||
|
<Grid Background="Green">
|
||||||
|
<TextBlock HorizontalAlignment="Center" Foreground="White" VerticalAlignment="Center">Sign in control</TextBlock>
|
||||||
|
<Button Height="30" VerticalAlignment="Bottom" Command="{Binding SignInCommand}">Sign In</Button>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for SigninControl.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class SigninControl : UserControl
|
||||||
|
{
|
||||||
|
public SigninControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Converters
|
||||||
|
{
|
||||||
|
public class UserStatusToImageSourceConverter:IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value.GetType() != typeof(UserState))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var userState = (UserState) value;
|
||||||
|
string basePath = string.Intern("pack://application:,,,/FlexitimeTaskbarUtility;component/Images/");
|
||||||
|
string imageName;
|
||||||
|
switch (userState)
|
||||||
|
{
|
||||||
|
case UserState.Unknown:
|
||||||
|
imageName = "Clock_LoggedOut.ico";
|
||||||
|
break;
|
||||||
|
case UserState.In:
|
||||||
|
imageName = "Clock_In.ico";
|
||||||
|
break;
|
||||||
|
case UserState.Out:
|
||||||
|
imageName = "Clock_Out.ico";
|
||||||
|
break;
|
||||||
|
case UserState.OutOfOffice:
|
||||||
|
imageName = "Clock_OutOfOffice.ico";
|
||||||
|
break;
|
||||||
|
case UserState.Remote:
|
||||||
|
imageName = "Clock_Remote.ico";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageSource ret = new BitmapImage(new Uri($"{basePath}{imageName}"));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Converters
|
||||||
|
{
|
||||||
|
public class UserStatusToLoginDialogVisibilityConverter:IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value.GetType() != typeof(UserState))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var userState = (UserState)value;
|
||||||
|
Visibility ret;
|
||||||
|
|
||||||
|
switch (userState)
|
||||||
|
{
|
||||||
|
case UserState.Unknown:
|
||||||
|
ret = Visibility.Visible;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = Visibility.Collapsed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Converters
|
||||||
|
{
|
||||||
|
public class UserStatusToSigninControlVisibilityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value.GetType() != typeof(UserState))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
var userState = (UserState)value;
|
||||||
|
Visibility ret;
|
||||||
|
|
||||||
|
switch (userState)
|
||||||
|
{
|
||||||
|
case UserState.Unknown:
|
||||||
|
ret = Visibility.Collapsed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = Visibility.Visible;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
FlexitimeUI/FlexitimeTaskbarUtility/DelegateCommand.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace NotifyIconWpf.Sample.Windowless
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Simplistic delegate command for the demo.
|
||||||
|
/// </summary>
|
||||||
|
public class DelegateCommand : ICommand
|
||||||
|
{
|
||||||
|
public Action CommandAction { get; set; }
|
||||||
|
public Func<bool> CanExecuteFunc { get; set; }
|
||||||
|
|
||||||
|
public void Execute(object parameter)
|
||||||
|
{
|
||||||
|
CommandAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter)
|
||||||
|
{
|
||||||
|
return CanExecuteFunc == null || CanExecuteFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler CanExecuteChanged
|
||||||
|
{
|
||||||
|
add { CommandManager.RequerySuggested += value; }
|
||||||
|
remove { CommandManager.RequerySuggested -= value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
using Flexitime.Objects;
|
||||||
|
using Prism.Events;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Events
|
||||||
|
{
|
||||||
|
public class UserLoggedInEvent:PubSubEvent<LoginResponse>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
using Prism.Events;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Events
|
||||||
|
{
|
||||||
|
public class UserLoggedOutEvent:PubSubEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Prism.Regions;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Extensions
|
||||||
|
{
|
||||||
|
public class ContextMenuRegionAdapter:RegionAdapterBase<ContextMenu>
|
||||||
|
{
|
||||||
|
public ContextMenuRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Adapt(IRegion region, ContextMenu regionTarget)
|
||||||
|
{
|
||||||
|
region.Views.CollectionChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||||
|
{
|
||||||
|
foreach (var item in e.NewItems)
|
||||||
|
{
|
||||||
|
regionTarget.Items.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||||
|
{
|
||||||
|
foreach (var oldItem in e.OldItems)
|
||||||
|
{
|
||||||
|
regionTarget.Items.Remove(oldItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IRegion CreateRegion()
|
||||||
|
{
|
||||||
|
return new Region();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net5.0-windows</TargetFramework>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Icons\**" />
|
||||||
|
<EmbeddedResource Remove="Icons\**" />
|
||||||
|
<None Remove="Icons\**" />
|
||||||
|
<Page Remove="Icons\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Images\Clock_Base.png" />
|
||||||
|
<None Remove="Images\Clock_Base.svg" />
|
||||||
|
<None Remove="Images\Clock_In.ico" />
|
||||||
|
<None Remove="Images\Clock_In.png" />
|
||||||
|
<None Remove="Images\Clock_In.svg" />
|
||||||
|
<None Remove="Images\Clock_LoggedOut.ico" />
|
||||||
|
<None Remove="Images\Clock_Out.ico" />
|
||||||
|
<None Remove="Images\Clock_Out.png" />
|
||||||
|
<None Remove="Images\Clock_Out.svg" />
|
||||||
|
<None Remove="Images\Clock_OutOfOffice.ico" />
|
||||||
|
<None Remove="Images\Clock_OutOfOffice.png.png" />
|
||||||
|
<None Remove="Images\Clock_OutOfOffice.svg" />
|
||||||
|
<None Remove="Images\Clock_Remote.ico" />
|
||||||
|
<None Remove="Images\Clock_Remote.png" />
|
||||||
|
<None Remove="Images\Clock_Remote.svg" />
|
||||||
|
<None Remove="Red.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Prism.DryIoc" Version="8.0.0.1909" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Flexitime.Objects\Flexitime.Objects.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Images\Clock_In.ico" />
|
||||||
|
<Resource Include="Images\Clock_LoggedOut.ico" />
|
||||||
|
<Resource Include="Images\Clock_Out.ico" />
|
||||||
|
<Resource Include="Images\Clock_OutOfOffice.ico" />
|
||||||
|
<Resource Include="Images\Clock_Remote.ico" />
|
||||||
|
<Resource Include="Red.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Base.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
95
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Base.svg
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="83.154762mm"
|
||||||
|
height="83.154762mm"
|
||||||
|
viewBox="0 0 83.154762 83.154762"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="Clock_Base.svg"
|
||||||
|
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"
|
||||||
|
inkscape:export-filename="C:\projects\flexitimetrackertool-v2\FlexitimeUI\FlexitimeTaskbarUtility\Images\Clock_Base.svg.png"
|
||||||
|
inkscape:export-xdpi="95.5924"
|
||||||
|
inkscape:export-ydpi="95.5924">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="-148.11056"
|
||||||
|
inkscape:cy="222.56383"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="g111"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="2400"
|
||||||
|
inkscape:window-height="1271"
|
||||||
|
inkscape:window-x="1"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-60.854168,-77.863102)">
|
||||||
|
<circle
|
||||||
|
id="path10"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="41.577381"
|
||||||
|
style="stroke-width:0.264583" />
|
||||||
|
<circle
|
||||||
|
id="path12"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="36.434746"
|
||||||
|
style="fill:#ffffff;stroke-width:0.284964" />
|
||||||
|
<g
|
||||||
|
id="g111">
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;stroke-width:1.76459"
|
||||||
|
id="rect14"
|
||||||
|
width="7.5903196"
|
||||||
|
height="34.534515"
|
||||||
|
x="98.636391"
|
||||||
|
y="87.664406"
|
||||||
|
ry="3.7951598" />
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;stroke-width:1.55969"
|
||||||
|
id="rect14-4"
|
||||||
|
width="7.5903196"
|
||||||
|
height="26.980021"
|
||||||
|
x="-40.71925"
|
||||||
|
y="-175.74658"
|
||||||
|
ry="3.7951598"
|
||||||
|
transform="rotate(152.81833)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_In.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_In.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
96
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_In.svg
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="83.154762mm"
|
||||||
|
height="83.154762mm"
|
||||||
|
viewBox="0 0 83.154762 83.154762"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="Clock_In.svg"
|
||||||
|
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"
|
||||||
|
inkscape:export-filename="C:\projects\flexitimetrackertool-v2\FlexitimeUI\FlexitimeTaskbarUtility\Images\Clock_In.png"
|
||||||
|
inkscape:export-xdpi="95.5924"
|
||||||
|
inkscape:export-ydpi="95.5924">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="-148.11056"
|
||||||
|
inkscape:cy="222.56383"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="2400"
|
||||||
|
inkscape:window-height="1271"
|
||||||
|
inkscape:window-x="1"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-60.854168,-77.863102)">
|
||||||
|
<circle
|
||||||
|
id="path10"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="41.577381"
|
||||||
|
style="stroke-width:0.264583;fill:#00ff00" />
|
||||||
|
<circle
|
||||||
|
id="path12"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="36.434746"
|
||||||
|
style="fill:#ffffff;stroke-width:0.284964" />
|
||||||
|
<g
|
||||||
|
id="g111"
|
||||||
|
style="fill:#00ff00">
|
||||||
|
<rect
|
||||||
|
style="fill:#00ff00;stroke-width:1.76459"
|
||||||
|
id="rect14"
|
||||||
|
width="7.5903196"
|
||||||
|
height="34.534515"
|
||||||
|
x="98.636391"
|
||||||
|
y="87.664406"
|
||||||
|
ry="3.7951598" />
|
||||||
|
<rect
|
||||||
|
style="fill:#00ff00;stroke-width:1.55969"
|
||||||
|
id="rect14-4"
|
||||||
|
width="7.5903196"
|
||||||
|
height="26.980021"
|
||||||
|
x="-40.71925"
|
||||||
|
y="-175.74658"
|
||||||
|
ry="3.7951598"
|
||||||
|
transform="rotate(152.81833)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_LoggedOut.ico
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Out.ico
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Out.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
96
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Out.svg
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="83.154762mm"
|
||||||
|
height="83.154762mm"
|
||||||
|
viewBox="0 0 83.154762 83.154762"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="Clock_Out.svg"
|
||||||
|
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"
|
||||||
|
inkscape:export-filename="C:\projects\flexitimetrackertool-v2\FlexitimeUI\FlexitimeTaskbarUtility\Images\Clock_In.png"
|
||||||
|
inkscape:export-xdpi="95.5924"
|
||||||
|
inkscape:export-ydpi="95.5924">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="137.63627"
|
||||||
|
inkscape:cy="193.86712"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="2400"
|
||||||
|
inkscape:window-height="1271"
|
||||||
|
inkscape:window-x="1"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-60.854168,-77.863102)">
|
||||||
|
<circle
|
||||||
|
id="path10"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="41.577381"
|
||||||
|
style="stroke-width:0.264583;fill:#ff0000" />
|
||||||
|
<circle
|
||||||
|
id="path12"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="36.434746"
|
||||||
|
style="fill:#ffffff;stroke-width:0.284964" />
|
||||||
|
<g
|
||||||
|
id="g111"
|
||||||
|
style="fill:#ff0000">
|
||||||
|
<rect
|
||||||
|
style="fill:#ff0000;stroke-width:1.76459"
|
||||||
|
id="rect14"
|
||||||
|
width="7.5903196"
|
||||||
|
height="34.534515"
|
||||||
|
x="98.636391"
|
||||||
|
y="87.664406"
|
||||||
|
ry="3.7951598" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ff0000;stroke-width:1.55969"
|
||||||
|
id="rect14-4"
|
||||||
|
width="7.5903196"
|
||||||
|
height="26.980021"
|
||||||
|
x="-40.71925"
|
||||||
|
y="-175.74658"
|
||||||
|
ry="3.7951598"
|
||||||
|
transform="rotate(152.81833)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_OutOfOffice.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="83.154762mm"
|
||||||
|
height="83.154762mm"
|
||||||
|
viewBox="0 0 83.154762 83.154762"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="Clock_OutOfOffice.svg"
|
||||||
|
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"
|
||||||
|
inkscape:export-filename="C:\projects\flexitimetrackertool-v2\FlexitimeUI\FlexitimeTaskbarUtility\Images\Clock_OutOfOffice.png.png"
|
||||||
|
inkscape:export-xdpi="95.5924"
|
||||||
|
inkscape:export-ydpi="95.5924">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="137.63627"
|
||||||
|
inkscape:cy="193.86712"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="2400"
|
||||||
|
inkscape:window-height="1271"
|
||||||
|
inkscape:window-x="1"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-60.854168,-77.863102)">
|
||||||
|
<circle
|
||||||
|
id="path10"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="41.577381"
|
||||||
|
style="stroke-width:0.264583;fill:#ffcc00" />
|
||||||
|
<circle
|
||||||
|
id="path12"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="36.434746"
|
||||||
|
style="fill:#ffffff;stroke-width:0.284964" />
|
||||||
|
<g
|
||||||
|
id="g111"
|
||||||
|
style="fill:#ffd42a">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffd42a;stroke-width:1.76459"
|
||||||
|
id="rect14"
|
||||||
|
width="7.5903196"
|
||||||
|
height="34.534515"
|
||||||
|
x="98.636391"
|
||||||
|
y="87.664406"
|
||||||
|
ry="3.7951598" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffd42a;stroke-width:1.55969"
|
||||||
|
id="rect14-4"
|
||||||
|
width="7.5903196"
|
||||||
|
height="26.980021"
|
||||||
|
x="-40.71925"
|
||||||
|
y="-175.74658"
|
||||||
|
ry="3.7951598"
|
||||||
|
transform="rotate(152.81833)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Remote.ico
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Remote.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
96
FlexitimeUI/FlexitimeTaskbarUtility/Images/Clock_Remote.svg
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="83.154762mm"
|
||||||
|
height="83.154762mm"
|
||||||
|
viewBox="0 0 83.154762 83.154762"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="Clock_Remote.svg"
|
||||||
|
inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"
|
||||||
|
inkscape:export-filename="C:\projects\flexitimetrackertool-v2\FlexitimeUI\FlexitimeTaskbarUtility\Images\Clock_Remote.png"
|
||||||
|
inkscape:export-xdpi="95.5924"
|
||||||
|
inkscape:export-ydpi="95.5924">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="137.63627"
|
||||||
|
inkscape:cy="193.86712"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="2400"
|
||||||
|
inkscape:window-height="1271"
|
||||||
|
inkscape:window-x="1"
|
||||||
|
inkscape:window-y="-9"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-60.854168,-77.863102)">
|
||||||
|
<circle
|
||||||
|
id="path10"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="41.577381"
|
||||||
|
style="stroke-width:0.264583;fill:#8f8feb;fill-opacity:1" />
|
||||||
|
<circle
|
||||||
|
id="path12"
|
||||||
|
cx="102.43155"
|
||||||
|
cy="119.44048"
|
||||||
|
r="36.434746"
|
||||||
|
style="fill:#ffffff;stroke-width:0.284964" />
|
||||||
|
<g
|
||||||
|
id="g111"
|
||||||
|
style="fill:#8f8feb;fill-opacity:1">
|
||||||
|
<rect
|
||||||
|
style="fill:#8f8feb;stroke-width:1.76459;fill-opacity:1"
|
||||||
|
id="rect14"
|
||||||
|
width="7.5903196"
|
||||||
|
height="34.534515"
|
||||||
|
x="98.636391"
|
||||||
|
y="87.664406"
|
||||||
|
ry="3.7951598" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8f8feb;stroke-width:1.55969;fill-opacity:1"
|
||||||
|
id="rect14-4"
|
||||||
|
width="7.5903196"
|
||||||
|
height="26.980021"
|
||||||
|
x="-40.71925"
|
||||||
|
y="-175.74658"
|
||||||
|
ry="3.7951598"
|
||||||
|
transform="rotate(152.81833)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Interfaces
|
||||||
|
{
|
||||||
|
public interface ILoginService
|
||||||
|
{
|
||||||
|
LoginResponse Login(LoginRequest loginRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
using Flexitime.Objects;
|
||||||
|
|
||||||
|
namespace FlexitimeTaskbarUtility.Interfaces
|
||||||
|
{
|
||||||
|
interface ISignInService
|
||||||
|
{
|
||||||
|
void SetUserState(UserState state);
|
||||||
|
}
|
||||||
|
}
|
||||||