/// <summary> /// Verify if claimed Type with claimed name and value exist and there is a valid connection between it /// and modelType with claimed id property and value /// </summary> private bool verifyConnection( Type claimType, string claimName, object claimValue, Type modelType, PropertyInfo idProperty, object idValue) { var claimId = claimType .GetProperties() .Where(prop => prop.IsDefined(typeof(KeyAttribute), true)) .FirstOrDefault(); var claimIdValue = claimId.GetValue(claimValue); if (claimIdValue == null) { return(false); } return(Utils.VerifyConnection( APIUtils.GetIQueryable(dbContext, modelType.Name), modelType, claimType, idProperty, idValue.ToString(), claimName, claimId, claimIdValue.ToString())); }
private dynamic ReadResource( IRequest request, HttpRequestMethod requestMethod, PermissionHandler <TRelation, TUser> permissionHandler) { var result = APIUtils.GetResource(dbContext, request); if (result == null) { return(new NotFoundResult()); } var serializerSettings = JsonConvert.DefaultSettings(); serializerSettings.ContractResolver = new APIJsonResolver <TRelation, TUser> { PermissionHandler = permissionHandler, ModelAction = ModelAction.Read, RequestMethod = requestMethod, IRequest = request, DbContext = dbContext, EngineService = EngineService, IncludeBindNever = true, IncludeKey = true }; EngineService.OnResourceRead(dbContext, request, result); Response.Headers.Add("Server", "NG-API"); return(new OkObjectResult(JsonConvert.SerializeObject(result, serializerSettings))); }
private dynamic DeleteResource( IRequest request, TRelation relationType, IRequest relatedRequest, PermissionHandler <TRelation, TUser> permissionHandler) { var resource = APIUtils.GetResource(dbContext, request) as object; if (resource == null) { return(new NotFoundResult()); } if (!(resource is RootModel)) { return(new BadRequestObjectResult( new APIError { Code = StatusCodes.Status424FailedDependency, Message = "Only api architecture based models can be deleted" } )); } var globalIntraction = dbContext.MagicDbSet(EngineService.MapRelationToType("Global")) as IEnumerable <dynamic>; var globalRelation = Enum.Parse(enumType: typeof(TRelation), value: "Global"); var isCreator = globalIntraction .Where(predicate: intraction => intraction.IntractionType.Equals(globalRelation) && intraction.Valid && ((intraction.ValidUntil == null || intraction.ValidUntil.HasValue == false) || (intraction.ValidUntil.HasValue && System.DateTime.Now.CompareTo(intraction.ValidUntil.Value) < 0)) && (intraction.CreatorId.Equals(permissionHandler.getRequesterID()) && intraction.FirstModelId.Equals(permissionHandler.getRequesterID()) && intraction.SecondModelId.Equals(resource.GetKeyPropertyValue()))) .Any(); if (!isCreator) { return(new BadRequestObjectResult( new APIError { Code = StatusCodes.Status403Forbidden, Message = "Only object creator (The owner) can delete it" })); } resource.GetType().GetProperty(nameof(RootModel.Deactivated)).SetValue(resource, true); dbContext.Entry(resource).Property(nameof(RootModel.Deactivated)).IsModified = true; dbContext.SaveChanges(); EngineService.OnResourceDeleted(dbContext, request, resource); return(new OkResult()); }
public dynamic GeneralAccessChainValidation( IRequest Request, MemberInfo Type, ModelAction ModelAction, HttpRequestMethod RequestMethod, TRelation RelationType, object ModelItself, object TypeValue = null, bool DefaultPolicy = false) { var typeName = Type.GetType().GetProperty("Name").GetValue(Type) as string; var modelPermissions = Type.GetCustomAttributes <ModelPermissionAttribute> (); var requirements = modelPermissions .AsParallel() .Where(requirement => requirement.ModelAction == ModelAction && requirement.RequestMethod == RequestMethod) .ToList(); if (requirements != null && requirements.Count > 0) { foreach (var requirement in requirements) { var validation = APIUtils.InvokeMethod( requirement.AccessChainResolver, "Validate", new object[] { DbContext, RequesterID, Request, ModelItself, typeName, TypeValue, ModelAction, RequestMethod, RelationType }); if (!(validation is bool && (bool)validation)) { return("Requirement validation with name { " + requirement.AccessChainResolver.Name + " } has been failed with result { " + validation + " }"); } } } else if (DefaultPolicy == false) { return("Requested action { " + ModelAction + " } is not valid for request method { " + RequestMethod + " }, or this action is not valid for this type { " + typeName + " } at all"); } return(true); }
public NeutronGeneralAPI( DbContext dbContext, UserManager <TUser> userManager, IModelParser modelParser, IApiEngineService <TRelation, TUser> engineService) { this.UserManager = userManager; this.dbContext = dbContext; this.ModelParser = modelParser; this.EngineService = engineService; this.Utils = new APIUtils(); }
private void ResolveRelationDependency( object model, string requesterID, IRequest request, IRequest relatedRequest, TRelation intractionType, HttpRequestMethod httpRequestMethod) { var propertyList = model.GetType() .GetProperties() .Where(property => property.IsDefined(typeof(RelationDependentValueAttribute), true)) .ToList(); var updateModel = false; foreach (var item in propertyList) { var attribute = item.GetCustomAttribute <RelationDependentValueAttribute> (); var relationDependentResolver = httpRequestMethod == HttpRequestMethod.Post ? attribute.OnRelationCreated : attribute.OnReleationDeleted; var result = APIUtils.InvokeMethod( relationDependentResolver, "OnRelationEvent", new object[] { dbContext, model, requesterID, request, relatedRequest, intractionType, httpRequestMethod }); if (result != null) { model = result; updateModel = true; } } if (updateModel) { dbContext.Update(model); } }
private dynamic PatchResource( IRequest request, HttpRequestMethod requestMethod, PermissionHandler <TRelation, TUser> permissionHandler, string jsonData) { var jsonResolver = new APIJsonResolver <TRelation, TUser> { DbContext = dbContext, PermissionHandler = permissionHandler, ModelAction = ModelAction.Create, RequestMethod = requestMethod, IRequest = request, EngineService = EngineService, IncludeKey = true, IncludeBindNever = false }; var serializerSettings = JsonConvert.DefaultSettings(); serializerSettings.ContractResolver = jsonResolver; var model = JsonConvert.DeserializeObject( jsonData, request.Temp_ResourceType, serializerSettings); var oldModel = APIUtils.GetResource(dbContext, request); if (oldModel == null) { return(new NotFoundResult()); } if (oldModel is IdentityUser) { return(new BadRequestObjectResult(new APIError { Message = "User model can not edited by general api, this must be handled using an special AccountController." })); } var modelKey = model.GetType().GetPropertiesWithAttribute(typeof(KeyAttribute)).FirstOrDefault(); if (modelKey == null) { return(new ForbidResult()); } if (request.IdentifierName != modelKey.Name) { var identifierProperty = model.GetType().GetCustomAttributes <IdentifierValidatorAttribute> () .Where(attribute => attribute.PropertyName == request.IdentifierName) .FirstOrDefault(); if (identifierProperty == null) { return(new ForbidResult()); } modelKey.SetValue(model, modelKey.GetValue(oldModel)); } var modelKeyValue = modelKey.GetValue(model).ToString(); var isValidIdentifier = modelKeyValue != null && modelKeyValue != request.IdentifierValue; if (!isValidIdentifier || !verifyModelRelationChain(model)) { return(BadRequest(new APIError { Message = "Error: Invalid relation in received model, it can be happend when you are not " + "permitted for this action or there are some invalid id(s)." })); } // dbContext.Update (model); // ExcludeAttributes (model); IncludeAttributes(model); var intraction = new ModelInteraction <TRelation> { CreatorId = permissionHandler.getRequesterID(), FirstModelId = permissionHandler.getRequesterID(), SecondModelId = modelKeyValue, ModelAction = ModelAction.Update }; dbContext.MagicAddIntraction(intraction, EngineService.MapRelationToType("Global")); dbContext.SaveChanges(); EngineService.OnResourcePatched(dbContext, request, model); return(new OkResult()); }
private dynamic JoinResourceAsync( IRequest request, IRequest relatedRequest, TRelation relationType, PermissionHandler <TRelation, TUser> permissionHandler, HttpRequestMethod httpRequestMethod) { var oneWayRelation = relatedRequest == null || !relatedRequest.filledWithData(); if (oneWayRelation) { relatedRequest = request; } var resourceType = oneWayRelation ? typeof(TUser) : ModelParser.GetResourceType(request.ResourceName); var relatedResourceType = ModelParser.GetResourceType(relatedRequest.ResourceName); bool firstIdentifierNameIsKey = true; if (!oneWayRelation) { firstIdentifierNameIsKey = resourceType.GetProperties() .Where(property => property.Name.Equals(request.IdentifierName) && property.IsDefined(typeof(KeyAttribute), true)) .Any(); } bool secondIdentifierNameIsKey = relatedResourceType.GetProperties() .Where(prop => prop.Name.Equals(relatedRequest.IdentifierName) && prop.IsDefined(typeof(KeyAttribute), true)) .Any(); if (!firstIdentifierNameIsKey || !secondIdentifierNameIsKey) { return(BadRequest(new APIError { Message = "To create relation only key identifier is acceptable" })); } // Check whether request is exist or not var requestModel = oneWayRelation ? UserManager.FindByIdAsync(permissionHandler.getRequesterID()).Result : APIUtils.GetResource(dbContext, request); if (requestModel == null) { return(NotFound(request)); } // Check if relationType is valid for request var resourceCheck = permissionHandler.ModelValidation( Request: request, ModelType: resourceType, ModelAction: ModelAction.Relate, RequestMethod: httpRequestMethod, RelationType: relationType); if (!(resourceCheck is bool && (bool)resourceCheck)) { return(BadRequest(new APIError { Message = "Request Error: " + resourceCheck })); } // Check whether related request is exist or not var relatedRequestModel = APIUtils.GetResource(dbContext, relatedRequest); if (relatedRequestModel == null) { return(NotFound(relatedRequest)); } // Check if relationType is valid for related request var relatedSourceCheck = permissionHandler.ModelValidation( Request: relatedRequest, ModelType: relatedResourceType, ModelAction: ModelAction.Relate, RequestMethod: httpRequestMethod, RelationType: relationType); if (!(relatedSourceCheck is bool && (bool)relatedSourceCheck)) { return(BadRequest(new APIError { Message = "Request Error: " + relatedSourceCheck })); } if (!oneWayRelation) { // Check if relationType is valid for requesterID var userCheck = permissionHandler.ModelValidation( Request: request, ModelType: typeof(TUser), ModelAction: ModelAction.Relate, RequestMethod: httpRequestMethod, RelationType: relationType); if (!(userCheck is bool && (bool)userCheck)) { return(BadRequest(new APIError { Message = "Request Error: " + userCheck })); } } var intractionType = EngineService.MapRelationToType(relationType.ToString()); var queryable = dbContext.MagicDbSet(intractionType); var FirstModelId = oneWayRelation ? permissionHandler.getRequesterID() : request.IdentifierValue; var SecondModelId = relatedRequest.IdentifierValue; var relation = (queryable as IEnumerable <dynamic>) .Where(intraction => intraction.Valid && ((intraction.ValidUntil == null || intraction.ValidUntil.HasValue == false) || (intraction.ValidUntil.HasValue && DateTime.Now.CompareTo(intraction.ValidUntil.Value) < 0)) && (intraction.CreatorId.Equals(permissionHandler.getRequesterID()) && intraction.FirstModelId.Equals(FirstModelId) && intraction.SecondModelId.Equals(SecondModelId))) .Take(1) .FirstOrDefault(); // Create relation if (httpRequestMethod == HttpRequestMethod.Post) { if (relation != null) { return(new OkObjectResult(relation)); } relation = new ModelInteraction <TRelation> { CreatorId = permissionHandler.getRequesterID(), FirstModelId = FirstModelId, SecondModelId = SecondModelId, IntractionType = relationType }; MagicExtentions.MagicAddIntraction(queryable, relation, intractionType); EngineService.OnRelationCreated(dbContext, request, relatedRequest, relation); } else if (httpRequestMethod == HttpRequestMethod.Delete) { if (relation == null) { return(new OkResult()); } queryable.Remove(relation); relation.Valid = false; relation.ValidUntil = DateTime.Now; relation.Information = "Extinct by " + permissionHandler.getRequesterID(); dbContext.MagicAddIntraction(relation as object, EngineService.MapRelationToType("Invalid")); EngineService.OnRelationDeleted(dbContext, request, relatedRequest, relation); } else { return(BadRequest()); } ResolveRelationDependency( requestModel, permissionHandler.getRequesterID(), request, relatedRequest, relationType, httpRequestMethod); ResolveRelationDependency( relatedRequestModel, permissionHandler.getRequesterID(), request, relatedRequest, relationType, httpRequestMethod); dbContext.SaveChanges(); return(new OkObjectResult(relation)); }
public dynamic ValidateRequest( HttpRequestMethod requestMethod, IRequest request, ModelAction requestedAction, TRelation relationType) { if (request == null || request.ResourceName == null || string.IsNullOrWhiteSpace(request.ResourceName)) { return("Request Error: Route parameters should not be empty"); } // Check ResourceName //* Check whether resource is exist or not? is direct access allowed or not? var resourceType = modelParser.GetResourceType(request.ResourceName); if (resourceType == null) { return("Requested resource {" + request.ResourceName + "} is not exist or direct access is not permitted"); } request.Temp_ResourceType = resourceType; if (requestedAction != ModelAction.Create) { if (request.IdentifierName == null || string.IsNullOrWhiteSpace(request.IdentifierName) || request.IdentifierValue == null || string.IsNullOrWhiteSpace(request.IdentifierValue)) { return("Request Error: Route parameters should not be empty"); } //* Check whether this identifier is exist or not for model var identifierValidator = (resourceType.GetCustomAttributes(typeof(IdentifierValidatorAttribute), true) as IdentifierValidatorAttribute[]) .Union( resourceType.GetProperties() .Where(property => property.IsDefined(typeof(IdentifierValidatorAttribute))) .Select(validator => validator.GetCustomAttribute(typeof(IdentifierValidatorAttribute), true) as IdentifierValidatorAttribute) ) .Where(validator => validator.PropertyName == request.IdentifierName) .FirstOrDefault(); if (identifierValidator == null || identifierValidator.Validator == null) { return("Requested model identifier does not exist or it's not permitted to use it as an identifier"); } var identifierValidation = APIUtils.InvokeMethod( identifierValidator.Validator, "Validate", new object[] { request.IdentifierValue }); if (!(identifierValidation is bool && (bool)identifierValidation)) { return("Request Error: The value {" + request.IdentifierValue + "} is not a valid value for identifier {" + request.IdentifierName + "}"); } } return(ModelValidation( Request: request, ModelType: resourceType, ModelAction: requestedAction, RequestMethod: requestMethod, RelationType: relationType)); }
public dynamic ReadCardList( string resourceName, string requesterID, string cursor ) { Cursor objCursor = null; if (cursor != null && !string.IsNullOrWhiteSpace(cursor)) { try { var decryptedCursor = cursor.DecryptString( engineService.GetCursorEncryptionKey() ); objCursor = JsonConvert.DeserializeObject <Cursor> (decryptedCursor); } catch (Exception) { } } var maxPage = engineService.GetMaxRangeReadPage(resourceName); var maxOPP = engineService.GetMaxRangeReadObjectPerPage(resourceName); if (objCursor == null) { objCursor = new Cursor(requesterID, resourceName); } else { if (objCursor.isExpired()) { return(new BadRequestObjectResult(new APIError { Message = "Cursor time limit has been expired." })); } if (objCursor.RequesterID != requesterID || objCursor.IssuedFor != resourceName) { return(new BadRequestObjectResult(new APIError { Message = "Cursor has been issued for someone else." })); } if (objCursor.PageNumber < 1 || objCursor.PageNumber > maxPage || objCursor.ObjPerPage < 1 || objCursor.ObjPerPage > maxOPP) { return(new BadRequestObjectResult(new APIError { Message = "Cursor page number or object/page is out of bound." })); } } var resourceType = modelParser.IsRangeReaderAllowed(resourceName); if (resourceType == null) { return(new NotFoundResult()); } var endPoint = objCursor.PageNumber * objCursor.ObjPerPage; var startpoint = endPoint - objCursor.ObjPerPage; var rangeAttribute = resourceType.GetCustomAttribute <RangeReaderAllowedAttribute> (); if (endPoint > rangeAttribute.MaxObjToRead) { return(new BadRequestObjectResult(new APIError { Message = "Requested range is exceeded from resource limitations (" + rangeAttribute.MaxObjToRead + ")" })); } var nextCursor = string.Empty; if (endPoint + objCursor.ObjPerPage <= rangeAttribute.MaxObjToRead && objCursor.PageNumber <= maxPage && objCursor.ObjPerPage <= maxOPP) { try { nextCursor = JsonConvert.SerializeObject( new Cursor( requesterID, resourceName, objCursor.PageNumber + 1, objCursor.ObjPerPage ) ) .EncryptString(engineService.GetCursorEncryptionKey()); } catch (Exception) { return(new BadRequestObjectResult(new APIError { Message = "Cursor creation has been failed" })); } } APIUtils utils = new APIUtils(); var result = utils.GetResourceWithRange( APIUtils.GetIQueryable(dbContext, resourceType.Name), startpoint, endPoint) as ICollection <Card>; if (result == null || result.Count == 0) { return(new CardList()); } var rangerIdProp = resourceType.GetProperties() .Where(prop => prop.IsDefined(typeof(KeyAttribute), false)) .FirstOrDefault(); var permissionHandler = new PermissionHandler <TRelation, TUser> ( requesterID, modelParser, engineService, dbContext); var iRequest = new IRequest { ResourceName = resourceType.Name, IdentifierName = rangerIdProp.Name }; foreach (var item in result) { iRequest.IdentifierValue = rangerIdProp.GetValue(item).ToString(); permissionHandler.CheckPermissionRequirements( iRequest, ModelAction.Read, HttpRequestMethod.Get, item); } return(new CardList { PageNumber = objCursor.PageNumber, Cursor = nextCursor, Cards = result.ToList(), }); }