Downloading content from URL without prompting for authentication in Grails - grails

I am working on a web app programmed in Grails. I have a page used to display a certain report and these reports can contain attachments. The attachment is stored in documentum and currently, when the user clicks on it, it is only a link to the location in documentum where the attachment is stored and prompts the user for his credentials. My app has documentum credentials stored in the configuration file therefore I want to use those rather than forcing the user to enter his own credentials. I am using RESTful services to retrieve link but I am trying to find a way to use the link to download directly to the users computer.
private def getFileInfo(def id, def subject) {
// product show view needs the following four lists to display the document information correctly
def ATRReportInstance = ATRReport.findByTrackingNumber(id)
def linkList = []
def nameList = []
def formatList = []
def idList = []
// open up a connection to the documentum server
def doc = connectToDocumentum()
if (!doc) return
def rest = doc.rest
def response = doc.response
if (response.status == 200) {
// retrieve the folder for this product (the name of this folder is the product's ID)
def rObjectId = rest.get(documentumServer + "/repositories/" + documentumfilestore + "?dql=select r_object_id from dm_folder where any r_folder_path='" + atrreportfolderpath + "/" + id + "'") {
auth authuser, authpass
}
// get the folder's ID from the folder object retrieved above
def folderObjectID
rObjectId.json.entries.each {
entry - >
folderObjectID = entry.content.properties.r_object_id
}
// get all of the documents in the product's MSDS folder using the folder ID retrieved above
def resp = rest.get(documentumServer + "/repositories/" + documentumfilestore + "?dql=select r_object_id, object_name, a_content_type, subject from cbs_document where any i_folder_id= '" + folderObjectID + "'") {
auth authuser, authpass
}
// cycle through the documents above to populate the four MSDS document information lists
def x = 0
resp.json.entries.each {
entry - >
if (entry.content.properties.subject == subject) {
// get the document's content object from the document's ID
def content = rest.get(documentumServer + "/repositories/" + documentumfilestore + "/objects/" + entry.content.properties.r_object_id + "/contents/content" + "?media-url-policy=local") {
auth authuser, authpass
}
if (entry.content.properties.r_object_id != null && ATRReportInstance.inactiveFiles != null && ATRReportInstance.inactiveFiles.contains(entry.content.properties.r_object_id.toString())) {} else {
linkList[x] = getLink(content.json.links, "enclosure")
if (linkList[x].contains("format=msg"))
linkList[x] = linkList[x].toString().substring(0, linkList[x].toString().indexOf("content-media")) + "content-media.msg"
formatList[x] = entry.content.properties.a_content_type
nameList[x] = entry.content.properties.object_name
idList[x] = entry.content.properties.r_object_id
x++
}
}
}
return [linkList: linkList, nameList: nameList, formatList: formatList, idList: idList]
} else {
// return null if documentum is unavailable
flash.message = message(code: 'error.documentum.unavailable')
return null
}
}
I'm thinking writing another function that can take in a URL and download the document to the user might work, but I can't figure how to retrieve that document within Grails.

If you want to bypass login you could either setup a SSO solution (requires some work for DCTM) or do a function as you suggest. However you should consider the licensing terms when doing this.

Here is the solution I implemented and that worked. It is a method that downloads a file in documentum using authentication credentials found in a configuration file.
def exportAttachment() {
//uses parameters from gsp file
def url = params.url
def name = params.name
def format = params.format
def extension
//find proper extension
for (s in documentumExtMap) {
if (s.value.equals(format)) {
extension = s.key
}
}
def connection = new URL(url).openConnection()
def remoteAuth = "Basic " + "${authuser}:${authpass}".bytes.encodeBase64()
connection.setRequestProperty("Authorization", remoteAuth)
def dataStream = connection.inputStream
response.setContentType("application/octet-stream")
response.setHeader('Content-disposition', 'Attachment; filename=' + name + '.' + extension)
response.outputStream << dataStream
response.outputStream.flush()
}
The method has three parameters: url, name, format.
Url is the location of the file in documentum.
Name is the name of the download client side
Format is the type of file that is being downloaded. In my case, I had to use this to get the proper extension needed for the file.

