/// <summary> /// Declare a new shadowed link rel. Shadowed means that the link will be named rel, but use the documentation /// from the shadowedRel as the endpoint docs for the new link. /// </summary> /// <param name="rel">The rel to advertise this link as.</param> /// <param name="shadowedRel">The rel on the controller to grab docs from.</param> /// <param name="controllerType">The type of the controller to grab docs from.</param> /// <param name="routeArgs">The route args.</param> public DeclareHalLinkAttribute(string rel, string shadowedRel, Type controllerType, String[] routeArgs = null) { this.Rel = rel; refInfo = new HalRelInfo(shadowedRel, controllerType, routeArgs); this.GroupName = HalcyonExtUtils.GetControllerName(refInfo.ControllerType); this.UriTemplate = refInfo.UrlTemplate; this.Method = refInfo.HttpMethod; LinkedToControllerRel = true; }
/// <summary> /// Declare a new link bound to a controller function. /// </summary> /// <param name="controllerType">The type of the controller.</param> /// <param name="funcName">The name of the function on the controller</param> /// <param name="routeArgs">The route args.</param> public DeclareHalLinkAttribute(Type controllerType, string funcName, String[] routeArgs = null) { refInfo = new HalRelInfo(controllerType, funcName, routeArgs); this.Rel = refInfo.HalRelAttr.Rel; this.GroupName = HalcyonExtUtils.GetControllerName(refInfo.ControllerType); this.UriTemplate = refInfo.UrlTemplate; this.Method = refInfo.HttpMethod; LinkedToControllerRel = true; }
public static bool CanUserAccess(ClaimsPrincipal principal, MethodInfo methodInfo, TypeInfo controllerTypeInfo) { //Check for anon access attribute var anonAttrs = methodInfo.GetCustomAttributes <AllowAnonymousAttribute>(true); if (anonAttrs.Any()) { return(true); } //Otherwise check the action method and controller for authorize attributes. var attributes = methodInfo.GetCustomAttributes <AuthorizeAttribute>(true); attributes = attributes.Concat(controllerTypeInfo.GetCustomAttributes <AuthorizeAttribute>(true)); return(HalcyonExtUtils.CheckRoles(principal, attributes)); }
private void Setup(string rel, Type controllerType, string[] routeArgs, TypeInfo controllerTypeInfo) { var routeAttr = controllerTypeInfo.GetCustomAttribute <RouteAttribute>(); if (routeAttr != null) { this.UrlTemplate += routeAttr.Template; } routeAttr = this.ActionMethodInfo.GetCustomAttribute <RouteAttribute>(); if (routeAttr != null) { EnsureTrailingUrlTemplateSlash(); this.UrlTemplate += routeAttr.Template; } var actionMethodName = this.ActionMethodInfo.Name; var methodAttribute = this.ActionMethodInfo.GetCustomAttribute <HttpMethodAttribute>(); if (methodAttribute != null) { EnsureTrailingUrlTemplateSlash(); this.UrlTemplate += methodAttribute.Template; this.HttpMethod = methodAttribute.HttpMethods.FirstOrDefault(); } if (this.UrlTemplate.Length == 0) { throw new InvalidOperationException($"Cannot build a route template for rel {rel} in controller {controllerType}. Did you forget to add a HttpMethod (HttpGet, HttpPost etc) attribute to the Action Method or a RouteAttribute to the controller class or Action Method."); } //Ensure leading slash if (this.UrlTemplate[0] != '\\' && this.UrlTemplate[0] != '/') { this.UrlTemplate = '/' + this.UrlTemplate; } //Remove the * from any route variables that include one this.UrlTemplate = this.UrlTemplate.Replace("{*", "{").Replace("[controller]", HalcyonExtUtils.GetControllerName(controllerType)).Replace("[action]", actionMethodName); if (routeArgs != null) { foreach (var arg in routeArgs) { int firstEquals = arg.IndexOf('='); if (firstEquals != -1) { var key = $"{{{arg.Substring(0, firstEquals)}}}"; var value = arg.Substring(firstEquals + 1).Trim(); this.UrlTemplate = this.UrlTemplate.Replace(key, value); } else { throw new InvalidOperationException($"The route argument {arg} for Action Method {actionMethodName} in controller {controllerType} is not valid. It must be in the format key=value."); } } } //Find data mode bool isQuery = false; bool isBody = false; bool isForm = false; foreach (var arg in this.ActionMethodInfo.GetParameters()) { isQuery = isQuery || arg.GetCustomAttribute <FromQueryAttribute>(true) != null; isBody = isBody || arg.GetCustomAttribute <FromBodyAttribute>(true) != null; isForm = isForm || arg.GetCustomAttribute <FromFormAttribute>(true) != null; } if (isQuery) { this.DataMode = DataModes.Query; } else if (isBody) { this.DataMode = DataModes.Body; } else if (isForm) { this.DataMode = DataModes.Form; } else { this.DataMode = DataModes.NoData; } }
public bool CanUserAccess(ClaimsPrincipal principal) { return(HalcyonExtUtils.CanUserAccess(principal, halRefInfo.ActionMethodInfo, halRefInfo.ControllerType.GetTypeInfo())); }
public async Task <EndpointDoc> GetDoc(string groupName, string method, string relativePath, EndpointDocBuilderOptions options) { if (relativePath == null) { relativePath = ""; } else if (relativePath.EndsWith("/") || relativePath.EndsWith("\\")) { relativePath = relativePath.Substring(0, relativePath.Length - 1); } var group = descriptionProvider.ApiDescriptionGroups.Items.FirstOrDefault(i => i.GroupName == groupName); if (group == null) { throw new InvalidOperationException($"Cannot find an api group for {groupName}. Did you declare a route to that group?"); } var action = group.Items.FirstOrDefault(i => i.HttpMethod == method && i.RelativePath == relativePath); if (action == null) { throw new InvalidOperationException($"Cannot find an api action for {relativePath} and method {method} in api group {groupName}."); } var description = new EndpointDoc(); bool handleFormData = true; if (options.IncludeResponse) { var controllerActionDesc = action.ActionDescriptor as ControllerActionDescriptor; if (controllerActionDesc != null) { var methodInfo = controllerActionDesc.MethodInfo; //Check to see if the user can actually access the endpoint we requested if (options.User != null && !HalcyonExtUtils.CanUserAccess(options.User, methodInfo, controllerActionDesc.ControllerTypeInfo)) { throw new UnauthorizedAccessException("User cannot access requested endpoint"); } var returnType = methodInfo.ReturnType; if (returnType != typeof(void)) { description.SetResponseSchema(await endpointDocCache.GetCachedResponse(groupName, method, relativePath, returnType, schemaBuilder.GetSchema)); } } } if (options.IncludeRequest) { foreach (var param in action.ParameterDescriptions) { if (param.Source.IsFromRequest) { if (param.Source.CanAcceptDataFrom(BindingSource.Body)) { description.SetRequestSchema(await endpointDocCache.GetCachedRequest(groupName, method, relativePath, param.Type, schemaBuilder.GetSchema)); } else if (param.Source.CanAcceptDataFrom(BindingSource.Query)) { description.SetRequestSchema(await endpointDocCache.GetCachedRequest(groupName, method, relativePath, param.ModelMetadata.ContainerType, schemaBuilder.GetSchema)); } else if (handleFormData && param.Source.CanAcceptDataFrom(BindingSource.Form)) { handleFormData = false; //This prevents this from running for everything that responsds to form, there should only be 1 per controller method. //Discover the type from the action method, there is no way to get the real object type from the description when dealing with form input Type type = null; var controllerActionDescriptor = action.ActionDescriptor as ControllerActionDescriptor; if (controllerActionDescriptor != null) { foreach (var arg in controllerActionDescriptor.MethodInfo.GetParameters()) { if (arg.CustomAttributes.Any(i => i.AttributeType == typeof(FromFormAttribute))) { type = arg.ParameterType; break; } } } if (type != null && validSchemaManager.IsValid(type)) { description.SetRequestSchema(await endpointDocCache.GetCachedRequest(groupName, method, relativePath, type, async t => { var schema = await schemaBuilder.GetSchema(t); schema.SetDataIsForm(true); return(schema); })); } } } } } return(description); }