Grails Send HTML Mail signature of method error - email

I am trying to figure out how to send html mail with mail send. I am stuck and am getting “No signature of method: applicable for argument types: (null) values: [null]” It is the request id variable that I am trying to pass to the email.
My job is below
import groovy.util.logging.Slf4j
import org.camunda.bpm.client.ExternalTaskClient
import org.camunda.bpm.client.task.ExternalTask
import org.camunda.bpm.engine.ProcessEngines
import org.camunda.bpm.engine.ProcessEngine
import grails.core.GrailsApplication
import edu.andrews.bpm.changeofprogram.EmailReceivedRequestExternalTaskService
import org.camunda.bpm.client.interceptor.auth.BasicAuthProvider
//import edu.andrews.bpm.changeofprogram.StudentApprovalRequestController
import org.springframework.beans.factory.annotation.Value
#Slf4j
class EmailReceivedRequestExternalTaskJob {
EmailReceivedRequestExternalTaskService emailReceivedRequestExternalTaskService
// StudentApprovalRequestController studentApprovalRequestController
#Value('${camunda.bpm.rest.endpoint}')
String bpmRESTUrl
static triggers = {
// s m h D M W Y
cron name: 'EmailReceivedRequestExternalTask', startDelay: 10000, cronExpression: '0 */5 * * * ?'
}
def execute() {
// execute job by Polling Camunda BPM for tasks on the topic
// specified in the Camunda Modeler implementation semantics
log.info("EmailReceivedRequestExternalTaskService polling of BPM endpoint ${bpmRESTUrl} starting...")
ExternalTaskClient client = ExternalTaskClient.create()
.baseUrl(bpmRESTUrl)
.maxTasks(5)
// .addInterceptor(new BasicAuthProvider(username, password))
.build()
client.subscribe("ProgramChangeReceivedEmail") // topic from Camunda Modeler
.lockDuration(10000L)
.handler( { externalTask, externalTaskService ->
log.info("Executing external task handler...")
Map<String,Object> procVars = externalTask.getAllVariables()
Map<String,Object> stuApproveVars = externalTask.getAllVariables()
String requestId = procVars.requestId
// def String idn = procVars.idn
String email = stuApproveVars.email
emailReceivedRequestExternalTaskService.studentApprovalRequest(email)
emailReceivedRequestExternalTaskService.studentApprovalRequest(idn)
log.info("\texternalTaskVars: ${procVars}")
externalTaskService.complete(externalTask)
}).open()
client.stop()
log.info("EmailReceivedRequestExternalTaskService...")
//log.info(idn)
}
}
Here is the service call
import grails.gorm.services.Service
import grails.gorm.transactions.Transactional
#Transactional
class EmailReceivedRequestExternalTaskService {
// def ApplyAcknowledgment(String email, String studentName) {
def ChangeAcknowledgment(String email, String requestId) {
sendMail {
async true
from "donnotreply#andrews.edu"
to email
subject "Change of Program"
// text "Thank you for submitting your change of program request. You will receive an email when it has been approved or denied."
html view: "/studentApprovalRequest/AcknowledgementEmail", model: [param1: requestId]
// html view: "/freeclass/FreeClassApplyAcknowledgment"
}
}
}
Here is the gsp html info
<html>
<head>
</head>
<body>
<p>Thank you for submitting your change of program request. You will receive an email when it has been approved or denied.</p>
<p>record: ${requestId}</p>
"<p>Thank you.</p>"
</body>
</html>

One of the issues is that it looks like your GSP is expecting a model variable named requestId but EmailReceivedRequestExternalTaskJob is creating a model variable called param1:
html view: "/studentApprovalRequest/AcknowledgementEmail", model: [param1: requestId]
You could change that to something like this:
html view: "/studentApprovalRequest/AcknowledgementEmail", model: [requestId: requestId]

It was my misunderstanding of things. In the .gsp page I should have referenced my variable as ${param1} instead of ${requestId}

Related

Can we link external API for a confluence page?

