How to programatically ad securitySchema to swagger.yaml - java

I have a swagger.yaml file containing definition of my API. I'm using the following piece of code to add it as a swagger resource and display it in swagger-ui:
#Primary
#Bean
public SwaggerResourcesProvider swaggerResourcesProvider(InMemorySwaggerResourcesProvider defaultResourcesProvider) {
return () -> {
SwaggerResource wsResource = new SwaggerResource();
wsResource.setName("Documentation");
wsResource.setSwaggerVersion("3.0");
wsResource.setLocation("/swagger.yaml");
List<SwaggerResource> resources = new ArrayList<>(defaultResourcesProvider.get());
resources.add(wsResource);
return resources;
};
}
However, I need to have a different security schema authorization URL for each instance of my application (dev, test, prod). The question is how to achieve it? Is it possible to programmatically add the security schema, which will be parametrized based on f.e. env variables?

You can do that using the following:
#Bean
public OpenAPI openAPI() {
return new OpenAPI()
.components(new Components().addSecuritySchemes("jwtScheme",
new SecurityScheme().type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name("Authorization")));
}
You can get whatever environment variables you want to parameterize this configuration. You can read more about this at https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api#10-swagger-ui-with-an-oauth-secured-api.

Related

Create a map dynamically in the mapStore

I'm using MapStore to persist the data into Database.
My application is a multi-tenant application so if new tenant comes in how to create a map at run time in mapstore..
if you see in the below code, currently I have hardcoded for tenant-1.
this is my configuration class
#Component
public class CacheConfig {
#Bean
public static Config config() {
System.err.println("config class");
Config config = new Config();
config.setInstanceName("hazelcast");
MapConfig mapCfg = new MapConfig();
mapCfg.setName("tenant-1");
mapCfg.setBackupCount(2);
mapCfg.setTimeToLiveSeconds(300);
MapStoreConfig mapStoreCfg = new MapStoreConfig();
mapStoreCfg.setClassName(PersonMapStore.class.getName()).setEnabled(true);
mapCfg.setMapStoreConfig(mapStoreCfg);
config.addMapConfig(mapCfg);
return config;
}
}
Any suggestions would be very helpful..
Thanks in advance....
you can use wildcards in the name. For example, you can set the name of the configuration to tenant-* to cover all tenants with the names like tenant-1, tenant-2 ...

GraphqlFieldVisibility in graphql-java 12.0

