I'm trying to add the field externalDocs to the generated Json from Springfox:
"externalDocs": {
"description": "find more info here",
"url": "https://swagger.io/about"
},
Reading the SpringFox documentation, I understand that I need to create a plugin to extends the SpringFox funcionalities and add this field. I tried:
#Component
#Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1002)
#Slf4j
public class ExternalDocSwaggerConfiguration implements ApiListingBuilderPlugin {
#Override
public void apply(final ApiListingContext apiListingContext) {
ObjectVendorExtension ext = new ObjectVendorExtension("externalDocs");
ext.addProperty(new StringVendorExtension("description", "Link externo"));
ext.addProperty(new StringVendorExtension("url", "https://swagger.io/about"));
apiListingContext.apiListingBuilder().extensions(
Collections.singletonList(ext)); // extensions does not exist
}
#Override
public boolean supports(final DocumentationType documentationType) {
return true;
}
}
I was expecting to add the extension, as showed here with the OperationBuilderPlugin, but there is no extensions method on the apiListingBuilder.
So, how could I add this tag on the root of the generated Swagger Json using SpringFox?
The version 2.7.0 added this feature.
To add this field externalDocs, you can use the extensions method from the Docket:
#Bean
public Docket customImplementation() {
ObjectVendorExtension ext = new ObjectVendorExtension("externalDocs");
ext.addProperty(new StringVendorExtension("description", "Link externo"));
ext.addProperty(new StringVendorExtension("url", "https://swagger.io/about"));
return new Docket(DocumentationType.SWAGGER_2)
.extensions(Collections.singletonList(ext))
.apiInfo(apiInfo())
.securitySchemes(newArrayList(apiKey()))
.pathMapping("/api")
.securityContexts(newArrayList(securityContext())).select()
.apis(getPackages())
.paths(PathSelectors.any())
.build();
}
Related
Trying to build Rest-assured request body with POJO and lombok builder
I have created the POJO objects, not sure how to build the request body for below code
{
"con": {
"ipAdr": "1.11.222",
"site": "ghyt"
},
"cred": {
"login": {
"user": "abc",
"pd": "xyz"
}
},
"view": "qwe"
}
First you need to add preferred JSON serializer to classpath or as a dependency in your Maven/Gradle project. As of RestAssured 4.0.0 you can try:
FasterXML's Jackson (https://github.com/FasterXML/jackson)
Google's GSON (https://github.com/google/gson)
Apache's Johnzon (https://github.com/apache/johnzon)
Here's an example of POJO with lombok's #Builder annotation:
#Data
#Builder
public class MyPojo {
private String view;
}
And RestAssured post request:
MyPojo pojo = MyPojo.builder()
.view("some-value")
.build();
RestAssured.given()
.contentType(ContentType.JSON)
.body(pojo)
.post("http://www.example.com");
There's a lot of information on this topic on RestAssured documentation page - https://github.com/rest-assured/rest-assured/wiki/Usage#object-mapping
The equivalent for your json is:
public class Con {
public String ipAdr;
public String site;
}
-----------------------------------com.example.Cred.java-----------------------------------
package com.example;
#Data
public class Cred {
public Login login;
}
-----------------------------------com.example.Example.java-----------------------------------
package com.example;
#Data
public class Example {
public Con con;
public Cred cred;
public String view;
}
-----------------------------------com.example.Login.java-----------------------------------
package com.example;
#Data
public class Login {
public String user;
public String pd;
enter code here
} ```
I'm using swagger.json file (generated by Swashbuckle) for ReDoc to display API documentation.
What I Need:
Add x-logo vendor extension to swagger json generated using Swashbuckle (Swashbuckle.AspNetCore.SwaggerGen library) so that ReDoc UI shows logo at the top left corner like this
Problem:
I was able to add x-log to the swagger.json file but it is added to wrong section of the file. It needs to be inside info section.
This is what I have done to add the x-logo
Created a document filter like below
public class XLogoDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Extensions["x-logo"] = new { url = "https://URL/of/the/logo", altText = "Company Logo" };
}
}
Added the filter to SwaggerDoc as
services.AddSwaggerGen(options =>
{
options.DocumentFilter<XLogoDocumentFilter>();
});
Actual
{
"swagger": "2.0",
"info": {
"version": "v1",
"title":"Sample REST API"
},
"x-logo": {
"url": "https://rebilly.github.io/ReDoc/petstore-logo.png",
"altText": "Aimia Logo"
}
}
Expected
{
"swagger": "2.0",
"info": {
"version": "v1",
"title":"Sample REST API",
"x-logo": {
"url": "https://rebilly.github.io/ReDoc/petstore-logo.png",
"altText": "Aimia Logo"
}
},
}
Really appreciate any help or suggestions to have the x-logo in the correct section of the swagger.json file.
After typing the question I found the solution myself. Instead of adding extension directly to swaggerDoc, add it to swaggerDoc.Info object.
public class XLogoDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
// need to check if extension already exists, otherwise swagger
// tries to re-add it and results in error
if (!swaggerDoc.Info.Extensions.ContainsKey("x-logo"))
{
swaggerDoc.Info.Extensions.Add("x-logo", new {
url = "https://URL/To/The/Logo",
altText = "Logo",
});
}
}
}
The newer versions of Swashbuckle support this in the SwaggerDoc setup:
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = ApiDescription,
Version = "v1",
Extensions = new Dictionary<string, IOpenApiExtension>
{
{"x-logo", new OpenApiObject
{
{"url", new OpenApiString("https://blah.com/logo")},
{ "altText", new OpenApiString("The Logo")}
}
}
}
});
for .NET core 2.2 and higher
public class XLogoDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
// need to check if extension already exists, otherwise swagger
// tries to re-add it and results in error
if (!swaggerDoc.Info.Extensions.ContainsKey("x-logo"))
swaggerDoc.Info.Extensions.Add("x-logo", new OpenApiObject
{
{"url", new OpenApiString("https://www.petstore.com/assets/images/logo.png")},
{"backgroundColor", new OpenApiString("#FFFFFF")},
{"altText", new OpenApiString("PetStore Logo")}
});
}
}
I did configure swagger with an Application subclass and the beanConfig object, my securityDefinition must allow swagger ui to show de api_key field to allow authentication for all my services layer.
BeanConfig beanConfig = new BeanConfig();
beanConfig.setSchemes(new String[] { "http" });
beanConfig.setHost("192.168.4.9:8080");
beanConfig.setBasePath("/cjppa/rest");
beanConfig.setResourcePackage("com.cjppa.fpuna.backend.resources");
beanConfig.setScan(true);
beanConfig.setPrettyPrint(true);
io.swagger.models.Info info = new io.swagger.models.Info();
io.swagger.models.Contact contact = new io.swagger.models.Contact();
info.setVersion("1.0");
beanConfig.setInfo(info);
io.swagger.models.auth.ApiKeyAuthDefinition apikey = new
io.swagger.models.auth.ApiKeyAuthDefinition();
apikey.setName("x-token");
apikey.setIn(In.HEADER);
Swagger swagger = new Swagger().info(info);
swagger.securityDefinition("api_key", apikey);
beanConfig.configure(swagger);
the expected api_key comes in the "x-token" http header
I tried also to bring swagger into my resteasy webservice with using BasicAuthentification for some operations of my webservice. I imported swagger via maven in my pom.xml:
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.18</version>
</dependency>
In my Application class I configured the BeanConfig:
import javax.ws.rs.ApplicationPath;
import io.swagger.jaxrs.config.BeanConfig;
#ApplicationPath("/rest")
public class Application extends javax.ws.rs.core.Application{
public Application() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0");
beanConfig.setResourcePackage("de.mycompany.topic.ws");
beanConfig.setBasePath("/de.mycompany.topic.ws/rest/");
beanConfig.setScan(true);
}
}
The important thing is to configure the BasicAuthentification in an ReaderListener implementation via Annotations. basicAuth is an arbitrary name.
import io.swagger.annotations.BasicAuthDefinition;
import io.swagger.annotations.SecurityDefinition;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.jaxrs.Reader;
import io.swagger.jaxrs.config.ReaderListener;
import io.swagger.models.Swagger;
#SwaggerDefinition(securityDefinition = #SecurityDefinition(basicAuthDefinitions = {
#BasicAuthDefinition(key = "basicAuth")
}) )
public class SwaggerCustomizeDefinition implements ReaderListener {
#Override
public void beforeScan(Reader reader, Swagger swagger) {
}
#Override
public void afterScan(Reader reader, Swagger swagger) {
}
}
In MyRestService I annotate my operations that should be not usable without basic authentification. See here e.g. for saving customers:
#Api
#Path("/")
public class MyRestService {
private final static String UTF8 = ";charset=UTF-8";
#POST
#Path("/customer")
#Produces(MediaType.APPLICATION_JSON + UTF8)
#ApiOperation(
value = "Saves customer specified in the body",
notes = "note that appears in swagger ui",
authorizations = {
#Authorization(value = "basicAuth", scopes={})
})
#ApiResponses(value = {
#ApiResponse(code = 201, message = "customer created"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 404, message = "customer format not supported"),
})
public Response saveCustomer(
String content,
#BasicAuthDefinition(key = "basicAuth") #HeaderParam("Authorization") String authorization) {
// authorization
try {
if (!MyManager.isAuthorized(authorization)) {
return Response.status(Status.UNAUTHORIZED).build();
}
} catch (Exception e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
//do the work, authorization was ok
}
}
That's it. I tried a lot of variations and this was the only one that works for me in whole. My main problem was, that the authorize button not appears in the ui and the lock above the single methods in the swagger ui was not clickable so that the basic authentification modal dialog not appears. With this implementation it works.
you can implements io.swagger.jaxrs.config.ReaderListener ,addSecurity in afterScan method . eg:
#SwaggerDefinition(securityDefinition = #SecurityDefinition(apiKeyAuthDefinitions = {
#ApiKeyAuthDefinition(in = ApiKeyAuthDefinition.ApiKeyLocation.HEADER, key = "token", name = "E-token"),
#ApiKeyAuthDefinition(in = ApiKeyAuthDefinition.ApiKeyLocation.HEADER, key = "userId", name = "E-userId"),
#ApiKeyAuthDefinition(in = ApiKeyAuthDefinition.ApiKeyLocation.HEADER, key = "corpId", name = "E-corpId") }) )
public class SwaggerCustomizeDefinition implements ReaderListener {
#Override
public void beforeScan(Reader reader, Swagger swagger) {
}
#Override
public void afterScan(Reader reader, Swagger swagger) {
swagger.addSecurity(new SecurityRequirement().requirement("token"));
swagger.addSecurity(new SecurityRequirement().requirement("userId"));
swagger.addSecurity(new SecurityRequirement().requirement("corpId"));
}
}
I was wondering if anyone knows an elegant way to get all the roles in the spring security plugin that have access to the current page.
I am using spring security and it's configured to use RequestMap domain objects.
The permissions in my app are pretty complex so I wanted to make a tag at the bottom of each page displaying the roles need to use the page.
I was doing a query for the request map but I want to make sure the way I match the url is the same as the way the plugin does.
Ideally I wouldn't have to run a query at all.
Grails version 2.2.1 Spring Security Plugin version 1.2.7.3
Thanks in advance
I got this to work by adding the following two classes to my src/java.
Class 1
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
FilterInvocationSecurityMetadataSource oldBean;
#Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) o;
HttpServletRequest request = filterInvocation.getHttpRequest();
request.setAttribute("PAGEROLES", oldBean.getAttributes(filterInvocation));
return oldBean.getAttributes(o);
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return oldBean.getAllConfigAttributes();
}
#Override
public boolean supports(Class<?> aClass) {
return FilterInvocation.class.isAssignableFrom(aClass);
}
public Object getOldBean() { return oldBean; }
public void setOldBean(FilterInvocationSecurityMetadataSource oldBean) { this.oldBean = oldBean; }
}
Class 2
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public class FilterSecurityMDSExtractor implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory bf;
private FilterInvocationSecurityMetadataSource metadataSource = new MyFilterInvocationSecurityMetadataSource();
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FilterInvocationSecurityMetadataSource) {
((MyFilterInvocationSecurityMetadataSource) metadataSource).setOldBean((FilterInvocationSecurityMetadataSource) bean);
return metadataSource;
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.bf = (ConfigurableListableBeanFactory)beanFactory;
}
}
I then added the following to resources.groovy
beans = {
filterSecurityMDSExtractor(FilterSecurityMDSExtractor)
}
Basically I am stuffing the user roles into the request
request.setAttribute("PAGEROLES", oldBean.getAttributes(filterInvocation));
then all I have to do is call the following
request.getAttribute("PAGEROLES");
to get the roles back out. I pieced together my solution by stealing from other great posts on Stackoverflow. Someone else might have a better solution but so far this is working for me.
I have a Grails Plugin called 'foo' that uses another Grails Plugin called 'common'.
grails.plugin.location.'common' = "../common"
The 'common' plugin contains domain classes, as well as resource files (.properties files, xml templates, ...). These files are all located in subfolders in common/grails-app/conf/.
There's one class that implements NamespaceContext in my 'common' plugin that uses these files in order to function properly.
public class MyNamespaceContext implements NamespaceContext {
private Map<String, String> namespaces;
public MyNamespaceContext() {
final String XML_NAMESPACES_FILE = "grails-app/conf/xml/xmlNamespaces.properties";
try {
Properties xmlNamespaces = new Properties();
xmlNamespaces.load(new FileReader(XML_NAMESPACES_FILE));
namespaces = new HashMap<String, String>((Map) xmlNamespaces);
} catch (FileNotFoundException e) {
throw new RuntimeException("XML namespaces file '" + XML_NAMESPACES_FILE + "' cannot be found");
} catch (IOException e) {
throw new RuntimeException("IOException");
}
}
...
}
This class is used in several classes, also located in 'common' that form my domain model, implemented as xml decorators.
public class UserXmlDecorator implements User {
private Document xmlDocument;
private XPath xPath;
private final String rawXml;
public UserXmlDecorator(String rawXml) {
this.rawXml = rawXml;
this.xmlDocument = XmlDocumentFactory.INSTANCE.buildXmlDocumentInUTF8(rawXml);
this.xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext());
}
public String getUserName() {
try {
XPathExpression userNameXPathExpr = xPath.compile("...");
String userName = userNameXPathExpr.evaluate(appendixBXmlDocument);
return userName;
} catch (XPathExpressionException e) {
throw new RuntimeException();
}
}
public String getAge() {
try {
XPathExpression ageXPathExpr = xPath.compile("...");
String age = ageXPathExpr.evaluate(appendixBXmlDocument);
return age;
} catch (XPathExpressionException e) {
throw new RuntimeException();
}
}
When creating these decorators in my Grails Plugin 'foo', I get a FileNotFound exception, because it is looking for the template in foo/grails-app/conf/xml/xmlNamespaces.properties, instead of common/grails-app/conf/xml/xmlNamespaces.properties.
I've read
Grails: How to reference a resource located inside an installed plugin? but this could not help me.
Any idea how I can solve this?
Solved this by putting the .properties file in the classpath instead of the conf/ directory and then using the classloader to lod the resource.
xmlNamespaces.load(this.getClass().getClassLoader().getResourceAsStream(XML_NAMESPACES_FILE));