I have a requirement where when the user clicks publish button in a Confluence page, need to trigger an external API (post endpoint ) where I can save confluence data in external DB.
Have a look at the Event Listener module and How to build an Event Listener. Basically, you create a plugin that captures the com.atlassian.confluence.event.events.content.page.PageEvent. In your case, you might use PageCreateEvent or PageUpdateEvent. This is for Confluence Server. As for Confluence Cloud, it might be in JavaScript or something.
If you don't feel like developing an addon for it,
then i recommend using an addon adaptavist scriprunner :
that's the easiest way to accomplish that (although not free!)
example from their webpage:
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.jira.JiraApplicationType
import com.atlassian.confluence.event.events.space.SpaceCreateEvent
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Response
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.sal.api.net.ResponseHandler
import groovy.json.JsonBuilder
import
static com.atlassian.sal.api.net.Request.MethodType.POST
def appLinkService = ComponentLocator.getComponent(ApplicationLinkService)
def appLink = appLinkService.getPrimaryApplicationLink(JiraApplicationType)
def applicationLinkRequestFactory = appLink.createAuthenticatedRequestFactory()
def event = event as SpaceCreateEvent
def space = event.space
def input = new JsonBuilder([
projectTypeKey : "business",
projectTemplateKey: "com.atlassian.jira-core-project-templates:jira-core-task-management",
name : space.name,
key : space.key,
lead : event.space.creator.name,
]).toString()
def request = applicationLinkRequestFactory.createRequest(POST, "/rest/api/2/project")
.addHeader("Content-Type", "application/json")
.setEntity(input)
request.execute(new ResponseHandler<Response>() {
#Override
void handle(Response response) throws ResponseException {
if (response.statusCode != 201) {
log.error("Creating jira project failed: ${response.responseBodyAsString}")
}
}
})

Redirect in Scala play framework

I have a problem with Redirect in Scala play framework.
How can I redirect to view BooksController.index() ? In documentation they suggest to use Redirect but I don't know how.
def edit(Id: Int) = Action {
val book: Book = Book.findById(Id)
Ok(views.html.edit())
}
def update = Action {
implicit request =>
val (id, title, price, author) = bookForm.bindFromRequest.get
val book: Book = Book.findById(id)
book.id = id
book.title = title
book.price = price
book.author = author
Redirect(routes.BooksController.index())
}
Now can recognize --> import play.api.mvc.Results._
But i have an error --> "object java.lang.ProcessBuilder.Redirect is not a value"
If you would really like to continue using reverse routing in your code instead of having string uri values all over the place, see this:
Redirect with Bad Request Status.
The Redirect function accepts only a String or Call.
Try the following steps:
0) Add in the BookController
import play.api.mvc._
1) Add the following string in your route config file(hard disk location: controllers/BooksController)
GET /redirectedPage controllers.BooksController.index
2) Define a variable in the BookController
val Home = Redirect(routes.BookController.index())
3) Describe in the BookController
def update = Action {
implicit request => Home
}
Also do "sbt clean; sbt compile" to recompile auto-calls in ReverseRoutes.scala.
Well done.
The last line in the update action is the Redirect call which redirects to BooksController index route
import play.api.mvc.Results._
Redirect(routes.BooksController.index())

How to PUT XmlSlurper back to REST with HttpBuilder

I'm trying to make GET and then PUT call on XML REST web service.
I do it this way:
#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7')
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
import groovy.xml.XmlUtil
def url = "http://localhost:81"
def pathPrefix = "/api/v1"
def http = new HTTPBuilder(url)
def profile = http.request(GET, XML) { req ->
uri.path = "$pathPrefix/profiles/55"
response.success = {resp, xml ->
xml
}
}
println XmlUtil.serialize(profile) // this is fine!
Now i'm going to change and save
profile.name = "New Name"
// this is not fine (i have 400 Bad Request)
// because it sends body not in XML
def savedProfile = http.request(PUT, XML) { req ->
uri.path = "$pathPrefix/profiles/55"
body = profile
response.success = {resp, xml ->
xml
}
}
println XmlUtil.serialize(savedProfile)
When i make PUT request HTTPBuilder do not send XML. It sends string, made of profile.toString().
It it not what i'm expecting.
How to send XmlSlurper object (that i obtained earlier) in PUT request?
Thank you.
I think i found the solution.
When i define body configuration value, i have to write
body = {
mkp.yield profile
}

Play2-mini and Akka2 for HTTP gateway