I have a project running with Spring Boot and have implemented a GraphQL API with com.graphql-java.graphql-java:12.0.
I want to set the field visibility for some Mutators and maybe some fields now, but unfortunately I find no tutorial, doc or example where I find a working explanation how to to this.
For explanation, I have following example entries in schema:
type Query {
login(username: String!, password: String!): String
organization(id: Int!): ApiOrganization
}
type Mutation {
updateProfile(profileData: ProfileInputDto): ID
updateAdminStuff(adminData: AdminStuffDto): ID
}
The Query entries shall now be visible in the schema for all users who use the api and also the Mutation to updateProfile shall be visible.
But the Mutation updateAdminStuff shall only be visible if the user is logged in as Admin Role, so that a normal user doesn't even know that this mutation exists. Additionally it can happen that only some fields of some schema types shall be made visible for some roles only.
I found out that there is a chance to set something like that by GraphqlFieldVisibility (https://www.graphql-java.com/documentation/v12/fieldvisibility/). First versions I found said to set it in GraphQLSchema, but there it seems deprecated and I should use GraphQLCodeRegistry to set the visibility. For GraphQLCodeRegistry I found out on https://www.graphql-java.com/documentation/v12/execution/
GraphQLCodeRegistry codeRegistry = newCodeRegistry()
.dataFetcher(
coordinates("CreateReviewForEpisodeMutation", "createReview"),
mutationDataFetcher()
)
.build();
GraphQLSchema schema = GraphQLSchema.newSchema()
.query(queryType)
.mutation(createReviewForEpisodeMutation)
.codeRegistry(codeRegistry)
.build();
But unfortunately I find no way to set this method for the schema generation I use.
Can someone please give me a hint (example, tutorial, documentation) where I can find a tipp for soulution? (if something is possible at all in GraphQL)
Here some additional infos about the project:
I have a schmea definition saved as schema.graphqls. I have a GraphQLProvider and it prepares the Scehma and a GraphQL the following way:
private GraphQL graphQL;
#Bean
public GraphQL graphQL() {
return graphQL;
}
#PostConstruct
public void init() throws IOException {
URL url = Resources.getResource("graphql/schema.graphqls");
String sdl = Resources.toString(url, Charsets.UTF_8);
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(String sdl) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
RuntimeWiring runtimeWiring = buildWiring();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
In my controller I fetch the data by
ExecutionInput executionInput = ExecutionInput.newExecutionInput().context(request).query(body.getQuery())
.build();
ExecutionResult executionResult = graphQL.execute(executionInput);
where body is a GraphQLQuery and graphQL is the bean of the code before.
Thanks for your help and best regards.
Ok got the answer in the GraphQL-Java chat.
I used this tutorial https://www.graphql-java.com/tutorials/getting-started-with-spring-boot/ to build the graphQl API and using this I build the runtime wiring by myself and there I can set the visibility.
I now implemented it this way:
GraphqlFieldVisibility blockedFields = BlockedFields.newBlock()
.addPattern("ApiField.secretfield")
.addPattern(".*\\.secretAdminMutation")
.build();
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring().fieldVisibility(blockedFields)....
and it works great!

Spring - How to get subset of properties file without enumerating all required properties

I am loading my properties file as following:
#Configuration
#PropertySource("classpath:app.properties")
class MyApp {
#Bean
public PropertySourcesPlaceholderConfigurer PropertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In the properties file, I have several database-related properties:
database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb
database.dataSource.x=...
database.dataSource.y=...
database.dataSource.z=...
Note:
${db-port} should be replaced by either the value of property/environment variable db-port or 5432. In my case, I am defining the environment variable db-port when spawning the Tomcat container.
All database-related properties are grouped under database. root. This is intentional, see below.
I want to avoid that I have to enumerate/hardcode all possible database-related properties in my code. Luckily, the database layer in use (Hikari) has the nice feature that I can pass all properties via a java.util.Properties. So, I want retrieve all defined properties under database.* and simply forward it to Hikari.
For this, I wrote the following utility:
#Component
public class PropertyFetcher
{
#Autowired
private ConfigurableEnvironment environment;
public Properties get(final String key) {
final Properties p = new Properties();
for (final PropertySource<?> s : environment.getPropertySources()) {
if (s instanceof EnumerablePropertySource) {
for (final String k : ((EnumerablePropertySource) s).getPropertyNames()) {
if (k.startsWith(key) && k.length() > key.length()) {
p.put(k.substring(key.length()), s.getProperty(k));
}
}
}
}
return p;
}
}
Now, when calling get("database."), I have all database-related properties as defined in the properties file. Great! But, the value for property dataSource.url is now
jdbc:postgresql://localhost:${db-port:5432}/mydb
instead of
jdbc:postgresql://localhost:9876/mydb
So, for some reason, the ${db-port:5432} is not resolved (yet?) when going via this route (ConfigurableEnvironment).
How can this be fixed? Or is there a better way to get all properties under a certain root without having to enumerate/hardcode them into the code?
Please note that in the default scenario, the ${db-port:5432} in property database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb is correctly resolved. I tested this by defining the following member and logging it:
#Value("${database.dataSource.url}")
final String url; // holds jdbc:postgresql://localhost:9876/mydb
You should read the property values from real environment only. Then only you will get actual or effective value of a property.
This will require a little change in your code.
change this line:
p.put(k.substring(key.length()), s.getProperty(k));
to this:
p.put(k.substring(key.length()), environment.getProperty(k));

How to configure a default #RestController URI prefix for all controllers?

I know you can set the server.contextPath in application.properties to change the root context.
Also, I can add an additional context in the application config for Spring Boot like the following example (in Groovy) to add an "/api" to the URL mappings of the root context:
#Bean
ServletRegistrationBean dispatcherServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
reg.name = "dispatcherServlet"
reg.addInitParameter("contextConfigLocation", "")
reg.addUrlMappings("/api/*")
reg.loadOnStartup = 2
reg
}
}
I am trying to have a separate base URI "/api" specifically for web service calls, that I can leverage for security, etc. However using the above approach will mean that any of my URIs, web service or not, can be reached with "/" or "/api", and provides no concrete segregation.
Is anyone aware of a better approach to set a base path for all #RestController(s) using configuration, without having to formally prefix every controller with /api/? If I am forced to manually prefix the URI for each controller, it would be possible to mistakenly omit that and bypass my security measures specific to web services.
Here is a reference in Stack Overflow to the same type of question, which was never completely answered:
Spring Boot: Configure a url prefix for RestControllers
In continuation to the currently accepted solution the github issue addresses the same.
Spring 5.1 and above you can implement WebMvcConfigurer and override configurePathMatch method like below
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Now all the #RestControllers will have /api as the prefix path alongside the path configured.
Official Documentation
There's a new solution to solve this kind of problem available since Spring Boot 1.4.0.RC1 (Details see https://github.com/spring-projects/spring-boot/issues/5004)
The solution of Shahin ASkari disables parts of the Auto configuration, so might cause other problems.
The following solution takes his idea and integrates it properly into spring boot. For my case I wanted all RestControllers with the base path api, but still serve static content with the root path (f.e. angular webapp)
Edit: I summed it up in a blog post with a slightly improved version see https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
RestController restApiController = beanType.getAnnotation(RestController.class);
if (restApiController != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
Also You can achieve the same result by configuring WebMVC like this:
#Configuration
public class PluginConfig implements WebMvcConfigurer {
public static final String PREFIX = "/myprefix";
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(MyCustomAnnotation.class));
}
}
Implement WebMvcConfigurer on any #Configuration class.
Override configurePathMatch method.
You can do many useful things with PathMatchConfigurer e.g. add prefix for several classes, that satisfy predicate conditions.
I had the same concern and was not a fan of the Spring EL option due to the issues documented and I wanted the prefix to be tightly controlled in the controllers but I did not want to depend on the developers doing the right thing.
There might be a better way these days but this is what I did. Can you guys see any downsides, I am still in the process of testing any side-effects.
Define a custom annotation.
This allows a developer to explicitly provide typed attributes such as int apiVersion(), String resourceName(). These values would be the basis of the prefix later.
Annotated rest controllers with this new annotation
Implemented a custom RequestMappingHandlerMapping
In the RequestMappingHandlerMapping, I could read the attribute of the custom annotation and modify the final RequestMappingInfo as I needed. Here are a few code snippets:
#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyCustomRequestMappingHandlerMapping();
}
}
And in the MyCustomRequestMappingHandlerMapping, overwrite the registerHandlerMethod:
private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class);
public MyCustomRequestMappingHandlerMapping() {
super();
}
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// find the class declaring this method
Class<?> beanType = method.getDeclaringClass();
// check for the My rest controller annotation
MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class);
if (myRestAnnotation != null) {
// this is a My annotated rest service, lets modify the URL mapping
PatternsRequestCondition oldPattern = mapping.getPatternsCondition();
// create a pattern such as /api/v${apiVersion}/${resourceName}
String urlPattern = String.format("/api/v%d/%s",
myRestAnnotation.apiVersion(),
myRestAnnotation.resourceName());
// create a new condition
PatternsRequestCondition apiPattern =
new PatternsRequestCondition(urlPattern);
// ask our condition to be the core, but import all settinsg from the old
// pattern
PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);
myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}",
beanType, myRestAnnotation, oldPattern, updatedFinalPattern);
mapping = new RequestMappingInfo(
mapping.getName(),
updatedFinalPattern,
mapping.getMethodsCondition(),
mapping.getParamsCondition(),
mapping.getHeadersCondition(),
mapping.getConsumesCondition(),
mapping.getProducesCondition(),
mapping.getCustomCondition()
);
}
super.registerHandlerMethod(handler, method, mapping);
}
}
Slightly less verbose solution which doesn't duplicate the logic of checking the annotation, but only changes the mapping path:
private static final String API_PREFIX = "api";
#Bean
WebMvcRegistrationsAdapter restPrefixAppender() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
if (mappingForMethod != null) {
return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod);
} else {
return null;
}
}
};
}
};
}
Side effects
Your error controller will also be mapped under /api/error, which breaks error handling (DispatcherServlet will still redirect errors to /error without prefix!).
Possible solution is to skip /error path when adding /api prefix in the code above (one more "if").
Someone has filed an issue in the Spring MVC Jira and come up with a nice solution, which I am now using. The idea is to use the Spring Expression Language in the prefix placed in each RestController file and to refer to a single property in the Spring Boot application.properties file.
Here is the link of the issue: https://jira.spring.io/browse/SPR-13882

