Skip to content

Commit

Permalink
many changes to support
Browse files Browse the repository at this point in the history
  • Loading branch information
olmobrutall committed Feb 3, 2020
1 parent 9465d4a commit 6e8aac6
Show file tree
Hide file tree
Showing 25 changed files with 261 additions and 185 deletions.
8 changes: 8 additions & 0 deletions Signum.Entities/EnumMessages.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@


using Signum.Utilities;
using System;
using System.ComponentModel;

namespace Signum.Entities
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class AllowUnathenticatedAttribute : Attribute
{

}

public enum OperationMessage
{
[Description("Create...")]
Expand Down Expand Up @@ -264,6 +271,7 @@ public enum CalendarMessage
Today,
}

[AllowUnathenticated]
public enum JavascriptMessage
{
[Description("Choose a type")]
Expand Down
106 changes: 68 additions & 38 deletions Signum.React/Facades/ReflectionServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,13 @@ public static object GetCurrentValidCulture()
t.IsEnum && !t.Name.EndsWith("Query") && !t.Name.EndsWith("Message"))
.ToDictionaryEx(GetTypeName, "Types"));

public static void RegisterLike(Type type)
public static Dictionary<string, Func<bool>> OverrideIsNamespaceAllowed = new Dictionary<string, Func<bool>>();

public static void RegisterLike(Type type, Func<bool> allowed)
{
TypesByName.Reset();
EntityAssemblies.GetOrCreate(type.Assembly).Add(type.Namespace!);
OverrideIsNamespaceAllowed[type.Namespace!] = allowed;
}

internal static void Start()
Expand All @@ -64,54 +67,68 @@ internal static void Start()
const BindingFlags instanceFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
const BindingFlags staticFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly;

public static event Action<TypeInfoTS, Type>? AddTypeExtension;
static TypeInfoTS OnAddTypeExtension(TypeInfoTS ti, Type t)
public static event Func<TypeInfoTS, Type, TypeInfoTS?>? TypeExtension;
static TypeInfoTS? OnTypeExtension(TypeInfoTS ti, Type t)
{
foreach (var a in AddTypeExtension.GetInvocationListTyped())
a(ti, t);
foreach (var a in TypeExtension.GetInvocationListTyped())
{
ti = a(ti, t)!;
if (ti == null)
return null;
}

return ti;
}

public static event Action<MemberInfoTS, PropertyRoute>? AddPropertyRouteExtension;
static MemberInfoTS OnAddPropertyRouteExtension(MemberInfoTS mi, PropertyRoute m)
public static event Func<MemberInfoTS, PropertyRoute, MemberInfoTS?>? PropertyRouteExtension;
static MemberInfoTS? OnPropertyRouteExtension(MemberInfoTS mi, PropertyRoute m)
{
if (AddPropertyRouteExtension == null)
if (PropertyRouteExtension == null)
return mi;

foreach (var a in AddPropertyRouteExtension.GetInvocationListTyped())
a(mi, m);
foreach (var a in PropertyRouteExtension.GetInvocationListTyped())
{
mi = a(mi, m)!;
if (mi == null)
return null;
}

return mi;
}


public static event Action<MemberInfoTS, FieldInfo>? AddFieldInfoExtension;
static MemberInfoTS OnAddFieldInfoExtension(MemberInfoTS mi, FieldInfo m)
public static event Func<MemberInfoTS, FieldInfo, MemberInfoTS?>? FieldInfoExtension;
static MemberInfoTS? OnFieldInfoExtension(MemberInfoTS mi, FieldInfo m)
{
if (AddFieldInfoExtension == null)
if (FieldInfoExtension == null)
return mi;

foreach (var a in AddFieldInfoExtension.GetInvocationListTyped())
a(mi, m);
foreach (var a in FieldInfoExtension.GetInvocationListTyped())
{
mi = a(mi, m)!;
if (mi == null)
return null;
}

return mi;
}