I'm evaluating the possibility of using Play2-mini with Scala to develop a service that will sit between a mobile client and existing web service. I'm looking for the simplest possible example of a piece of code where Play2-mini implements a server and a client. Ideally the client will use Akka2 actors.
With this question, I'm trying to find out how it is done, but also to see how Play2-Mini and Akka2 should co-operate. Since Play2-Mini appears to be the replacement for the Akka HTTP modules.
Play2-mini contains the following code example, in which I created two TODO's. If someone can help me with some sample code to get started, I will be really grateful.
package com.example
import com.typesafe.play.mini._
import play.api.mvc._
import play.api.mvc.Results._
object App extends Application {
def route = {
case GET(Path("/testservice")) & QueryString(qs) => Action{ request=>
println(request.body)
//TODO Take parameter and content from the request them pass it to the back-end server
//TODO Receive a response from the back-end server and pass it back as a response
Ok(<h1>Server response: String {result}</h1>).as("text/html")
}
}
}
Here's the implementation of your example.
Add the following imports:
import play.api.libs.ws.WS
import play.api.mvc.BodyParsers.parse
import scala.xml.XML
Add the following route:
case GET(Path("/testservice")) & QueryString(qs) => Action{ request =>
Async {
val backendUrl = QueryString(qs,"target") map (_.get(0)) getOrElse("http://localhost:8080/api/token")
val tokenData = QueryString(qs,"data") map (_.get(0)) getOrElse("<auth>john</auth>")
WS.url(backendUrl).post(XML loadString tokenData).map { response =>
Ok(<html><h1>Posted to {backendUrl}</h1>
<body>
<div><p><b>Request body:</b></p>{tokenData}</div>
<div><p><b>Response body:</b></p>{response.body}</div>
</body></html>).as("text/html") }
}
}
All it does, is forwarding a GET request to a back-end serivce as a POST request. The back-end service is specified in the request parameter as target and the body for the POST request is specified in the request parameter as data (must be valid XML). As a bonus the request is handled asynchronously (hence Async). Once the response from the back-end service is received the front-end service responds with some basic HTML showing the back-end service response.
If you wanted to use request body, I would suggest adding the following POST route rather than GET (again, in this implementation body must be a valid XML):
case POST(Path("/testservice")) & QueryString(qs) => Action(parse.tolerantXml){ request =>
Async {
val backendUrl = QueryString(qs,"target") map (_.get(0)) getOrElse("http://localhost:8080/api/token")
WS.url(backendUrl).post(request.body).map { response =>
Ok(<html><h1>Posted to {backendUrl}</h1>
<body>
<div><p><b>Request body:</b></p>{request.body}</div>
<div><p><b>Response body:</b></p>{response.body}</div>
</body></html>).as("text/html") }
}
}
So as you can see, for your HTTP Gateway you can use Async and play.api.libs.ws.WS with Akka under the hood working to provide asynchronous handling (no explicit Actors required). Good luck with your Play2/Akka2 project.
Great answer by romusz
Another way to make a (blocking) HTTP GET request:
import play.api.libs.ws.WS.WSRequestHolder
import play.api.libs.ws.WS.url
import play.api.libs.concurrent.Promise
import play.api.libs.ws.Response
val wsRequestHolder: WSRequestHolder = url("http://yourservice.com")
val promiseResponse: Promise[Response] = wsRequestHolder.get()
val response = promiseResponse.await.get
println("HTTP status code: " + response.status)
println("HTTP body: " + response.body)

CSRF token generation

