Spring Boot Config Server - Always returning old values - configserver

Config client is unable to fetch the changed property values from the modified property files in Git. I need assistance in resolving this issue.
I created a new spring config server and client. Initially the values from the property files were fetched properly. When i changed the values in the property file, the client was still returning old values. I tried POSTing to http://localhost:8080/actuator/refresh and even after that, the old values are returned. Finally i deleted the property file from the git repository and the client still returns old values.
Config Server bootstrap.properties
spring.application.name=ConfigServer
server.port=8888
encrypt.key=123456
spring.security.user.password=configpassword123
spring.cloud.config.server.git.uri=https://some-repository/ConfigRepo.git
spring.cloud.config.server.git.username=git_user
spring.cloud.config.server.git.password=git_password
ConfigServer.java
#Configuration
#EnableDiscoveryClient
#SpringBootApplication
#EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
WebSecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
}
Config Client bootstrap.properties
spring.application.name=config-client
spring.cloud.config.uri=http://config-server:8888
spring.cloud.config.username=user
spring.cloud.config.password=configpassword123
management.endpoints.web.exposure.include=*
Config Client Controller Class
#RefreshScope
#RestController
public class ConfigController {
#Value("${applicationname}")
private String appName;
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Value("${instancename}")
private String environment;
#Value("${dbconnection}")
private String dbConnection;
#GetMapping("/user")
public String getUser() {
return "Application: "+ appName +" Instance: "+ environment + " User: " + username + " / " + password;
}
#GetMapping("/dbconn")
public String getDBConnection() {
return dbConnection;
}
}

Please check whether your post refresh request works fine. You need to permit the POST request in security configuration.
Reference code :
http.antMatchers(HttpMethod.POST, "/refresh").permitAll();

Related

Using #Value in PersistanceConfig

I am trying to database config dynamicly in dependence to environment.
I would like to use #Value to get database info.
My Config file is:
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = "service")
#PropertySource("classpath:application-${environment}.properties")
#EnableJpaRepositories("repositories")
public class PersistanceConfig {
private final String MODEL_PATH = "model";
#Value("${mariadb.driver}:#{null}")
private String driver;
#Value("${mariadb.url}:#{null}")
private String url;
#Value("${mariadb.username}:#{null}")
private String username;
#Value("${mariadb.password}:#{null}")
private String password;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] {MODEL_PATH});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
Assert.notNull(driver, "JBDC driver is not defined");
Assert.notNull(url, "url is not defined");
Assert.notNull(username, "username is not defined");
Assert.notNull(password, "password is not defined");
final BasicDataSource source = new BasicDataSource();
source.setDriverClassName(driver);
source.setUrl(url);
source.setUsername(username);
source.setPassword(password);
return source;
}
#Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
properties.setProperty(
"hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
During compile time is property-file properly found and variables driver, url, username and password are properly filled.
Unfortunatelly, during runtime #Value doesn't execute and fill the expression into variable (subsequent exceptions is: "Could not use '${mariadb.driver}' to connect").
According another answer here I added static PropertySourcesPlaceholderConfigurer bean, but i had no effect.
I also tried different combination of Environmnet class and PropertySource values but nothing worked.
Thank you for your advise.
Problem was pretty stupid.
I had my configurations file in root of project, not root of src java folder.

Oauth2 server example with all 4 grant types

Is it possible to have an auth server defining all four types of grant types?
And if yes then how can I test them? A sample auth client for grant type password would be helpful.
I am presuming that you want to implement it using Spring OAuth server implementation. One example by Dave Syer is hosted on GitHub, and it uses JDBC as the token store.
Now for the grant types, you have the option to configure that per client that you register with your OAuth server. You will notice that it has been setup using "authorizedGrantTypes" in the example below.
For testing - password grant type is very easy to test using a REST client like Postman and making a call to the OAuth server.
Feel free to comment on this if you need more help. All the best!
Resource Server
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.tokenStore(tokenStore);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
Authorization Server
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager auth;
#Autowired
private DataSource dataSource;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(auth).tokenStore(tokenStore())
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.jdbc(dataSource)
.passwordEncoder(passwordEncoder)
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code",
"refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(60).and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code")
.authorities("ROLE_CLIENT").scopes("read", "trust")
.resourceIds("oauth2-resource")
.redirectUris("http://anywhere?key=value").and()
.withClient("my-client-with-secret")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_CLIENT").scopes("read")
.resourceIds("oauth2-resource").secret("secret");
// #formatter:on
}
}

Reading application.properties from any class in spring boot