public static event Action<OperationInfoTS, OperationInfo, Type>? AddOperationExtension;
static OperationInfoTS OnAddOperationExtension(OperationInfoTS oi, OperationInfo o, Type type)
public static event Func<OperationInfoTS, OperationInfo, Type, OperationInfoTS?>? OperationExtension;
static OperationInfoTS? OnOperationExtension(OperationInfoTS oi, OperationInfo o, Type type)
{
if (AddOperationExtension == null)
if (OperationExtension == null)
return oi;

foreach (var a in AddOperationExtension.GetInvocationListTyped())
a(oi, o, type);
foreach (var a in OperationExtension.GetInvocationListTyped())
{
oi = a(oi, o, type)!;
if (oi == null)
return null;
}

return oi;
}



public static HashSet<Type> ExcludeTypes = new HashSet<Type>();

internal static Dictionary<string, TypeInfoTS> GetTypeInfoTS()
Expand Down Expand Up @@ -167,7 +184,7 @@ where typeof(ModelEntity).IsAssignableFrom(type) && !type.IsAbstract
where !type.IsEnumEntity() && !ReflectionServer.ExcludeTypes.Contains(type)
let descOptions = LocalizedAssembly.GetDescriptionOptions(type)
let allOperations = !type.IsEntity() ? null : OperationLogic.GetAllOperationInfos(type)
select KeyValuePair.Create(GetTypeName(type), OnAddTypeExtension(new TypeInfoTS
select KeyValuePair.Create(GetTypeName(type), OnTypeExtension(new TypeInfoTS
{
Kind = KindOfType.Entity,
FullName = type.FullName!,
Expand All @@ -182,7 +199,7 @@ select KeyValuePair.Create(GetTypeName(type), OnAddTypeExtension(new TypeInfoTS
QueryDefined = queries.QueryDefined(type),
Members = PropertyRoute.GenerateRoutes(type)
.Where(pr => InTypeScript(pr))
.ToDictionary(p => p.PropertyString(), p =>
.Select(p =>
{
var validators = Validator.TryGetPropertyValidator(p)?.Validators;

Expand All @@ -196,19 +213,24 @@ select KeyValuePair.Create(GetTypeName(type), OnAddTypeExtension(new TypeInfoTS
Unit = UnitAttribute.GetTranslation(p.PropertyInfo?.GetCustomAttribute<UnitAttribute>()?.UnitName),
Type = new TypeReferenceTS(IsId(p) ? PrimaryKey.Type(type).Nullify() : p.PropertyInfo!.PropertyType, p.Type.IsMList() ? p.Add("Item").TryGetImplementations() : p.TryGetImplementations()),
IsMultiline = validators?.OfType<StringLengthValidatorAttribute>().FirstOrDefault()?.MultiLine ?? false,
IsVirtualMList = p.IsVirtualMList(),
IsVirtualMList = p.IsVirtualMList(),
MaxLength = validators?.OfType<StringLengthValidatorAttribute>().FirstOrDefault()?.Max.DefaultToNull(-1),
PreserveOrder = settings.FieldAttributes(p)?.OfType<PreserveOrderAttribute>().Any() ?? false,
};

return OnAddPropertyRouteExtension(mi, p);
}),
return KeyValuePair.Create(p.PropertyString(), OnPropertyRouteExtension(mi, p)!);
})
.Where(kvp => kvp.Value != null)
.ToDictionaryEx("properties"),

Operations = allOperations == null ? null : allOperations.ToDictionary(oi => oi.OperationSymbol.Key, oi => OnAddOperationExtension(new OperationInfoTS(oi), oi, type)),
HasConstructorOperation = allOperations != null && allOperations.Any(oi => oi.OperationType == OperationType.Constructor),
Operations = allOperations == null ? null : allOperations.Select(oi => KeyValuePair.Create(oi.OperationSymbol.Key, OnOperationExtension(new OperationInfoTS(oi), oi, type)!)).Where(kvp => kvp.Value != null).ToDictionaryEx("operations"),

RequiresEntityPack = allOperations != null && allOperations.Any(oi => oi.HasCanExecute != null),

}, type))).ToDictionaryEx("entities");
}, type)))
.Where(kvp => kvp.Value != null)
.ToDictionaryEx("entities");

