<commit_before>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Signum.Engine.Maps;
using Signum.Entities.Authorization;
using Signum.Entities.Basics;
using Signum.Engine.DynamicQuery;
using Signum.Engine.Basics;
using Signum.Entities;
using Signum.Utilities.DataStructures;
using Signum.Utilities;
using Signum.Entities.DynamicQuery;
using System.Reflection;
using System.Linq.Expressions;
using Signum.Entities.Reflection;
using Signum.Utilities.Reflection;
using Signum.Utilities.ExpressionTrees;
using Signum.Engine.Linq;
using Signum.Engine.Exceptions;
using System.Data.SqlClient;
using System.Xml.Linq;

namespace Signum.Engine.Authorization
{
    public static partial class TypeAuthLogic
    {
        static readonly Variable<bool> queryFilterDisabled = Statics.ThreadVariable<bool>("queryFilterDisabled");
        public static IDisposable DisableQueryFilter()
        {
            if (queryFilterDisabled.Value) return null;
            queryFilterDisabled.Value = true;
            return new Disposable(() => queryFilterDisabled.Value = false);
        }

        static readonly Variable<bool> saveDisabled = Statics.ThreadVariable<bool>("saveDisabled");
        public static IDisposable DisableSave()
        {
            if (saveDisabled.Value) return null;
            saveDisabled.Value = true;
            return new Disposable(() => saveDisabled.Value = false);
        }

        const string CreatedKey = "Created";
        const string ModifiedKey = "Modified";

        static void Schema_Saving_Instance(IdentifiableEntity ident)
        {
            if (ident.IsNew)
            {
                var created = (List<IdentifiableEntity>)Transaction.UserData.GetOrCreate(CreatedKey, () => new List<IdentifiableEntity>());
                if (created.Contains(ident))
                    return;

                created.Add(ident);
            }
            else
            {
                if (IsCreatedOrModified(Transaction.TopParentUserData(), ident) ||
                   IsCreatedOrModified(Transaction.UserData, ident))
                    return;

                var modified = (List<IdentifiableEntity>)Transaction.UserData.GetOrCreate(ModifiedKey, () => new List<IdentifiableEntity>());

                modified.Add(ident);
            }

            Transaction.PreRealCommit -= Transaction_PreRealCommit;
            Transaction.PreRealCommit += Transaction_PreRealCommit;
        }

        private static bool IsCreatedOrModified(Dictionary<string, object> dictionary, IdentifiableEntity ident)
        {
            var modified = (List<IdentifiableEntity>)dictionary.TryGetC(ModifiedKey);
            if (modified != null && modified.Contains(ident))
                return true;

            var created = (List<IdentifiableEntity>)dictionary.TryGetC(CreatedKey);
            if (created != null && created.Contains(ident))
                return true;

            return false;
        }

        public static void RemovePreRealCommitChecking(IdentifiableEntity entity)
        {
            var created = (List<IdentifiableEntity>)Transaction.UserData.TryGetC(CreatedKey);
            if (created != null && created.Contains(entity))
                created.Remove(entity);

            var modified = (List<IdentifiableEntity>)Transaction.UserData.TryGetC(ModifiedKey);
            if (modified != null && modified.Contains(entity))
                modified.Remove(entity); 
        }

        static void Transaction_PreRealCommit(Dictionary<string, object> dic)
        {
            var modified = (List<IdentifiableEntity>)dic.TryGetC(ModifiedKey);

            if (modified.HasItems())
            {
                var groups = modified.GroupBy(e => e.GetType(), e => e.Id);

                //Assert before
                using (Transaction tr = Transaction.ForceNew())
                {
                    foreach (var gr in groups)
                        miAssertAllowed.GetInvoker(gr.Key)(gr.ToArray(), TypeAllowedBasic.Modify);

                    tr.Commit();
                }

                //Assert after
                foreach (var gr in groups)
                {
                    miAssertAllowed.GetInvoker(gr.Key)(gr.ToArray(), TypeAllowedBasic.Modify);
                }
            }

            var created = (List<IdentifiableEntity>)Transaction.UserData.TryGetC(CreatedKey);

            if (created.HasItems())
            {
                var groups = created.GroupBy(e => e.GetType(), e => e.Id);

                //Assert after
                foreach (var gr in groups)
                    miAssertAllowed.GetInvoker(gr.Key)(gr.ToArray(), TypeAllowedBasic.Create);
            }
        }


