Giter Club home page Giter Club logo

permissionaccesscontrol2's Introduction

PermissionAccessControl2

Welcome to version 2 of the example application that contains my approach to feature and data authorization code. This web site pretends to be a SaaS application which provides stock and sales management to companies with multiple retail outlets.

NOTE: This version has been updated to NET Core 3.1
I didn't update to NET 5 because NET Core 3.1 is the long-term support version. The update to NET 5 should be fairly easy (converting from 2.2 to 3.1 was had work!)

This is open-source application (MIT license).

See the articles

NOTE: If you like what these articles describe and want to add one or more of these features to your application I STRONGLY suggest you read the Part 7 article, which gives a step-by-step guide to how to pick/copy the right code from the PermissionAccessControl2 into your app.

How to play with the application

You start the PermissionAccessControl2 project to run the ASP.NET Core application. The home screen shows you what you can do. It also tells you what setup was used the features/database - section Controlling how the demo works.

The default setting (see Configuration section below) will use in-memory databases which it will preload with demo users and data at startup (NOTE: Its a bit slow to start as it is setting up all the demo users and data). The demo users have:

  1. Different Permissions, which controls what they can do, e.g. only a StoreManager can provide a refund.
  2. Different DataKey, which controls what part of the shop data they can see, e.g. a SalesAssistant and StoreManager can only see the data in their shop, but a Director can see all shop data in the company.
  3. There is Refresh Claims menu dropdown which allows you to try the "refreshing claims" feature described in the Part 5 article.
  4. There is a Impersonation menu dropdown which allows you to try the "user impersonation" feature described in the Part 6 article.

There is a link on the home page to a list of users that you can log in via (the email address is also the password). There are two different companies, 4U Inc. and Pets2 Ltd., which have a number of shops in different divisions, represented by hierarchical data. Logging in as a user will give you access to some features and data (if linked to data).

The home page gives you more information on what you can do.

Configuration

The appsetting.json file contains settings that configure how the system runs.

Controlling how the demo works