return result;
}
Expand Down Expand Up @@ -248,19 +270,23 @@ where type.IsEnum
where descOptions != DescriptionOptions.None
let kind = type.Name.EndsWith("Query") ? KindOfType.Query :
type.Name.EndsWith("Message") ? KindOfType.Message : KindOfType.Enum
select KeyValuePair.Create(GetTypeName(type), OnAddTypeExtension(new TypeInfoTS
select KeyValuePair.Create(GetTypeName(type), OnTypeExtension(new TypeInfoTS
{
Kind = kind,
FullName = type.FullName!,
NiceName = descOptions.HasFlag(DescriptionOptions.Description) ? type.NiceName() : null,
Members = type.GetFields(staticFlags)
.Where(fi => kind != KindOfType.Query || queries.QueryDefined(fi.GetValue(null)!))
.ToDictionary(fi => fi.Name, fi => OnAddFieldInfoExtension(new MemberInfoTS
.Select(fi => KeyValuePair.Create(fi.Name, OnFieldInfoExtension(new MemberInfoTS
{
NiceName = fi.NiceName(),
IsIgnoredEnum = kind == KindOfType.Enum && fi.HasAttribute<IgnoreAttribute>()
}, fi)),
}, type))).ToDictionaryEx("enums");
}, fi)!))
.Where(a=>a.Value != null)
.ToDictionaryEx("query"),
}, type)))
.Where(a => a.Value != null)
.ToDictionaryEx("enums");

return result;
}
Expand All @@ -271,7 +297,7 @@ public static Dictionary<string, TypeInfoTS> GetSymbolContainers(IEnumerable<Typ

var result = (from type in allTypes
where type.IsStaticClass() && type.HasAttribute<AutoInitAttribute>()
select KeyValuePair.Create(GetTypeName(type), OnAddTypeExtension(new TypeInfoTS
select KeyValuePair.Create(GetTypeName(type), OnTypeExtension(new TypeInfoTS
{
Kind = KindOfType.SymbolContainer,
FullName = type.FullName!,
Expand All @@ -280,13 +306,15 @@ select KeyValuePair.Create(GetTypeName(type), OnAddTypeExtension(new TypeInfoTS
.Where(s =>
s.FieldInfo != null && /*Duplicated like in Dynamic*/
s.IdOrNull.HasValue /*Not registered*/)
.ToDictionary(s => s.FieldInfo.Name, s => OnAddFieldInfoExtension(new MemberInfoTS
.Select(s => KeyValuePair.Create(s.FieldInfo.Name, OnFieldInfoExtension(new MemberInfoTS
{
NiceName = s.FieldInfo.NiceName(),
Id = s.IdOrNull!.Value.Object
}, s.FieldInfo))
}, s.FieldInfo)!))
.Where(a => a.Value != null)
.ToDictionaryEx("fields"),
}, type)))
.Where(a => a.Value.Members.Any())
.Where(a => a.Value != null && a.Value.Members.Any())
.ToDictionaryEx("symbols");

return result;
Expand Down Expand Up @@ -340,9 +368,11 @@ public class TypeInfoTS
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "toStringFunction")]
public string? ToStringFunction { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, PropertyName = "queryDefined")]
public bool QueryDefined { get; internal set; }
public bool QueryDefined { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "members")]
public Dictionary<string, MemberInfoTS> Members { get; set; } = null!;
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, PropertyName = "hasConstructorOperation")]
public bool HasConstructorOperation { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "operations")]
public Dictionary<string, OperationInfoTS>? Operations { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore, PropertyName = "requiresEntityPack")]
Expand Down
3 changes: 3 additions & 0 deletions Signum.React/Facades/SignumServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Signum.Engine.Maps;
using Signum.Engine.Operations;
using Signum.Entities;
using Signum.Entities.Basics;
using Signum.React.ApiControllers;
using Signum.React.Filters;
using Signum.React.Json;
Expand Down Expand Up @@ -85,6 +86,8 @@ public static void Start(IApplicationBuilder app, IWebHostEnvironment hostingEnv
SignumControllerFactory.RegisterArea(MethodInfo.GetCurrentMethod()!);

ReflectionServer.Start();
ReflectionServer.OverrideIsNamespaceAllowed.Add(typeof(DayOfWeek).Namespace!, () => UserHolder.Current != null);
ReflectionServer.OverrideIsNamespaceAllowed.Add(typeof(CollectionMessage).Namespace!, () => UserHolder.Current != null);
}