When I add a property in the application.properties files, this can be access from the main class without any problem.
#SpringBootApplication
#ComponentScan(basePackages = "com.example.*")
public class MailTestApplication implements CommandLineRunner {
#Value("${admin.mail}")
String email;
public static void main(String[] args) {
SpringApplication.run(MailTestApplication.class, args);
}
#Override
public void run(String... strings) throws Exception {
System.out.println(email);
Email email = new Email();
email.sendMail();
}
}
However, when I try to access it from any other class it is never retrieved.
#Component
public class Email {
#Autowired
private MailSender sender;
#Value("${admin.mail}")
String email;
public Email() {
}
public void sendMail() {
SimpleMailMessage msg = new SimpleMailMessage();
System.out.println(email);
msg.setTo("sample#email.com");
msg.setSubject("Send mail by Spring Boot");
msg.setText("Send mail by Spring Boot");
sender.send(msg);
}
}
I was reading some of the previous questions other users posted without a clear result for me. I even tried to find some examples with similar resutl.
Could someone give me any clue about this?
Thanks a lot in advance.
The #Value should work (Im asuming your class is under the com.example.* package since you are scanning that package) but if you want to do it another way this is what im using :
public class JpaConfiguration {
public static final String TRANSACTION_MANAGER_NAME = "jpaTransactionManager";
#Autowired
Environment applicationProperties;
Then to use it
#Bean
public DriverManagerDataSource driverManagerDataSource() {
DriverManagerDataSource driverConfig = new DriverManagerDataSource();
driverConfig.setDriverClassName(applicationProperties.getProperty("data.jpa.driverClass"));
driverConfig.setUrl(applicationProperties
.getProperty("data.jpa.connection.url"));
driverConfig.setUsername(applicationProperties
.getProperty("data.jpa.username"));
driverConfig.setPassword(applicationProperties
.getProperty("data.jpa.password"));
return driverConfig;
}
UPDATE AFTER GETTING THE GITHUB REPO
I Don't really know what you are trying to build but :
If you do this:
#Override
public void run(String... strings) throws Exception {
//System.out.println(email);
Email email = new Email();
email.sendMail();
}
Then you are creating the instance of the class, and not spring. so you shouldn't be creating the instance yourself there it should be spring.
That said, i dont know if you are creating a web application a command line application or both.
That said ill give you a minor solution to show you that the dependency injection is in fact working.
1_ add a getter to your email on email class. remove the CommandLine interface (If you want to implement this i would recomend you to put CommandLine implmentations on another package say Controller);
And then run your app like this:
#SpringBootApplication
#ComponentScan(basePackages = "com.example")
public class MailTestApplication {
#Value("${admin.mail}")
String email;
public static void main(String[] args) {
// SpringApplication.run(MailTestApplication.class, args);
final ConfigurableApplicationContext context = new SpringApplicationBuilder(MailTestApplication.class).run(args);
Email e = context.getBean(Email.class);
System.out.println(e.getEmail());
}
The Key thing I want to show is that the instance is created by spring thats why the wiring works. and the email gets printed in the console.
Regarding the email class :
#Component
public class Email {
// #Autowired
// private MailSender sender;
#Value("${admin.mail}")
String email;
public Email() {
}
public void sendMail() {
SimpleMailMessage msg = new SimpleMailMessage();
System.out.println(email);
msg.setTo("sample#email.com");
msg.setSubject("Send mail by Spring Boot");
msg.setText("Send mail by Spring Boot");
// sender.send(msg);
}
public String getEmail() {
return email;
}
}
I Comment out the MailSender since I think you need to configure that too, i have made a custom mailSender that uses gmail and other for mailChimp that i can share with you if you need. but again I dont really know what your intent with the app is.
Hope the info helps you.

SpelEvaluationException interpreting "access" string in ResourceServerConfigurerAdapter

Any ideas on this?
From Tomcat:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 8): Method call: Attempted to call method throwOnError(java.lang.Boolean) on null context object
Returned to Client:
java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.throwOnError(#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN'))'
org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:13)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:34)
org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:18)
org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:77)
I do a POST to my authorization server /oauth/token and get a token.
If I take that token and add a Authorization: Bearer header to a GET request to the resource server, I get that error.
In my subclass of ResourceServerConfigurerAdapter, the line it blows up on is here:
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.accessDecisionManager(accessDecisionManager())
.anyRequest()
.fullyAuthenticated();
I know that the resource server recognizes the token because if i leave it out, I get the proper error. If I make up a fake one then I get the "invalid token" message, which is expected. If I use the actual token Spring is jumps in and blows up on the .access()
Thanks in advance for any help. I'm putting the code for my ResourceReserver below:
#Configuration
#EnableWebSecurity
#EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
#Autowired
private OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint;
#Autowired
private ResourceServerTokenServices tokenServices;
#Autowired
private TokenStore tokenStore;
#Autowired
#Qualifier("oauth2ResourceId")
private String oauth2ResourceId;
#Autowired
#Qualifier("oauth2Realm")
private String oauth2Realm;
#Bean
OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint() {
final OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
entryPoint.setRealmName(oauth2Realm);
entryPoint.setTypeName("Basic");
return entryPoint;
}
private AccessDecisionManager accessDecisionManager() {
return new UnanimousBased(Arrays.<AccessDecisionVoter>asList(new ScopeVoter(),
new AuthenticatedVoter(),
new WebExpressionVoter()));
}
private AuthenticationManager getAuthenticationManager() {
final OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
oAuth2AuthenticationManager.setTokenServices(tokenServices);
return oAuth2AuthenticationManager;
}
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**")
.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
.accessDecisionManager(accessDecisionManager())
.anyRequest()
.fullyAuthenticated();
http
.anonymous()
.disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
http
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(logoutSuccessHandler())
.invalidateHttpSession(true);
/*
http
.requiresChannel()
.antMatchers("/oauth/api/**")
.requiresSecure();
http
.portMapper()
.http(8080)
.mapsTo(8443);
*/
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.authenticationManager(getAuthenticationManager())
.tokenServices(tokenServices)
.tokenStore(tokenStore)
.resourceId(oauth2ResourceId);
}
private LogoutSuccessHandler logoutSuccessHandler() {
return new OAuth2SuccessLogoutHandler(tokenStore);
}
static final class OAuth2SuccessLogoutHandler implements LogoutSuccessHandler {
private final TokenStore tokenStore;
public OAuth2SuccessLogoutHandler(final TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
request.toString();
}
}
}
The hasAnyRole() method is not OAuth2 related and therefore is not on the #oauth2 variable (it's on the root so you don't need to qualify it).