This application is written to work with both in-memory or normal (e.g. SQL Server) databases (version 1 only worked with in-memory, but that made it difficult to convert to normal databases). The "DemoSetup" section is shown below:

  "DemoSetup": {
    "DatabaseSetup": "InMemory", //This can be "InMemory" or "Permanent" (a real database) database.
    "CreateAndSeed": true, //If this is true then it will create the dbs and ensure the data is seeded
    "AuthVersion": "Everything" //The options are Off, LoginPermissions, LoginPermissionsDataKey, PermissionsOnly, PermissionsDataKey, Impersonation, RefreshClaims, Everything
  

They are descibed in the next three subsections.

1. DatabaseSetup property

This swiches between:

  • "InMemory": which selects an in-memory Sqlite database - very easy to try out things or changing the database.
  • "Permanent": which selects a SQL Server database.

NOTE that I use context.Database.EnsureCreated() on startup to create the database because its easy. BUT it does preclude the use of EF Core Migrations. See PermissionsOnlyApp, which I create as part of the Part 7 acticle. It usees EF Core Migrations to handle database changes.

If you use "Permanent" for the "DatabaseSetup" then you need to provide two connection strings: one for the ASP.NET Core Identity database and the other for the database which holds both the multi-tenant data and the extra authorization data.

  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=PermissionAccessControl2-AspNetCoreIdentity;Trusted_Connection=True;MultipleActiveResultSets=true",
    "DemoDatabaseConnection": "Server=(localdb)\\mssqllocaldb;Database=PermissionAccessControl2-DemoDatabase;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

2. CreateAndSeed property

This is there for people who want to mess about with a SQL Server database. If its false then all the database create and seed parts are turned off.

NOTE: The check/add of the SuperAdmin user isn't turned off by this property.

3. AuthVersion property

This allows you to try the different authorization features covered in the articles. I'm not going to describe all the features here beacause they can be seen in the AddClaimsToCookie class.

Setting up SuperAdmin user

The appsetting.json file should have a "SuperAdmin" section as shown below. on startup the extension method CheckAddSuperAdminAsync checks to see if there is a user with the role "SuperAdmin". If there isn't it tries to add a user with the given email (which will fail if that is already used).

  "SuperAdmin": //This holds the information on the superuser. You must have one SuperUser setup otherwise you can't manage users
  {
    "Email": "... email of super admin user ...",
    "Password": "... password ..."
  },

NOTES:

  1. I recommend you override the email/password values when deploying, using something like Azure's override of appsettings.json.
  2. Because the role "SuperAdmin" is so powerful I recommend you only have one user with that role. You use the "SuperAdmin" user to set up the other admin users and use them for your normal admin jobs.

permissionaccesscontrol2's People

Contributors

jonpsmith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

permissionaccesscontrol2's Issues

What needs to be unique?

Hello there,

Again thanks for providing this code, the more I work with it the more impressed I am. However I would like to report a possible issue in the sample application that may happen in a real world scenario. For instance, the demo application comes with the following company definitions:

4U Inc.|West Coast|San Fran|SF Dress4U, SF Tie4U, SF Shirt4U
4U Inc.|West Coast|LA|LA Dress4U, LA Tie4U, LA Shirt4U
4U Inc.|East Coast|NY Dress4U, Boston Shirt4U
Pets2 Ltd.|London|Cats Place, Kitten Place
Pets2 Ltd.|Bristol|Dogs Place, Puppy Place

However, if I change it to:

4U Inc.|West Coast|San Fran|SF Dress4U, SF Tie4U, SF Shirt4U
4U Inc.|West Coast|LA|LA Dress4U, LA Tie4U, LA Shirt4U
4U Inc.|East Coast|NY Dress4U, Boston Shirt4U
Pets2 Ltd.|West Coast|Cats Place, Kitten Place
Pets2 Ltd.|Bristol|Dogs Place, Puppy Place

Then I get a System.InvalidOperationException: 'Sequence contains more than one element' because the FirstOfDefault in DemoSetup:55 fails. However, isn't this a possible situation? (e.g. Pets2 Ltd. also migrating to the West Coast)?

I know it is a demo, but should I worry about non-uniqueness of other entities in the code?

Cancellation token may be "default" rather than "new"?

Hello,

Thanks for your great project. I'm currently integrating it into my own open source project and noticed that it would be more logical to use default(CancellationToken) instead of new CancellationToken() in the following code:

https://github.com/JonPSmith/PermissionAccessControl2/blob/master/DataLayer/EfCode/ExtraAuthorizeDbContext.cs#L48

https://github.com/JonPSmith/PermissionAccessControl2/blob/master/DataLayer/EfCode/CompanyDbContext.cs#L37

In my understanding, this means that you pass a not null cancellation token when it's null, thus cancelling the operation?

ScanAssemblesForDtos Issues

Could you explain why this code:
services.ConfigureGenericServicesEntities(typeof(MyDbContext), typeof(CompanyDbContext)) .ScanAssemblesForDtos(Assembly.GetAssembly(typeof(ListUsersDto))) .RegisterGenericServices();

Is causing so many errors e.g.
InvalidOperationException: The entity type 'CompanyAddress' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.

However, it does has a composite key.
modelBuilder.Entity<CompanyAddress>(entity => { entity.HasKey(e => new { e.CompanyId, e.AddressId }); .... }

Use Roles To Permissions in Angular 8 Core app 3.0 APIs

Hello @JonPSmith ,
Great solution to implement Roles and permission in asp.net core.
Quick question - can we use roles and permission in Angular 8 clientapp with core 3.0 APIs with ApplicationUser to authorize APIControllers?

Any pointer is greatly appreciated. Thanks again!

Unable to create an object of type 'ApplicationDbContext'

Hi John,

Thank you for your contributions!
Much appreciated.

Unfortunately the complexity of the code you've written is quite a mouthful for me and i could use some guidance.

I'm currently testing your code, but i get some errors when trying to migrate the ApplicationDbContext database. What i want to achieve is to add some personal data to each of the users, which I intend to store in the ApplicationDbContext.
However when i try to migrate the updated context i get the following error.

Add-migration CreateIdentitySchema -c ApplicationDbContext -v Using project '<ProjectName>'. Using startup project '<ProjectName>'. Build started... Build succeeded. C:\Program Files\dotnet\dotnet.exe exec --depsfile "C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\bin\Debug\netcoreapp2.2\<ProjectName>.deps.json" --additionalprobingpath C:\Users\<User>\.nuget\packages --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig "C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\bin\Debug\netcoreapp2.2\<ProjectName>.runtimeconfig.json" C:\Users\<User>\.nuget\packages\microsoft.entityframeworkcore.tools\2.2.6\tools\netcoreapp2.0\any\ef.dll migrations add CreateIdentitySchema --json --context ApplicationDbContext --verbose --no-color --prefix-output --assembly "C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\bin\Debug\netcoreapp2.2\<ProjectName>.dll" --startup-assembly "C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\bin\Debug\netcoreapp2.2\<ProjectName>.dll" --project-dir "C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\\" --language C# --working-dir "C:\Users\<User>\Documents\Repo\Dev\<ProjectName>" --root-namespace <ProjectName> Using assembly '<ProjectName>'. Using startup assembly '<ProjectName>'. Using application base 'C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\bin\Debug\netcoreapp2.2'. Using working directory 'C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>'. Using root namespace '<ProjectName>'. Using project directory 'C:\Users\<User>\Documents\Repo\Dev\<ProjectName>\<ProjectName>\'. Finding DbContext classes... Finding IDesignTimeDbContextFactory implementations... Finding application service provider... Finding IWebHost accessor... No CreateWebHostBuilder(string[]) method was found on type '<ProjectName>.Program'. No application service provider was found. Finding DbContext classes in the project... Found DbContext 'ApplicationUser'. Found DbContext 'Hydro'. Found DbContext 'ApplicationDbContext'. Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728 ---> System.MissingMethodException: No parameterless constructor defined for this object. at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean skipCheckThis, Boolean fillCache) at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass12_3.<FindContextTypes>b__13() --- End of inner exception stack trace --- at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass12_3.<FindContextTypes>b__13() at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func1 factory)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_1.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_01.<Execute>b__0() at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action) Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

How do i proceed from here.

Another question not related to any errors.
I'm also trying to modify the seed data, but ideally I want to do this as a company admin from a view. How would you go about changing that data dynamically? Do we get all the information we need by injecting the UserManager in the constructor to our views?

Repackage as a library (or equivalent)

Hi there,

First of all, thanks for sharing this code with the world!

This is not much an issue but rather a question. Would you consider repackaging the ideas and code presented in this repository as a library (or at least parts of it)? It would make it much easier to keep up with any eventual updates you would like to make to the code.

Invalid ObjectName

Hi Jon,
I have attached a log file of the error I am receiving when attempting to use Permanent for database setup. The Default Connection does work and creates the aspnet tables. For some reason, I am receiving an invalid object name error almost as if it wants to find a table called UsersToRoles, but I see no setup for a SQL object of that nature anywhere in the code. Hoping this is a super simple fix (interpreted as I am doing something wrong!)
Thanks!

InvalidObjectNameError.txt

Login does not work

Hi,

I downloaded your git repository as a zip file, extracted it and run PermissionAccessControl2 in Visual Studio 2019. No Problem so far. If I then try to login with any of the given users, nothing happens. No error no successful login.

Is it broken or is something wrong on my side?

Kind Regards,
Timo

RazorTagHelper Error

I have setup the PermissionsAccessControl2 as my start project but when i build i get an error so cant run it.
My error is
Severity Code Description Project File Line Suppression State
Error MSB4018 The "RazorTagHelper" task failed unexpectedly.
System.InvalidOperationException: DOTNET_HOST_PATH is not set
at Microsoft.AspNetCore.Razor.Tasks.DotNetToolTask.get_DotNetPath()
at Microsoft.AspNetCore.Razor.Tasks.DotNetToolTask.GenerateFullPathToTool()
at Microsoft.Build.Utilities.ToolTask.ComputePathToTool()
at Microsoft.Build.Utilities.ToolTask.Execute()
at Microsoft.AspNetCore.Razor.Tasks.DotNetToolTask.Execute()
at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
at Microsoft.Build.BackEnd.TaskBuilder.d__26.MoveNext() PermissionAccessControl2 C:\Users*******.nuget\packages\microsoft.aspnetcore.razor.design\2.2.0\build\netstandard2.0\Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets 79

SubGroup user and shop-level keys

Hi Jon,

I've got another question, hopefully a quick one ;-)

Using the company/group/retail outlet with an hierarchical data key as an example, what would be the best way to ensure that whenever Joe (LA divisional manager in the example) creates and saves an entity that should relate only to LA Shirt4U, this entity gets saved with the datakey for this shop, and not with Joe's division-wide data key?

Should it be set manually in those cases?

Sorry if there is a mechanism already implemented for achieving this, but I couldn't find it in the code yet.

Exception On "Permanent" Database creation

The following exception is thrown when using the "Permanent" Database option:

"Introducing FOREIGN KEY constraint 'FK_ShopSales_Tenants_TenantItemId' on table 'ShopSales' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors."

Missing remove role from user

Hello,
am I wrong or there is no method defined to remove one or more roles assigned to an user?
Any implementations hints?
Thank you.

SQL Outputs

Hi Jon,

Whenever I go to a new page the PAC2 spits our this SQL for every role in the database. Have a missed something?
Should these have been cached the first time the application starts?

exec sp_executesql N'SELECT TOP(1) [r].[RoleName], [r].[Description], [r].[PermissionsInRole] FROM [Security].[RolesToPermissions] AS [r] WHERE [r].[RoleName] = @__p_0',N'@__p_0 nvarchar(100)',@__p_0=N'CacheRole' go

Feature request - user specific permissions

Hey there, really nice articles about augmenting default authorization capabilities. One feature that could be really nice to add in my opinion would be to add permissions not only to roles but also to specific users.

This way, most permissions could be generalized as they currently are, and the system would be flexible enough to allow super user or other high privileges user to add/remove specific permissions on a per-user basis, eliminating one of the big downsides or role based authorization which often leads to multiple roles being created for very narrow use cases that are often specific to individual users and not groups of users.

PostgreSQL instead of MSSQL

Hi,

Is it possible to use PostgreSQL instead of SQL Server?

Is there anything particular to look our for?

Querying users with permissions

Hi Jon,

I've been using this code as a base, and I was wondering if I could get your input on a particular topic. This is more of a question than an issue though, sorry for bringing this up as an issue request.

Right now, I've been using the role/permissions to control access to individual controller's views and this has been working great. However, do you think it would be a good idea to use those permissions for more than just controlling access?

For example, let's say we are building a university management application like Microsoft's Contoso University example, and one of the requirements of the app is to only allow certain teachers to coordinate a course. A possible way to implement this would be to add a "CanCoordinateCourse" column in the Teachers table and check this flag accordingly. However, since the PermissionAccessControl skeleton code already gives a system for handling permissions, do you think it would be a good idea to create a "Permissions.CanCoordinateCourse" and store it in one of the roles?

The problem I see with the latter is that, if for example we would like to query all teachers that can coordinate a course, we would need to keep unpacking the list of permissions from the "_permissionsInRole" string, complicating the queries. On the other hand, in the former approach, every time a new "CanCordinateXXX" permission would need to be added we would need to add a new column to the database and therefore perform a migration.

How would you approach that?

No superuser setup and login not working

Hi, I just downloaded and run last version (updated for 3.1), but I found a couple of problems:

  • Login is not working using any of the credential available. It simply comes back to default page without authenticating the user.
  • The only login that gave an error was the superuser one, so I tracked the seed data process and found that the CheckAddSuperAdminAsync was never called. I added the call just after the call to CheckSeedDataAndUserAsync in Program.cs and now the superuser has been created. Unfortunately his login now behaves like all the others, without visible errors but no authentication either.

Thanks.

Sell menu not visible as a SalesAssistant

Looks like the permissions to show the Sell menu were accidentally changed from SalesRead || StockRead to SalesRead only, making the SalesAssistant unable to sell.

Invalid object name 'Tenants'

Getting exception on startup when running with DatabaseSetup=Permanent and SQL Server.
Order tables in DB was created success but not Tentants

JoYZXEfFhD

Add Migration: no suitable constructor found for entity type 'ModulesForUser'

Hello Jon,

In my .net core 2.2 project (so this uses EF Core 2.2), I've added your classes to my DbContext and when manually launching the migration, I get the following error, I just fixed it by adding a private parameterless constructor to ModulesForUser entity.

Side note: Instead of using IdentityUser, I use my own User entity that adds some fields to the standard IdentityUser, I will consider code changes but I don't think it causes troubles.

In the webapp's startup project:

>dotnet ef migrations add NewPermissionsSystem

System.InvalidOperationException: No suitable constructor found for entity type 'ModulesForUser'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'userId_', 'allowedPaidForModules_' in 'ModulesForUser(string userId_, PaidForModules allowedPaidForModules_)'.
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConstructorBindingConvention.Apply(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelBuilt(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.