public static EntityPackTS GetEntityPack(Entity entity)
Expand Down
5 changes: 2 additions & 3 deletions Signum.React/Scripts/Constructor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Dic } from './Globals';
import { Entity, ModifiableEntity, SelectorMessage, EntityPack } from './Signum.Entities';
import { Type, getTypeInfo, OperationType, New, OperationInfo, PropertyRoute } from './Reflection';
import { Type, getTypeInfo, OperationType, New, OperationInfo, PropertyRoute, tryGetTypeInfo } from './Reflection';
import SelectorModal from './SelectorModal';
import * as Operations from './Operations';
import * as Navigator from './Navigator';
Expand All @@ -20,10 +20,9 @@ export function constructPack(type: string | Type<any>, props?: any, pr?: Proper

const typeName = (type as Type<any>).typeName ?? type as string;

const ti = getTypeInfo(typeName);
const ti = tryGetTypeInfo(typeName);
if (ti)
pr = PropertyRoute.root(ti);


const c = customConstructors[typeName];
if (c)
Expand Down
10 changes: 5 additions & 5 deletions Signum.React/Scripts/Finder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import { TypeEntity, QueryEntity } from './Signum.Entities.Basics';

import {
Type, IType, EntityKind, QueryKey, getQueryNiceName, getQueryKey, isQueryDefined, TypeReference,
getTypeInfo, getTypeInfos, getEnumInfo, toMomentFormat, toNumbroFormat, PseudoType, EntityData,
TypeInfo, PropertyRoute, QueryTokenString
getTypeInfo, tryGetTypeInfos, getEnumInfo, toMomentFormat, toNumbroFormat, PseudoType, EntityData,
TypeInfo, PropertyRoute, QueryTokenString, getTypeInfos
} from './Reflection';

import SearchModal from './SearchControl/SearchModal';
Expand Down Expand Up @@ -223,7 +223,7 @@ export function findOptionsPathQuery(fo: FindOptions, extra?: any): any {
export function getTypeNiceName(tr: TypeReference) {

const niceName = tr.typeNiceName ??
getTypeInfos(tr)
tryGetTypeInfos(tr)
.map(ti => ti == undefined ? getSimpleTypeNiceName(tr.name) : (ti.niceName ?? ti.name))
.joinComma(External.CollectionMessage.Or.niceToString());

Expand Down Expand Up @@ -581,7 +581,7 @@ export function parseFindOptions(findOptions: FindOptions, qd: QueryDescription,
fo.columnOptions = mergeColumns(Dic.getValues(qd.columns), fo.columnOptionsMode ?? "Add", fo.columnOptions ?? []);

var qs: QuerySettings | undefined = querySettings[qd.queryKey];
const tis = getTypeInfos(qd.columns["Entity"].type);
const tis = tryGetTypeInfos(qd.columns["Entity"].type);


if (!fo.groupResults && (!fo.orderOptions || fo.orderOptions.length == 0)) {
Expand Down Expand Up @@ -1515,7 +1515,7 @@ export const entityFormatRules: EntityFormatRule[] = [
{
name: "View",
isApplicable: row => true,
formatter: (row, columns, sc) => !row.entity || !Navigator.isNavigable(row.entity.EntityType, undefined, true) ? undefined :
formatter: (row, columns, sc) => !row.entity || !Navigator.isNavigable(row.entity.EntityType, { isSearch: true }) ? undefined :
<EntityLink lite={row.entity}
inSearch={true}
onNavigated={sc?.handleOnNavigated}
Expand Down
10 changes: 5 additions & 5 deletions Signum.React/Scripts/Frames/FrameModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ValidationError } from '../Services'
import { ifError } from '../Globals'
import { TypeContext, StyleOptions, EntityFrame, IHasChanges } from '../TypeContext'
import { Entity, Lite, ModifiableEntity, JavascriptMessage, NormalWindowMessage, getToString, EntityPack, entityInfo, isEntityPack, isLite, is, isEntity } from '../Signum.Entities'
import { getTypeInfo, PropertyRoute, ReadonlyBinding, GraphExplorer, isTypeModel } from '../Reflection'
import { getTypeInfo, PropertyRoute, ReadonlyBinding, GraphExplorer, isTypeModel, tryGetTypeInfo } from '../Reflection'
import { ValidationErrors, ValidationErrorHandle } from './ValidationErrors'
import { renderWidgets, WidgetContext, renderEmbeddedWidgets } from './Widgets'
import { EntityOperationContext } from '../Operations'
Expand Down Expand Up @@ -67,7 +67,7 @@ export const FrameModal = React.forwardRef(function FrameModal(p: FrameModalProp
}));

const typeName = getTypeName(p.entityOrPack);
const typeInfo = getTypeInfo(typeName);
const typeInfo = tryGetTypeInfo(typeName);


React.useEffect(() => {
Expand Down Expand Up @@ -214,7 +214,7 @@ export const FrameModal = React.forwardRef(function FrameModal(p: FrameModalProp
};

const styleOptions: StyleOptions = {
readOnly: p.readOnly != undefined ? p.readOnly : Navigator.isReadOnly(pc.pack),
readOnly: p.readOnly != undefined ? p.readOnly : Navigator.isReadOnly(pc.pack, { isEmbedded: p.propertyRoute?.member?.type.isEmbedded }),
frame: frame,
};

Expand Down Expand Up @@ -316,9 +316,9 @@ export function FrameModalTitle({ pack, pr, title, getViewPromise }: { pack?: En
if (entity == undefined || entity.isNew)
return undefined;

const ti = getTypeInfo(entity.Type);
const ti = tryGetTypeInfo(entity.Type);

if (ti == undefined || !Navigator.isNavigable(ti, false)) //Embedded
if (ti == undefined || !Navigator.isNavigable(ti)) //Embedded
return undefined;

return (
Expand Down
5 changes: 2 additions & 3 deletions Signum.React/Scripts/Frames/FramePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ export default function FramePage(p: FramePageProps) {
const mounted = useMounted();
const forceUpdate = useForceUpdate();

const type = getTypeInfo(p.match.params.type).name;
const ti = getTypeInfo(p.match.params.type);
const type = ti.name;
const id = p.match.params.id;

const ti = getTypeInfo(type);

useTitle(state?.pack.entity.toStr ?? "", [state?.pack.entity]);

React.useEffect(() => {
Expand Down
Loading

2 comments on commit 6e8aac6

@olmobrutall
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improving Security and Performance with optional TypeInfo

This commit is a big change that solves the main security concern with Signum Framework.

The client-side part of Signum.React uses extensively information coming from api/reflection/types. The result contains metadata for entities, enums, messages and queries, including translated names, property types, format and units, and security information.

Before this change, this means that if an anonymous user that doesn't have access to see InvoiceEntity gets the metadata for InvoiceEntity anyway, including all the properties, types and translations, etc..

After this change, a user will only get metadata information for types he is authorized for, at least, read in the UI.

This has some consequences:

  • You can not longer get translated names for types you don't have access to, like: InvoiceEntity.nicePluralName().
  • Enums/Messages/Operations/Symbols have no way to be authorized (will be too much of a hassle), so they get automatically authorized when the namespace is authorized.
  • Namespaces that contain no entity can still be used using RegisterLike, but now requires a new lambda to determine if the namespace should be allowed. Example here.
  • In order to get access to Types when UserEntity.Current is null (not logged yet) you need to use the new [AllowUnathenticatedAttribute]

@MehdyKarimpour
Copy link
Contributor

@MehdyKarimpour MehdyKarimpour commented on 6e8aac6 Feb 4, 2020 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.