Certificate not found in test with EjbContainer and GlassFish

I have an EJB that communicates with a site over https. The logic is to send a xml file in request and receive another in response. This works fine in development environment after adding the site certificate to cacerts inside GlassFish domain. The problem appears when the communication happens in test environment with EJBContainer. Even with org.glassfish.ejb.embedded.glassfish.installation.root and org.glassfish.ejb.embedded.glassfish.instance.root properties defined and certificate added to cacerts, the test execution ends with:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
My EJB is implemented as follows:
#Stateless
#LocalBean
public class CommunicationService {
public String communicate() {
try {
URL url = new URL("https://www.comprasnet.gov.br/XML/treinamento/consultamatserv.asp");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
writer.flush();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
StringBuilder resposta = new StringBuilder();
while ((line = reader.readLine()) != null) {
resposta.append(line);
}
writer.close();
reader.close();
return resposta.toString();
} catch (Exception e) {
return null;
}
}
}
My test configuration uses TestNG 6.8.5, GlassFish 3.1.2.2 and EJBContainer. The configuration starts creating another domain in GlassFish to avoid port conflicts with the default domain if it running. To do that, I've run the command:
asadmin create-domain --portbase 9100 domain-test
I've defined a super class with an annotated method with #BeforeSuite that starts the embedded container with the following content:
public abstract class GlassfishEmbeddedBaseTest {
protected Context ic;
protected UserTransaction tx;
private static EJBContainer ejbContainer;
#BeforeSuite
protected void beforeSuite() throws Exception {
String glassfishHome = System.getenv("GLASSFISH_HOME");
Properties properties = new Properties();
properties.put(EJBContainer.MODULES, new File[] { new File(
"target/classes") });
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
glassfishHome + "/glassfish");
properties.put("org.glassfish.ejb.embedded.glassfish.instance.root",
glassfishHome + "/glassfish/domains/domain-test");
properties.put(EJBContainer.APP_NAME, "app-name");
ejbContainer = EJBContainer.createEJBContainer(properties);
}
#BeforeClass
protected void load() throws Exception {
ic = ejbContainer.getContext();
}
#BeforeMethod
protected void beforeMethod() throws Exception {
tx = (UserTransaction) ic.lookup("java:comp/UserTransaction");
tx.begin();
}
#AfterMethod
protected void rollBack() throws Exception {
tx.rollback();
}
}
In the test class, I've did a look up for my EJB and calls the logic that communicates with the site over https:
public class CommunicationServiceTest extends GlassfishEmbeddedBaseTest {
private CommunicationService communicationService;
#BeforeClass
public void init() throws NamingException {
communicationService = (CommunicationService) ic
.lookup("java:global/app-name/classes/CommunicationService");
}
#Test
public void testCommunicate() {
String response = communicationService.communicate();
Assert.assertNotNull(response);
}
}
I found a bug related to this problem in GlassFish Jira:
https://java.net/jira/browse/GLASSFISH-17179, and as the EJBContainer is based on domain-test and the certified is installed in cacerts from this domain, I think that can be a problem of copy the cacerts defined on instance root property to the temporary directory created on embedded container start time.
How can I lead with that?
EJBContainer offers a property named org.glassfish.ejb.embedded.glassfish.instance.reuse that determine:
If true, no changes are made to the existing configuration file, and a temporary server
instance is not created for the embedded run. Instead, execution happens against the existing server instance. Do not use this option if the reused server instance could be in use by the running GlassFish Server.
Adding this property with value true, before create EJBContainer, solved the issue, since the certificate was already added to the cacerts in domain domain-test and nothing is copied to the temporary folder anymore.