Related

How to update credentials of specific folder in Jenkins using Groovy script?

I want to update a credentials object of an existing folder. How can I do that using groovy?
Here is what I have so far:
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.hudson.plugins.folder.Folder
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
// Init
String user_name = "my_user_name"
String user_pass = "my_new_pass"
String folderName = "Projects"
Folder targetFolder = null
// Get folder:
def allJenkinsItems = Jenkins.getInstance().getItems();
for (currentJenkinsItem in allJenkinsItems)
{
if(!(currentJenkinsItem instanceof Folder)) {continue}
if(((Folder)currentJenkinsItem).getFullName().equals(folderName))
{
targetFolder = (Folder)currentJenkinsItem;
}
}
if (targetFolder == null) {println "Failed to find folder: folderName"; return}
// Get target credentials of that folder:
def credsList = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
targetFolder,
null,
null
);
// Get target creds out of the list - will get the first one it encounters:
def targetCreds = credsList.findResult { it.username == user_name ? it : null }
if (targetCreds == null) {println "Failed to find username: $user_name under credentials of folder: $folderName"; return}
// Gets store - how to get the folder's store??
def credentials_store = Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
// Try to update the creds of the folder.
// **updateResult is always 'false' here**
def updateResult = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
targetCreds,
new UsernamePasswordCredentialsImpl(targetCreds.scope, targetCreds.id, targetCreds.description, targetCreds.username, user_pass)
)
if (updateResult) {
println "Success changing password for ${user_name}"
} else {
println "Failed changing password for ${user_name}"
}
But when I am trying to update - I get a updateResult == false.
How can I update the credentials after they are found?
Found it myself:
/*
* Configures single (username & password) credentials for a folder in global domain
* if already exists a credentials with defined username - it will update it
* if more than one exists - the first one it encounters will be updated
*/
import java.util.logging.Logger
import jenkins.model.*
import com.cloudbees.hudson.plugins.folder.*;
import com.cloudbees.hudson.plugins.folder.properties.*;
import com.cloudbees.hudson.plugins.folder.properties.FolderCredentialsProvider.FolderCredentialsProperty;
import com.cloudbees.plugins.credentials.impl.*;
import com.cloudbees.plugins.credentials.*;
import com.cloudbees.plugins.credentials.domains.*;
// Init
def logger = Logger.getLogger("")
jenkins = Jenkins.instance
String user_name = "my_user_name"
String user_pass = "my_new_pass"
String description = "my desc"
String folderName = "Projects"
String id = java.util.UUID.randomUUID().toString()
Credentials c = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, id, "description: "+id, user_name, user_pass)
logger.info("Configuring domain credentials for folder: $folderName")
for (folder in jenkins.getAllItems(Folder.class)) {
if(folder.name.equals(folderName)) {
AbstractFolder<?> folderAbs = AbstractFolder.class.cast(folder)
FolderCredentialsProperty property = folderAbs.getProperties().get(FolderCredentialsProperty.class)
// If not defined FolderCredentialsProperty yet - define and finish
if(property == null) {
logger.info("Initializing folder credentials store and add credentials in global store")
property = new FolderCredentialsProperty([c])
folderAbs.addProperty(property)
jenkins.save()
return
}
// Check for existing credentials - and update their password & description
// will update the first credentials it encounters
def credentials_store = property.getStore()
List<com.cloudbees.plugins.credentials.Credentials> folderCredentialsList = property.getCredentials()
for (creds in folderCredentialsList) {
logger.info("Checking existing credentials of folder: $folderName for user: $user_name")
if (creds.username.equals(user_name)) {
// Found username - updating it
// Try to update the creds of the folder:
def updateResult = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
creds,
new UsernamePasswordCredentialsImpl(creds.scope, creds.id, description, creds.username, user_pass)
)
if (updateResult) {
println "Update successful"
} else {
println "Update failed"
}
jenkins.save()
return
}
}
logger.info("Didn't find credntials with username: $user_name - adding new one")
// If got here - then:
// 1. There is already a FolderCredentials property defined for folder: folderName
// 2. didn't find any credentials(of username & password type) with username == user_name
// so just add the new credentials
property.getStore().addCredentials(Domain.global(), c)
jenkins.save()
return
}
}