Exists any <jaxws:endpoint /> annotation?

I'm using CXF with Spring to publish and to consume my WebServices in JBoss 5.1. All works fine.
However, there's a thing that's I think very tedious: to put a jaxws:endpoint tag for every WebService in applicationContext.xml.
There's realy no way to do that with annotations? Thanks for all.
As time pass, there arise some new possibilities.
Working with CXF/SpringBoot (SpringBoot: 1.2.3, CXF: 3.10, Spring: 4.1.6) there is a nice alternative in order to get rid of the jaxws:endpoint configuration in cxf-servlet.xml, as jonashackt pointed out in nabble.com. However, this solution is only possible if there is only one endpoint in the application (at least I did not succeed to configure more than one).
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServletRegistrationBean dispatcherServlet() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean(cxfServlet, "/api/*");
}
#Bean(name="cxf")
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public MyServicePortType myService() {
return new MyServiceImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), myService());
endpoint.publish("/MyService");
return endpoint;
}
Where MyServicePortType is a class having the #WebService annotation. This Endpoint is then called for URL's like "localhost:8080/api/MyService"
Of course these #Bean declarations may be placed in any other spring config class.
In contrary to the copied original solution I suggest to instantiate the Bus (cxf-Bean) by using the factory method instead of the direct "new SpringBus()":
BusFactory.newInstance().createBus()
There are some annotations to configure things that you can also put in <jaxws:endpoint>. An annotation to declare a CXF endpoint would be nice.
You can configure an endpoint using code instead of Spring XML. This can be handy if you have a lot of repetitive configuration that you can factor out. Or if you have certain endpoints configured differently in different environments.
For example:
#Autowired var authImpl: Auth = _
#Autowired var faultListener: FaultListener = _
def initWebServices() {
var sf: JaxWsServerFactoryBean = _
val propMap = mutable.HashMap[String, AnyRef]("org.apache.cxf.logging.FaultListener"->faultListener.asInstanceOf[AnyRef])
sf = new JaxWsServerFactoryBean
sf.setServiceBean(authImpl)
sf.setAddress("/auth")
sf.setServiceName(new QName("http://auth.ws.foo.com/", "auth", "AuthService"))
sf.setProperties(propMap)
sf.create
// more services...

Categories