        static GenericInvoker<Action<int[], TypeAllowedBasic>> miAssertAllowed = 
            new GenericInvoker<Action<int[], TypeAllowedBasic>>((a, tab) => AssertAllowed<IdentifiableEntity>(a, tab));
        static void AssertAllowed<T>(int[] requested, TypeAllowedBasic typeAllowed)
            where T : IdentifiableEntity
        {
            using (DisableQueryFilter())
            {
                var found = requested.GroupsOf(1000).SelectMany(gr => Database.Query<T>().Where(a => gr.Contains(a.Id)).Select(a => new
                {
                    a.Id,
                    Allowed = a.IsAllowedFor(typeAllowed, ExecutionMode.InUserInterface),
                })).ToArray();

                if (found.Length != requested.Length)
                    throw new EntityNotFoundException(typeof(T), requested.Except(found.Select(a => a.Id)).ToArray());

                int[] notFound = found.Where(a => !a.Allowed).Select(a => a.Id).ToArray();
                if (notFound.Any())
                {
                    List<DebugData> debugInfo = Database.Query<T>().Where(a => notFound.Contains(a.Id))
                        .Select(a => a.IsAllowedForDebug(typeAllowed, ExecutionMode.InUserInterface)).ToList();

                    string details = debugInfo.ToString(a => "  {0} because {1}".Formato(a.Lite, a.Error), "\r\n");

                    throw new UnauthorizedAccessException(AuthMessage.NotAuthorizedTo0The1WithId2.NiceToString().Formato(
                        typeAllowed.NiceToString(),
                        notFound.Length == 1 ? typeof(T).NiceName() : typeof(T).NicePluralName(), notFound.CommaAnd()) + "\r\n" + details);
                }
            }
        }

        public static void AssertAllowed(this IIdentifiable ident, TypeAllowedBasic allowed)
        {
            AssertAllowed(ident, allowed, ExecutionMode.InUserInterface);
        }

        public static void AssertAllowed(this IIdentifiable ident, TypeAllowedBasic allowed, bool inUserInterface)
        {
            if (!ident.IsAllowedFor(allowed, inUserInterface))
                throw new UnauthorizedAccessException(AuthMessage.NotAuthorizedTo0The1WithId2.NiceToString().Formato(allowed.NiceToString().ToLower(), ident.GetType().NiceName(), ident.Id));
        }

        public static void AssertAllowed(this Lite<IIdentifiable> lite, TypeAllowedBasic allowed)
        {
            AssertAllowed(lite, allowed, ExecutionMode.InUserInterface);
        }<commit_msg>TypeAuthLogic.Conditions better message<commit_after>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Signum.Engine.Maps;
using Signum.Entities.Authorization;
using Signum.Entities.Basics;
using Signum.Engine.DynamicQuery;
using Signum.Engine.Basics;
using Signum.Entities;
using Signum.Utilities.DataStructures;
using Signum.Utilities;
using Signum.Entities.DynamicQuery;
using System.Reflection;
using System.Linq.Expressions;
using Signum.Entities.Reflection;
using Signum.Utilities.Reflection;
using Signum.Utilities.ExpressionTrees;
using Signum.Engine.Linq;
using Signum.Engine.Exceptions;
using System.Data.SqlClient;
using System.Xml.Linq;

namespace Signum.Engine.Authorization
{
    public static partial class TypeAuthLogic
    {
        static readonly Variable<bool> queryFilterDisabled = Statics.ThreadVariable<bool>("queryFilterDisabled");
        public static IDisposable DisableQueryFilter()
        {
            if (queryFilterDisabled.Value) return null;
            queryFilterDisabled.Value = true;
            return new Disposable(() => queryFilterDisabled.Value = false);
        }

        static readonly Variable<bool> saveDisabled = Statics.ThreadVariable<bool>("saveDisabled");
        public static IDisposable DisableSave()
        {
            if (saveDisabled.Value) return null;
            saveDisabled.Value = true;
            return new Disposable(() => saveDisabled.Value = false);
        }

        const string CreatedKey = "Created";
        const string ModifiedKey = "Modified";

        static void Schema_Saving_Instance(IdentifiableEntity ident)
        {
            if (ident.IsNew)
            {
                var created = (List<IdentifiableEntity>)Transaction.UserData.GetOrCreate(CreatedKey, () => new List<IdentifiableEntity>());
                if (created.Contains(ident))
                    return;

                created.Add(ident);
            }
            else
            {
                if (IsCreatedOrModified(Transaction.TopParentUserData(), ident) ||
                   IsCreatedOrModified(Transaction.UserData, ident))
                    return;

                var modified = (List<IdentifiableEntity>)Transaction.UserData.GetOrCreate(ModifiedKey, () => new List<IdentifiableEntity>());

                modified.Add(ident);
            }

            Transaction.PreRealCommit -= Transaction_PreRealCommit;
            Transaction.PreRealCommit += Transaction_PreRealCommit;
        }