Trying to delete a uploaded file from directory file system

I have a web app that uploads files to file system and show them in a list. I am trying to delete the item with a button. I know I need to get the path of the directory file to be able to delete it and I believe this is where I am stuck:
def delete = {
def doc = Document.get(params.id)
def path = Document.get(path.id)
doc.delete(path)
redirect( action:'list' )
}
error I am getting: No such property: path for class: file_down.DocumentController Possible solutions: flash
It seems to me def path = Document.get(path.id) is wrong, in that case how do we find the path of a document ?
This is my upload method where I upload the files, assign it to a specific filesize, date, and fullPath( which is the uploaded folder)
def upload() {
def file = request.getFile('file')
if(file.empty) {
flash.message = "File cannot be empty"
} else {
def documentInstance = new Document()
documentInstance.filename = file.originalFilename
documentInstance.fullPath = grailsApplication.config.uploadFolder + documentInstance.filename
documentInstance.fileSize = file.getSize() / (1024 * 1024)
documentInstance.company = Company.findByName(params.company)
if (documentInstance.company == null) {
flash.message = "Company doesn't exist"
redirect (action: 'admin')
}
else {
file.transferTo(new File(documentInstance.fullPath))
documentInstance.save()
redirect (action:'list', params: ['company': params.company])
}
}
}
I think you have an error in this line:
def path = Document.get(path.id)
You try to get path.id from the path variable you are just declaring.
I'm pretty sure that you mean
def path = new File(doc.fullPath)
path.delete() // Remove the file from the file-system
doc.delete() // Remote the domain instance in DB
Alternative:
class Document {
// Add this to your Document domain
def beforeDelete = {
new File(fullPath).delete()
}
}
and then you could just do this in your controller:
def delete = {
def doc = Document.get(params.id)
doc.delete() // Delete the domain instance in DB
redirect( action:'list' )
}

How to get the object url with alias name from aws s3 using CloudFront