This is a question about generating CSRF tokens.
Usually I'd like to generate a token based off of a unique piece of data associated with the user's session, and hashed and salted with a secret key.
My question is in regards to generating tokens when there is NO unique user data to use. No sessions are available, cookies are not an option, IP address and things of that nature are not reliable.
Is there any reason why I cannot include the string to hash as part of the request as well?
Example pseudocode to generate the token and embed it:
var $stringToHash = random()
var $csrfToken = hash($stringToHash + $mySecretKey)
click me
Example server-side validation of the CSRF token
var $stringToHash = request.get('key')
var $isValidToken = hash($stringToHash + $mySecrtKey) == request.get('csrfToken')
The string being used in the hash would be different on each request. As long as it was included in each request, the CSRF token validation could proceed. Since it is new on each request and only embedded in the page, outside access to the token would not be available. Security of the token then falls to the $mySecretKey being known only to me.
Is this a naive approach? Am I missing some reason why this cannot work?
Thanks
Is there any reason why I cannot include the string to hash as part of the request as well?
CSRF tokens have two parts. The token embedded in the form, and a corresponding token somewhere else, be it in a cookie, stored in a session or elsewhere. This use of elsewhere stops a page being self contained.
If you include the string to hash in the request, then the request is self contained, so copying the form is all an attacker needs to do, as they have both parts of the token, and thus there is no protection.
Even putting it in the form URL means that it's self contained, the attacker simply copies the form and the submission URL.
Try base64_encode(openssl_random_pseudo_bytes(16)).
https://github.com/codeguy/php-the-right-way/issues/272#issuecomment-18688498 and I used it for my form example in https://gist.github.com/mikaelz/5668195
CSRF token meant to prevent (unintentional) data modifications, which are usually applied with POST requests.
Thus, you must include CSRF token for each request that changes data (either GET or POST request).
My question is in regards to
generating tokens when there is NO
unique user data to use. No sessions
are available, cookies are not an
option, IP address and things of that
nature are not reliable.
Then simply create a unique user id for each visitor.
Include that id in a cookie or in the URLs (if cookies are disabled).
Edit:
Consider the following event:
You have logged-in to your facebook account and then entered to some arbitrary website.
In that website there's a form that you submit, which tells your browser to send a POST request to your facebook account.
That POST request may change your password or add a comment etc, because that the facebook application recognized you as a registered & logged-in user. (unless there's another blocking mechanism, like CAPTCHA )
I think the best idea to make hash based on HMAC, i.e. make hash encrypted by some password this sequence: username+user_id+timestamp. Each request the hash must be different, timestamp must be if you don't want to get simple replay the hash in attack.
You simply just need the same "token" in the URL/form and in the cookie. This means that you could have your page setting the token cookie to whatever it wants to (preferably some random value) by JavaScript and then just pass the very same value in all requests that goes to your server (as a URI ?param or form-field). No need to have your server generating the cookie.
This is safe as long as we trust that the browser doesn't allow pages from a domain to edit/read cookies for other domains, and this is assumed to be quite secure today.
Having your server generating the token will assume that this token can be safely transmitted to your browser without being picked up by any CSRF attempts (why take the risk?). Though you could put more logic into a server generated token, but to prevent CSRF there is no need.
(If I'm wrong here please let me know)
I wanna say your approach works, because CSRF attack is the attacker utilizing victim's browser to forge a logged-in status, why can they do so? because on most server side the session check is based on a SessionID in cookie, and cookie is a piece of data will be automatically attached to a HTTP request sent to server.
Therefore, there are two key factors for defending CSRF
Generate a challenge token, and require client to pass it to server in a non-cookie way, either URL param or POST form is ok.
Keep the token safe as what you did to the SessionID, for instance, using SSL.
I recommend reading CSRF Prevention Cheat Sheet
There are multiple implementation of CSRF token. The key thing is whether this csrf token is generated on the client side or server side. Because the implementation changes drastically for these two scenarios and the entropy of the token as well.
For server side, SecureRandom is the preferred way but in your case you want to generate the CSRF token before any user is identified, window.crypto provides this functionality where you can generate a unguessable enough string to be used for CSRF token.
With the help of CSRF token we can sure incoming request is authenticated (know user not hacker)
Please note i have required below approach but google can't help me even on stackoverflow i did't get mentioned code below but after collection of stackoverflow answer i have made my day. So it's useful for further searching/ specially for beginners
I have described below Spring MVC with Spring Interceptor
Note - I have used google cache to store salt in cache for re verification
below dependency need to add pom.xml
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
below HandlerInterceptorAdapter implemention
package com.august.security;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
public class CsrfSecurity extends HandlerInterceptorAdapter {
List<String> urlList= new LinkedList<>();
private static final String CSRF_TAG = "CSRF-CHECK";
#SuppressWarnings("unchecked")
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handleer)
throws Exception {
System.out.println("Inside Pre Handler");
String reqUrl = request.getRequestURI().toString();
System.out.println("Request URL : " + reqUrl);
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
//local host url http://localhost:8080/august/
if (request.getRequestURI().contains("/august/")) {
System.out.println("pre handler return true");
//it will return and next executed postHandelr method
//because of on above url my webApplication page working
return true;
}
if (ignoreUrl().contains(request.getRequestURI())) {
System.out.println("inside ignore uri");
return true;
} else {
System.out.println("CSRF Security intercepter preHandle method started.......");
String salt = request.getParameter("csrfPreventionSalt");
HttpSession sessionAttribute = request.getSession();
Cache<String, Boolean> csrfPreventionSalt = (Cache<String, Boolean>) sessionAttribute
.getAttribute("csrfPreventionSalt");
if (csrfPreventionSalt == null) {
System.out.println("Salt not matched session expired..");
parameterValuesPrint(request, "saltCacheNotFound");
response.sendRedirect("error");
return false;
} else if (salt == null) {
parameterValuesPrint(request, "noSaltValue");
System.out.println("Potential CSRF detected !! inform ASAP");
response.sendRedirect("error");
return false;
} else if (csrfPreventionSalt.getIfPresent(salt) == null) {
System.out.println("saltValueMisMatch");
System.out.println("Potential CSRF detected !! inform ASAP");
response.sendRedirect("error");
} else {
request.setAttribute("csrfPreventionSalt", csrfPreventionSalt);
}
return true;
}
}
#SuppressWarnings("unchecked")
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
System.out.println("Inside post Handler");
System.out.println("CSRF Security key generator method started");
try {
//localhost url http://localhost:8080/august/
//api is my controller path so no need to genrate token for api
if (request.getRequestURI().contains("/august/api/")) {
System.out.println("No need to genrate salt for api");
} else {
HttpSession sessionAttribute = request.getSession();
Cache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>) sessionAttribute
.getAttribute("csrfPreventionSalt");
System.out.println("csrfPreventionSaltCache ::: " + csrfPreventionSaltCache);
if (csrfPreventionSaltCache == null) {
csrfPreventionSaltCache = CacheBuilder.newBuilder().maximumSize(5000)
.expireAfterWrite(20, TimeUnit.MINUTES).build();
request.getSession().setAttribute("csrfPreventionSaltCache", csrfPreventionSaltCache);
}
String salt = RandomStringUtils.random(20, 0, 0, true, true, null, new SecureRandom());
System.out.println("csrfPreventionSalt genrated ::: " + salt);
csrfPreventionSaltCache.put(salt, Boolean.TRUE);
if (modelAndView != null) {
System.out.println("Model and view not null and salt is added in modelAndView");
modelAndView.addObject("csrfPreventionSalt", salt);
}
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion : ");
if (ex != null) {
System.out.println("exception : " + ex.getMessage());
ex.printStackTrace();
}
}
private List<String> ignoreUrl() {
if(urlList == null) {
urlList.add("/august/error");
//add here your ignored url.
}
return urlList;
}
private void parameterValuesPrint(HttpServletRequest request, String err) {
StringBuilder reqParamAndValue = new StringBuilder();
Enumeration<?> params = request.getParameterNames();
while (params.hasMoreElements()) {
Object objOri = params.nextElement();
String param = (String) objOri;
String value = request.getParameter(param);
reqParamAndValue = reqParamAndValue.append(param + "=" + value + ",");
}
System.out.println(CSRF_TAG + " " + err + "RequestedURL : " + request.getRequestURL());
}
}
Below is Interceptor registration with spring context
package com.august.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.august.security.CsrfSecurity;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.august")
public class SpringConfiguration extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
//viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public CsrfSecurity csrfSecurity() {
return new CsrfSecurity();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CsrfSecurity());
}
}
below is my controller
package com.august.v1.appcontroller;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class HomeController {
#Autowired
HttpSession httpSession;
#RequestMapping("/")
public String index(Model model) {
httpSession.invalidate();
System.out.println("Home page loaded");
return "index";
}
}
below is my index.jsp jsp page
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" isELIgnored="false"%>
//don't forget to add isELIgnored="false" on old(version) jsp page because of i
//have wasted 1 hour for this
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>ADS Home</title>
</head>
<body>
<h1>${csrfPreventionSalt}</h1>
<input type="hidden" name="csrfPreventionSalt" value=${csrfPreventionSalt}>
</body>
</html>
For Understanding about CSRF - CSRF explanation
CSRF utilizes the user's session, so, if you don't have one, there is no CSRF.