        private static bool IsCreatedOrModified(Dictionary<string, object> dictionary, IdentifiableEntity ident)
        {
            var modified = (List<IdentifiableEntity>)dictionary.TryGetC(ModifiedKey);
            if (modified != null && modified.Contains(ident))
                return true;

            var created = (List<IdentifiableEntity>)dictionary.TryGetC(CreatedKey);
            if (created != null && created.Contains(ident))
                return true;

            return false;
        }

        public static void RemovePreRealCommitChecking(IdentifiableEntity entity)
        {
            var created = (List<IdentifiableEntity>)Transaction.UserData.TryGetC(CreatedKey);
            if (created != null && created.Contains(entity))
                created.Remove(entity);

            var modified = (List<IdentifiableEntity>)Transaction.UserData.TryGetC(ModifiedKey);
            if (modified != null && modified.Contains(entity))
                modified.Remove(entity); 
        }

        static void Transaction_PreRealCommit(Dictionary<string, object> dic)
        {
            var modified = (List<IdentifiableEntity>)dic.TryGetC(ModifiedKey);

            if (modified.HasItems())
            {
                var groups = modified.GroupBy(e => e.GetType(), e => e.Id);

                //Assert before
                using (Transaction tr = Transaction.ForceNew())
                {
                    foreach (var gr in groups)
                        miAssertAllowed.GetInvoker(gr.Key)(gr.ToArray(), TypeAllowedBasic.Modify);

                    tr.Commit();
                }

                //Assert after
                foreach (var gr in groups)
                {
                    miAssertAllowed.GetInvoker(gr.Key)(gr.ToArray(), TypeAllowedBasic.Modify);
                }
            }

            var created = (List<IdentifiableEntity>)Transaction.UserData.TryGetC(CreatedKey);

            if (created.HasItems())
            {
                var groups = created.GroupBy(e => e.GetType(), e => e.Id);

                //Assert after
                foreach (var gr in groups)
                    miAssertAllowed.GetInvoker(gr.Key)(gr.ToArray(), TypeAllowedBasic.Create);
            }
        }


        static GenericInvoker<Action<int[], TypeAllowedBasic>> miAssertAllowed = 
            new GenericInvoker<Action<int[], TypeAllowedBasic>>((a, tab) => AssertAllowed<IdentifiableEntity>(a, tab));
        static void AssertAllowed<T>(int[] requested, TypeAllowedBasic typeAllowed)
            where T : IdentifiableEntity
        {
            using (DisableQueryFilter())
            {
                var found = requested.GroupsOf(1000).SelectMany(gr => Database.Query<T>().Where(a => gr.Contains(a.Id)).Select(a => new
                {
                    a.Id,
                    Allowed = a.IsAllowedFor(typeAllowed, ExecutionMode.InUserInterface),
                })).ToArray();

                if (found.Length != requested.Length)
                    throw new EntityNotFoundException(typeof(T), requested.Except(found.Select(a => a.Id)).ToArray());

                int[] notFound = found.Where(a => !a.Allowed).Select(a => a.Id).ToArray();
                if (notFound.Any())
                {
                    List<DebugData> debugInfo = Database.Query<T>().Where(a => notFound.Contains(a.Id))
                        .Select(a => a.IsAllowedForDebug(typeAllowed, ExecutionMode.InUserInterface)).ToList();

                    string details = debugInfo.ToString(a => "  '{0}'  because {1}".Formato(a.Lite, a.Error), "\r\n");

                    throw new UnauthorizedAccessException(AuthMessage.NotAuthorizedTo0The1WithId2.NiceToString().Formato(
                        typeAllowed.NiceToString(),
                        notFound.Length == 1 ? typeof(T).NiceName() : typeof(T).NicePluralName(), notFound.CommaAnd()) + "\r\n" + details);
                }
            }
        }

        public static void AssertAllowed(this IIdentifiable ident, TypeAllowedBasic allowed)
        {
            AssertAllowed(ident, allowed, ExecutionMode.InUserInterface);
        }

        public static void AssertAllowed(this IIdentifiable ident, TypeAllowedBasic allowed, bool inUserInterface)
        {
            if (!ident.IsAllowedFor(allowed, inUserInterface))
                throw new UnauthorizedAccessException(AuthMessage.NotAuthorizedTo0The1WithId2.NiceToString().Formato(allowed.NiceToString().ToLower(), ident.GetType().NiceName(), ident.Id));
        }

        public static void AssertAllowed(this Lite<IIdentifiable> lite, TypeAllowedBasic allowed)
        {
            AssertAllowed(lite, allowed, ExecutionMode.InUserInterface);
        }
{'fraction_non_alphanumeric': 0.10299508888427751, 'fraction_numerical': 0.0017984367434460814, 'mean_word_length': 2.202214839424142, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3020047', 'n_tokens_mistral': 3710, 'n_tokens_neox': 3617, 'n_words': 706}