I am uploading files with unique id like 'd9127dfd01182afe7d34a37' as object name to amazon s3 and storing the file information with my local database including original name of the file. And I am using CloudFront url to download the file.
If I download the file using CloudFront url file name is d9127dfd01182afe7d34a37. But I need to change file name again to it's original name wich I have in my database. I don't want to download it. I want to give the url with original name to the client(WebUI) and client can download it through url.
serverside code
document_url = initialize_cloud_service(document.provider['primary']).get_object_url(document_id, expires_at, 'CloudFront' )
if document_url
item = {}
item['id'] = document['_id'].to_s
item['name'] = document['name']
item['mime_type'] = document['mime_type']
item['url'] = document_url
return {success: true, message: MESSAGES['get_url_succuss'],data: item}.to_json
end
client side code
download: function(response){
file = response.data
link = document.createElement('a');
link.download = file.name;
link.href = file.url;
link.click();
},
Is there any way to achieve this? Please help me out. I am using ruby on rails and mongodb as local database.
Thanks
I have achieved by doing the following changes
Server Side code
begin
expires_at = Time.now.to_i + 30.seconds.to_i
options = nil
selected_provider = provider || document.provider['primary']
case selected_provider
when "s3"
options = {"response-content-disposition" => "attachment; filename=#{document['name']}"}
downloadable_url = initialize_cloud_service(selected_provider).get_downloadable_url(document_id, expires_at, options)
when "google_cloud"
downloadable_url = initialize_cloud_service(selected_provider).get_downloadable_url(document_id, expires_at, options)
downloadable_url += "&response-content-disposition=attachment%3B%20filename%3D#{document['name']}"
end
item = {}
item['id'] = document['_id'].to_s
item['name'] = document['name']
item['mime_type'] = document['mime_type']
item['url'] = downloadable_url
return {success: true, message: MESSAGES['get_url_succuss'],data: item}.to_json
rescue Exception => e
puts 'Exception in download, message: ' + e.message
return {success: false, message: MESSAGES['default']}.to_json
end
client side code
download: function(response){
var hiddenIFrameID = 'hiddenDownloader',
iframe = document.getElementById(hiddenIFrameID);
if (iframe === null) {
iframe = document.createElement('iframe');
iframe.id = hiddenIFrameID;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
iframe.src = response.data.url;
},

commonsMultipartFile trouble

Hi I have am trying to implement a file upload in my application where the file uploaded is parsed and an entry is created in the database using that information.
def save = {
def file = request.getFile("file");
def filename = file.getOriginalFilename();
def type = filename.split('\\.');
if(!file.isEmpty()){
if(type[1] == "properties"){
redirect(action:"parsePropertyFile", params:params);
}
}
}
def parsePropertyFile = {
println "\n"
println params.file;
println "\n";
def f = params.file;
println f;
def filename = f.getOriginalFilename();
println filename;
}
when I print out f this is output:
org.springframework.web.multipart.commons.CommonsMultipartFile#29d32df9
but when I try to call getOriginalFilename() on f I get the following error:
groovy.lang.MissingMethodException: No signature of method:
java.lang.String.getOriginalFilename() is applicable for argument types: () values: []
I also printed out file from the save function and the output of that is also:
org.springframework.web.multipart.commons.CommonsMultipartFile#29d32df9
so why am I getting the error?
Instead of redirecting, can you just call your another function? Redirect will issue an http redirect with the file as param with no need.
if(type[1] == "properties") {
parsePropertyFile(file)
}
And then:
private def parsePropertyFile(def file) {
String filename = file.getOriginalFilename();
...
}
In your parsePropertyFile action you aren't getting a File object, you're getting the String from params. Just like in your save action, you need to do
def f = request.getFile('file')
println f.getOriginalFilename()

How to export pdf report in jasper reports

I want to export a report as pdf and it should ask the user for a download location. How do I do this in grails?
This is my code:
def exportToPdf(JasperPrint jasperPrint,String path,request){
String cur_time =System.currentTimeMillis();
JRExporter pdfExporter = null;
pdfExporter = new JRPdfExporter();
log.debug("exporting to file..."+JasperExportManager.exportReportToPdfFile(jasperPrint, "C:\\pdfReport"+cur_time+".pdf"));
return ;
}
In jasper controller:
/**
* Generate a html response.
*/
def generateResponse = {reportDef ->
if (!reportDef.fileFormat.inline && !reportDef.parameters._inline) {
response.setHeader("Content-disposition", "attachment; filename=\"" + reportDef.name + "." + reportDef.fileFormat.extension + "\"");
response.contentType = reportDef.fileFormat.mimeTyp
response.characterEncoding = "UTF-8"
response.outputStream << reportDef.contentStream.toByteArray()
} else {
render(text: reportDef.contentStream, contentType: reportDef.fileFormat.mimeTyp, encoding: reportDef.parameters.encoding ? reportDef.parameters.encoding : 'UTF-8');
}
}
Have you looked at the Jasper Plugin? It seems to have the tools already built for you. As far as asking the user for a download location the browser has some controller over how files are received from a web page. Is your real issue that you want control over the download location?
[UPDATE]
Using the location 'c:\' is on your server not the client and this is why it is not downloading.
try something like this...
def controllerMethod = {
def temp_file = File.createTempFile("jasperReport",".pdf") //<-- you don't have to use a temp file but don't forget to delete them off the server at some point.
JasperExportManager.exportReportToPdfFile(jasperPrint, temp_file.absolutePath));
response.setContentType("application/pdf") //<-- you'll have to handle this dynamically at some point
response.setHeader("Content-disposition", "attachment;filename=${temp_file.getName()}")
response.outputStream << temp_file.newInputStream() //<-- binary stream copy to client
}
I have not tested this and there are better ways of handling the files and streams but i think you'll get the general idea.

Resources