Injecting Spring security config code using javaassist - spring-security

I am having a spring boot application, where during the startup of the application, i am trying to inject the Spring-Security Java configuration using Javaassist. I am generating the java config dynamically from values from DB. Here is the code,
public class WarLock implements SpringApplicationRunListener {
private final SpringApplication application;
public WarLock(SpringApplication application, String[] args) throws IOException {
this.application = application;
}
#Override
public void started() {
try {
System.out.println("Re-write security config called");
rewriteSecurityConfigClass();
} catch (NotFoundException | CannotCompileException e) {
e.printStackTrace();
}
}
private void rewriteSecurityConfigClass() throws NotFoundException, CannotCompileException {
SecurityConfig config = new SecurityConfig();
ClassPool cp = ClassPool.getDefault();
cp.appendClassPath(new LoaderClassPath(application.getClassLoader()));
CtClass compiledClass = cp.get(config.getClass().getName());
CtClass[] argClasses = { cp.get(HttpSecurity.class.getName()) };
CtMethod method = compiledClass.getDeclaredMethod("configure",argClasses);
method.setBody("http .csrf().disable() "+
".authorizeRequests() "+
" .antMatchers(\"/css/**\", \"/index\").permitAll() "+
" .antMatchers(\"/user/**\").hasAuthority(\"USER\") "+
" .antMatchers(\"/tryadmin\").hasAuthority(\"ADMIN\") "+
" .antMatchers(\"/try\").hasAuthority(\"USER\") "+
" .and() "+
".authenticationProvider(authenticationProvider()) "+
" .exceptionHandling() "+
" .authenticationEntryPoint(entryPoint) "+
" .and() "+
".formLogin() "+
" .usernameParameter(\"username\") "+
" .passwordParameter(\"password\") "+
" .successHandler(loginSuccessHandler) "+
" .failureHandler(loginFailureHandler) "+
" .and() "+
".logout() "+
" .permitAll() "+
" .logoutRequestMatcher(new AntPathRequestMatcher(\"/login\", \"DELETE\")) "+
" .logoutSuccessHandler(logoutSuccessHandler) "+
" .deleteCookies(\"JSESSIONID\") "+
" .invalidateHttpSession(true) "+
" .and() "+
".sessionManagement() "+
" .enableSessionUrlRewriting(true) "+
" .maximumSessions(1); ");
compiledClass.toClass();
But the code is failing while startup,
javassist.CannotCompileException: [source error] authorizeRequests() not found in org.springframework.security.config.annotation.web.HttpSecurityBuilder
It is looking for authorizeRequests() inside the class HTTPSecurityBuilder but it actually have to look into the class "HttpSecurity". How can I fix this? Thanks in advance.

Javassist does not support generic types but only respects the erasure of any method. As you can see in from the javadoc of CsrfConfigurer, the disable method is generic which is why Javassist assumes the wrong type and cannot resolve the correct method.
The only way to handle this is to add an appropriate type casting after any generic step of the Spring Security DSL which, unfortunately, is almost every step.
If you are looking for an alternative which does not suffer this limitation, have a look at my library Byte Buddy which works with compiled code rather than strings and does not suffer this limitation.

Related

How to pass request param using feign client?

I am currentlt using Feign Client to call an end point to get outlook mails. But the request parameter are not passing correctly in the api.
#FeignClient(name = "email", url = "${BASE.URI}")
public interface EmailClient {
#GetMapping("/mailfolders/Inbox/messages")
EmailRequestNew getMessages(#RequestHeader HashMap<String, Object> headers,
#RequestParam String filter);
Through service I am calling this Email client to get Mails and passing the filter as
below where from and to are datetime
String param = "$filter=receivedDateTime ge " + from + " and receivedDateTime lt " + to +
"&$expand=singleValueExtendedProperties($filter=id+eq+'String+0x0070')";
but the actual api which are calling is not accurate
assume BASE.URI is something like (10.0.0.120:8080)
https://BASE.URI/mailfolders/Inbox/messages?param=%24filter%3DreceivedDateTime%20ge%202022-11-18T05%3A32%3A56Z%20and%20receivedDateTime%20lt%202022-11-18T09%3A32%3A56Z%26%24expand%3DsingleValueExtendedProperties%28%24filter%3Did%20eq%20%27String%200x0070%27%29
but I want my complete api to be like below when I hardcoded the Request param in the GetMapping
(#GetMapping("/mailfolders/Inbox/messages$filter=receivedDateTime ge 2022-11-18T05:32:56Z and receivedDateTime lt 2022-11-18T09:32:56Z&$expand=singleValueExtendedProperties($filter=id+eq+'String+0x0070')"))
https://dev-api.bhspecialty.com/xchange/v1/mailfolders/Inbox/messages?%24filter=receivedDateTime%20ge%202022-11-18T04:16:58Z%20and%20receivedDateTime%20lt%202022-11-18T08:16:58Z&%24expand=singleValueExtendedProperties($filter=id+eq+'String+0x0070')
How can I achive this.
I tried URL Encoding/Decoding but it is not working.
Example:
URLDecoder.decode(param,"UTF-8")
UriUtils.encodePath(param, "UTF-8");
But nothing is working.
So I was able to do this by creating a RequestInterceptor and then decoding the URI and also change my EmailClient to take PathVariable instead of RequestParam.
#GetMapping(value = "/mailfolders/Inbox/messages?$expand={expand}&$filter={filter}", consumes = MediaType.APPLICATION_JSON_VALUE)
EmailRequestNew getMessages(#RequestHeader HashMap<String, Object> headers,
#PathVariable String filter, #PathVariable String expand);
#Component
public class FeignClientRequestInterceptor implements RequestInterceptor {
private static Logger logger = LogManager.getLogger(FeignClientRequestInterceptor.class);
#Override
public void apply(RequestTemplate template) {
try {
template.uri(URLDecoder.decode(template.request().url(), "UTF-8"));
logger.info("FeignClientRequestInterceptor: " + URLDecoder.decode(template.request().url(), "UTF-8") );
} catch (UnsupportedEncodingException e) {
logger.log(Level.INFO,"Error in FeignClientRequestInterceptor: " + template.request().url() );
throw new RuntimeException(e);
}
}
}
This is the final uri which is created:
https://BASE.URI/mailfolders/Inbox/messages?%24expand=singleValueExtendedProperties($filter=id%20eq%20'String%200x0070')&%24filter=receivedDateTime%20ge%202022-11-21T08:17:59Z%20and%20receivedDateTime%20lt%202022-11-21T12:17:59Z

"no such property: p1 for class: Script3" while setting XML transform on Jenkins

I am trying to fetch some xsl:message in an XSLT transform on a groovy script running on a Jenkins machine and I get no such property: p1 for class: Script3.
Here is the code:
final FileOutputStream compareOutputFile = new FileOutputStream(/*outputHtmlFile*/)
final Transformer transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new FileReader(/*transforXslFile*/)
final String firstSourceFile = /* */
final String secondSourceFile = /* */
transformer.setParameter("firstSourceFile", firstSourceFile)
transformer.setParameter("secondSourceFile", secondSourceFile)
final StreamSource streamSource = new StreamSource(new FileReader(/*emptyXmlFile*/))
final StreamResult streamResult = new StreamResult(compareOutputFile)
String result
try {
final ErrorListener errorListener = new ErrorListener() {
#Override
void warning(final TransformerException e) throws TransformerException {
result = "Warning " + e.getMessage()
}
#Override
void error(final TransformerException e) throws TransformerException {
result = "Error " + e.getMessage()
}
#Override
void fatalError(final TransformerException e) throws TransformerException {
result = "Fatal " + e.getMessage()
}
}
transformer.setErrorListener(errorListener);
} catch (final Throwable e) {
return "Error while setting listener: " + e.getMessage()
}
try {
transformer.transform(streamSource, streamResult)
result = "All good"
}
catch (final Throwable e){
return "exception " + e.getMessage()
}
return result
This method is annotated with #NonCPS.
Then, I echo the return value of this method in the caller method. And I get Error while setting listener: No such property: p1 for class: Script3.
If I'm not mistaken, it could be because the result variable is not accessible within the ErrorListener definition scope.
How can I fetch these xsl:message (which all have the attribute terminate="no") to have them to mark the pipeline step as failed on Jenkins ?
It turned out to be a scope issue.
Using a public field on created ErrorListener to access it resolved the issue.

Xamarin.Android Custom Component Click event binding using xaml

I am creating a custom button component which involves an label and button. I can set the displaytext and other properties using XAML after defining them in the attrs.xml (like
<UButton displayText="Hello" ... />
). but i need to allow the Click event of this control to be handled by the user by defining it in the XAML like
mclick="button_click"
. but i am not able to find a documentation for handling this. Can you please guide me.
Let's look Button's source code.
In this link (Android attrs.xml source code), search onClick, you will find:
<!-- Name of the method in this View's context to invoke when the view is
clicked. This name must correspond to a public method that takes
exactly one parameter of type View. For instance, if you specify
<code>android:onClick="sayHello"</code>, you must declare a
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity). -->
<attr name="onClick" format="string" />
That why when you use :
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="onClick"/>
it will find the onClick method.
Here, you will see how Android handle the android:onClick="onClick", in it's View(Context context, AttributeSet attrs, int defStyleAttr) method, android:onClick="onClick" is correspond to R.styleable.View_onClick:
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new OnClickListener() {
private Method mHandler;
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + View.this.getClass() + idText, e);
}
}
try {
mHandler.invoke(getContext(), View.this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
}
break;
You will find, it find the method by reflection.
So, I guess you have forgot to handle the mclick="button_click". As usually, we don't use this to add click listener, it is a little complex. We use View's setOnClickListener.

Spring AMQP Synchronous Transaction Rollback

Spring AMQP Synchronous Transaction rollback does not work. Here DB transactions within the source are not handled by Spring. I need to have the Spring AMQP messages to be received and send within one transaction. Following are the snapshot of relevant code. Please let me know if you require anything else.
/////Connection Factory initialization
#Bean
public ConnectionFactory getConnectionFactory() {
System.out.println("hello");
configManager();
String ip = ConfigManager.getQueueServerHost();
System.out.println("IP Address : "+ip);
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(ip);
connectionFactory.setUsername(ConfigManager.getQueueUserName());
connectionFactory.setPassword(ConfigManager.getQueuePassword());
connectionFactory.setPort(ConfigManager.getQueueServerPort());
//connectionFactory.setPublisherReturns(true);
//connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
/////Rabbit Template initialization
#Bean
public RabbitTemplate producerRabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(getConnectionFactory());
rabbitTemplate.setRoutingKey(ConfigManager.getProducerQueueName());
rabbitTemplate.setQueue(ConfigManager.getProducerQueueName());
rabbitTemplate.setMandatory(true);
rabbitTemplate.setChannelTransacted(true);
return rabbitTemplate;
}
/////Transactional Code
#Transactional(readOnly=false, rollbackFor=Exception.class)
public void processFile(RabbitTemplate rabbitTemplate)throws Exception{
rabbitTemplate.setRoutingKey(ConfigManager.getConsumerQueueName());
rabbitTemplate.setQueue(ConfigManager.getConsumerQueueName());
Object messageObj = rabbitTemplate.receiveAndConvert();
Message message = null;
try{
if(messageObj != null){
if (messageObj instanceof Message){
message = (Message)messageObj;
System.out.println("Message received is '" + message.getFileName() + "' for Hospital "+message.getHospitalId());
String newFileName = this.process(message.getFileName(), message.getHospitalId());
this.sendMessage(newFileName, message.getHospitalId());
}else{
System.out.println("Unknown message received '" + messageObj + "'");
}
}
}catch(Exception e){
e.printStackTrace();
throw e;
}
}
It works fine for me; I just uploaded a Gist with my test case that shows it working.
I suggest you turn on TRACE level logging to see all the transaction activity (and compare it with the log that I put in the Gist).

twitter4j - access tweet information from Streaming API

My goal is to collect all tweets containing the words "France" and "Germany" and to also collect associated metadata (e.g., the geo coordinates attached to the tweet). I know that this metadata is available, but I can't figure out how to access it with the Java library I'm using : "twitter4j".
Ok, so what I have so far is taken from code samples on the twitter4j site. It prints out all tweets containing my chosen keywords, as they are provided in real-time by Twitter's Streaming API. I call the filter method on my TwitterStream object, and this provides the stream. But I need more control. Namely, I would like to be able to:
1) write the tweets to a file;
2) only print out the first 1000 tweets;
3) access other metadata attached to the tweet (the filter method just prints out the username and the tweet itself).
Here is the code I have so far:
import twitter4j.FilterQuery;
import twitter4j.Status;
import twitter4j.StatusDeletionNotice;
import twitter4j.StatusListener;
import twitter4j.TwitterException;
import twitter4j.TwitterStream;
import twitter4j.TwitterStreamFactory;
import twitter4j.conf.ConfigurationBuilder;
public class Stream {
public static void main(String[] args) throws TwitterException {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true);
cb.setOAuthConsumerKey("bbb");
cb.setOAuthConsumerSecret("bbb");
cb.setOAuthAccessToken("bbb");
cb.setOAuthAccessTokenSecret("bbb");
TwitterStream twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
StatusListener listener = new StatusListener() {
public void onStatus(Status status) {
System.out.println("#" + status.getUser().getScreenName() + " - " + status.getText());
}
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
}
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
System.out.println("Got track limitation notice:" + numberOfLimitedStatuses);
}
public void onScrubGeo(long userId, long upToStatusId) {
System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
}
public void onException(Exception ex) {
ex.printStackTrace();
}
};
FilterQuery fq = new FilterQuery();
String keywords[] = {"France", "Germany"};
fq.track(keywords);
twitterStream.addListener(listener);
twitterStream.filter(fq);
}
}
After looking at this with fresh eyes I realised the solution (which was pretty obvious). Editing the following part of the code:
public void onStatus(Status status) {
System.out.println("#" + status.getUser().getScreenName() + " - " + status.getText());
}
allows me to access other metadata. For example, if I want to access the tweet's date, I simply need to add the following:
System.out.println(status.getCreatedAt());
The Error 401 comes when the API is trying to access some information which is unable to fetch at present. So you need to check the permission which are allowed on twitter. Change it to READ, WRITE and ... for full API access. Or there might be problem as you might be using the proxy server. Hence mention the proxy details using the following commands.
System.getProperties().put("http.proxyHost", "10.3.100.211");
System.getProperties().put("http.proxyPort", "8080");
To write tweets on file:
FileWriter file = new FileWriter(....);
public void onStatus(Status status) {
System.out.println("#" + status.getUser().getScreenName() + " - " + status.getText() + " -> "+ status.getCreatedAt());
try {
file.write(status.getUser().getScreenName() + " - " + status.getText() + " -> "+ status.getCreatedAt() +"\n");
file.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Resources