/src/service/services/version1/Authorize.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using PipServices3.Commons.Refer;
using PipServices3.Rpc.Auth;
using PipServices3.Rpc.Services;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Pip.Services.SampleFacade.Build;
using Pip.Services.SampleFacade.Operations.Version1;
using System.Collections.Generic;
using PipServices3.Commons.Errors;
using System.Linq;
using Microsoft.Extensions.Primitives;

namespace Pip.Services.SampleFacade.Services.Version1
{
    public class AuthorizerV1 : IReferenceable
    {
        private BasicAuthorizer _basicAuth = new BasicAuthorizer();
        private RoleAuthorizer _roleAuth = new RoleAuthorizer();
        private OwnerAuthorizer _ownerAuth = new OwnerAuthorizer();

        public void SetReferences(IReferences references)
        {
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> Anybody()
        {
            return _basicAuth.Anybody();
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> Signed()
        {
			return _basicAuth.Signed();
		}

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> Owner(string idParam = "user_id")
        {
            return _ownerAuth.Owner(idParam);
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> OwnerOrAdmin(string idParam = "user_id")
        {
            return async (request, response, user, routeData, next) =>
            {
                if (user == null || !user.Identity.IsAuthenticated)
                {
                    await HttpResponseSender.SendErrorAsync(
                        response,
                        new UnauthorizedException(
                            null, "NOT_SIGNED",
                            "User must be signed in to perform this operation"
                        ).WithStatus(401)
                    );
                }
                else
                {
                    var identity = user.Identity as ClaimsIdentity;
                    var userIdClaim = identity?.Claims.FirstOrDefault(c =>
                        c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier");

					if (!request.Query.TryGetValue(idParam, out StringValues userId))
					{
						userId = routeData.Values.TryGetValue(idParam, out object idValue) 
                            ? new StringValues(idValue.ToString())
                            : new StringValues();
					}

					if (userIdClaim?.Value != userId.ToString())
                    {
                        await HttpResponseSender.SendErrorAsync(
                            response,
                            new UnauthorizedException(
                                null, "FORBIDDEN",
                                "Only data owner can perform this operation"
                            ).WithStatus(403)
                        );
                    }
                    else
                    {
                        await next();
                    }
                }
            };

            //return _ownerAuth.OwnerOrAdmin(idParam);
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> SiteRoles(string[] roles, string idParam = "site_id")
        {
            return async (HttpRequest request, HttpResponse response, ClaimsPrincipal user, RouteData routeData, Func<Task> next) =>
            {
                var sessionUser = GetContextItem<SessionUserV1>(request, "user");

                if (sessionUser == null)
                {
                    await HttpResponseSender.SendErrorAsync(response, new UnauthorizedException(
                        null, "NOT_SIGNED",
                        "User must be signed in to perform this operation",
                        null
                    ).WithStatus(401));

                    return;
                }

                var siteId = routeData.Values["site_id"];
                var authorized = sessionUser.Roles.Contains("admin");

                if (siteId != null && !authorized)
                {
					foreach (var role in roles)
					{
                        authorized = authorized || sessionUser.Roles.Contains(siteId + ":" + role);
                    }
                }

                if (!authorized)
                {
                    await HttpResponseSender.SendErrorAsync(response, new UnauthorizedException(
                            null, "NOT_IN_SITE_ROLE",
                            "User must be site:" + string.Join(" or site:", roles) + " to perform this operation"
                        ).WithDetails("roles", roles).WithStatus(403));

                    return;
                }

                await next();
            };
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> Admin()
        {
            return _roleAuth.UserInRole("admin");
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> SiteAdmin(string idParam = "site_id")
        {
            return SiteRoles(new[] { "admin" }, idParam);
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> SiteManager(string idParam = "site_id")
        {
            return SiteRoles(new[] { "admin", "manager" }, idParam);
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> SiteUser(string idParam = "site_id")
        {
            return SiteRoles(new[] { "admin", "manager", "user" }, idParam);
        }

        public Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Func<Task>, Task> SiteAdminOrOwner(string userIdParam = "user_id", string siteIdParam = "site_id")
        {
            return async (HttpRequest request, HttpResponse response, ClaimsPrincipal user, RouteData routeData, Func<Task> next) =>
            {
                var sessionUser = GetContextItem<SessionUserV1>(request, "user");

                if (sessionUser == null)
                {
                    await HttpResponseSender.SendErrorAsync(response, new UnauthorizedException(
                        null, "NOT_SIGNED",
                        "User must be signed in to perform this operation",
                        null
                    ).WithStatus(401));

                    return;
                }

                var userId = request.Query[userIdParam].ToString();

				if (userId == null || userId != sessionUser.Id)
				{
					var siteId = request.Query[siteIdParam].ToString();
					var authorized = sessionUser.Roles.Contains("admin")
                        || sessionUser.Roles.Contains(siteId + "admin");

					if (!authorized)
					{
						await HttpResponseSender.SendErrorAsync(response, new UnauthorizedException(
								null, "NOT_IN_SITE_ROLE",
								"User must be site:admin to perform this operation"
							).WithDetails("roles", new[] { "admin" }).WithStatus(403));

						return;
					}
				}

				await next();
            };
        }

        private static T GetContextItem<T>(HttpRequest request, string name)
            where T : class
        {
            if (request != null && request.HttpContext.Items.TryGetValue(name, out object item))
            {
                return item as T;
            }

            return null;
        }
    }
}