getEnvironment() {
- return environment;
- }
-
-
- public Subject getSubject() {
- return subject;
- }
- public Explanation explain() {
- return new Explanation() {
-
- public void describe(PrintStream out) {
- out.println("No authorization attempted.");
- }
-
- public Code getCode() {
- return Code.GRANTED_NO_AUTHORIZATION_ATTEMPTED;
- }
- };
- }
-
+ @Override
+ protected boolean isAuthorized() {
+ return true;
+ }
- @Override
- public long evaluationDuration() {
- return 0;
- }});
- }
- }
- return decisions;
+ /**
+ * Factory method returning an instance implementing the {@link com.dtolabs.rundeck.core.authorization.Authorization}
+ * interface.
+ *
+ * @param framework Framework instance
+ * @param aclBasedir Directory where the ACLs reside.
+ *
+ * @return
+ */
+ public static Authorization create(final Framework framework, final File aclBasedir) {
+ return new NoAuthorization(framework, aclBasedir);
}
+
}
diff --git a/core/src/main/resources/com/dtolabs/launcher/setup/templates/apitoken.aclpolicy.template b/core/src/main/resources/com/dtolabs/launcher/setup/templates/apitoken.aclpolicy.template
index 79c3065762a..9a36b9c4b65 100644
--- a/core/src/main/resources/com/dtolabs/launcher/setup/templates/apitoken.aclpolicy.template
+++ b/core/src/main/resources/com/dtolabs/launcher/setup/templates/apitoken.aclpolicy.template
@@ -15,7 +15,7 @@ for:
adhoc:
- allow: [run,kill] # allow running/killing adhoc jobs
job:
- - allow: [read,update,delete,run,kill] # allow read/write/delete/run/kill of all jobs
+ - allow: [create,read,update,delete,run,kill] # allow create/read/write/delete/run/kill of all jobs
node:
- allow: [read,run] # allow read/run for all nodes
by:
diff --git a/plugins/build.gradle b/plugins/build.gradle
index badca42af8b..16e7cd8bdfd 100644
--- a/plugins/build.gradle
+++ b/plugins/build.gradle
@@ -1,6 +1,6 @@
allprojects{
- version = '1.4.0.1'
+ version = '1.4.0.2'
defaultTasks 'clean','build'
rundeckPluginVersion= '1.0'
}
diff --git a/rundeckapp/application.properties b/rundeckapp/application.properties
index 7ee57dd4b4e..dc617eddbff 100644
--- a/rundeckapp/application.properties
+++ b/rundeckapp/application.properties
@@ -2,8 +2,8 @@
#Wed Nov 02 09:45:44 PDT 2011
app.grails.version=1.3.7
app.name=rundeck
-app.version=1.4.0.1
-build.ident=1.4.0.1-1
+app.version=1.4.0.2
+build.ident=1.4.0.2-1
plugins.hibernate=1.3.7
plugins.jetty=1.2-SNAPSHOT
plugins.mail=0.9
diff --git a/rundeckapp/grails-app/conf/AuthorizationFilters.groovy b/rundeckapp/grails-app/conf/AuthorizationFilters.groovy
index 0f83b8871a3..d4590cced60 100644
--- a/rundeckapp/grails-app/conf/AuthorizationFilters.groovy
+++ b/rundeckapp/grails-app/conf/AuthorizationFilters.groovy
@@ -104,7 +104,7 @@ public class AuthorizationFilters {
/**
* Check the user has authorization for the actions.
*/
- authorizationCheck(controller: '*', action: '*') {
+ postLoginAuthorizationCheck(controller: '*', action: '*') {
before = {
if (request.invalidApiAuthentication ) {
diff --git a/rundeckapp/grails-app/conf/BootStrap.groovy b/rundeckapp/grails-app/conf/BootStrap.groovy
index 95f659baaf5..a4bb5d2c699 100644
--- a/rundeckapp/grails-app/conf/BootStrap.groovy
+++ b/rundeckapp/grails-app/conf/BootStrap.groovy
@@ -10,15 +10,25 @@ import org.apache.log4j.Level
import org.apache.log4j.net.SocketAppender
import grails.util.GrailsUtil
import com.dtolabs.launcher.Setup
-
+import org.codehaus.groovy.grails.plugins.web.filters.FilterConfig
+import org.codehaus.groovy.grails.plugins.web.filters.FilterToHandlerAdapter
class BootStrap {
def grailsApplication
def scheduledExecutionService
def executionService
+ def filterInterceptor
def init = { servletContext ->
+
+ filterInterceptor.handlers.sort { FilterToHandlerAdapter handler1,
+ FilterToHandlerAdapter handler2 ->
+ FilterConfig filter1 = handler1.filterConfig
+ FilterConfig filter2 = handler2.filterConfig
+ filter1.name <=> filter2.name
+ }
+
def String rdeckBase
if(!grailsApplication.config.rdeck.base){
//look for system property
diff --git a/rundeckapp/grails-app/conf/ProjectSelectFilters.groovy b/rundeckapp/grails-app/conf/ProjectSelectFilters.groovy
index 44db8bf3d4e..d89e094ad26 100644
--- a/rundeckapp/grails-app/conf/ProjectSelectFilters.groovy
+++ b/rundeckapp/grails-app/conf/ProjectSelectFilters.groovy
@@ -58,7 +58,7 @@ public class ProjectSelectFilters {
* on first user login, set the session.project if it is not set, to the last user project selected, or
* to the first project in the available list
*/
- projectSelection(controller: 'framework', action: '(createProject|selectProject|projectSelect|(create|save|check|edit|view)ResourceModelConfig)',invert:true) {
+ projectSelection(controller: 'framework', action: '(createProject|selectProject|projectSelect|noProjectAccess|(create|save|check|edit|view)ResourceModelConfig)',invert:true) {
before = {
if (request.api_version || request.is_api_req) {
//only default the project if not an api request
@@ -108,7 +108,11 @@ public class ProjectSelectFilters {
}
session.project = selected
if (!selected) {
- redirect(action: 'createProject', controller: 'framework')
+ if (!frameworkService.authorizeApplicationResourceTypeAll(fw, 'project', ['create'])) {
+ redirect(action: 'noProjectAccess', controller: 'framework')
+ }else{
+ redirect(action: 'createProject', controller: 'framework')
+ }
return false
}
}
diff --git a/rundeckapp/grails-app/controllers/FrameworkController.groovy b/rundeckapp/grails-app/controllers/FrameworkController.groovy
index f6af02ecbc4..e2315382c68 100644
--- a/rundeckapp/grails-app/controllers/FrameworkController.groovy
+++ b/rundeckapp/grails-app/controllers/FrameworkController.groovy
@@ -52,6 +52,14 @@ class FrameworkController {
response.setHeader(Constants.X_RUNDECK_ACTION_UNAUTHORIZED_HEADER, request.error)
render(template: fragment ? '/common/errorFragment' : '/common/error', model: [:])
}
+
+ def noProjectAccess = {
+ response.setStatus(403)
+ request.title = "Unauthorized"
+ request.error = "No authorized access to projects. Contact your administrator."
+ response.setHeader(Constants.X_RUNDECK_ACTION_UNAUTHORIZED_HEADER, request.error)
+ return render(template: '/common/error', model: [:])
+ }
/**
* This action returns a json object informing about whether the user is authorized
* to run scripts in the current project context.
diff --git a/rundeckapp/grails-app/controllers/MenuController.groovy b/rundeckapp/grails-app/controllers/MenuController.groovy
index 5f799bcddd7..d6785eaa5b5 100644
--- a/rundeckapp/grails-app/controllers/MenuController.groovy
+++ b/rundeckapp/grails-app/controllers/MenuController.groovy
@@ -248,14 +248,12 @@ class MenuController {
}
// Filter the groups by what the user is authorized to see.
- def authorization = frameworkService.getFrameworkFromUserSession(request.session, request).getAuthorizationMgr()
- def env = Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE +"project"), session.project))
- def decisions = authorization.evaluate(res, request.subject, new HashSet([AuthConstants.ACTION_READ,AuthConstants.ACTION_DELETE,AuthConstants.ACTION_RUN,AuthConstants.ACTION_UPDATE,AuthConstants.ACTION_KILL]), env)
+ def decisions = frameworkService.authorizeProjectResources(framework,res, new HashSet([AuthConstants.ACTION_READ, AuthConstants.ACTION_DELETE, AuthConstants.ACTION_RUN, AuthConstants.ACTION_UPDATE, AuthConstants.ACTION_KILL]),query.projFilter)
log.debug("listWorkflows(evaluate): "+(System.currentTimeMillis()-preeval));
long viewable=System.currentTimeMillis()
- def authCreate = frameworkService.authorizeProjectResource(framework, [type: 'resource', kind: 'job'], AuthConstants.ACTION_CREATE, session.project)
+ def authCreate = frameworkService.authorizeProjectResource(framework, [type: 'resource', kind: 'job'], AuthConstants.ACTION_CREATE, query.projFilter)
def Map jobauthorizations=[:]
diff --git a/rundeckapp/grails-app/controllers/ScheduledExecutionController.groovy b/rundeckapp/grails-app/controllers/ScheduledExecutionController.groovy
index 571d01f8e78..76a633c31c6 100644
--- a/rundeckapp/grails-app/controllers/ScheduledExecutionController.groovy
+++ b/rundeckapp/grails-app/controllers/ScheduledExecutionController.groovy
@@ -166,7 +166,7 @@ class ScheduledExecutionController {
response.setStatus (404)
return error.call()
}
- if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_READ], session.project)) {
+ if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_READ], scheduledExecution.project)) {
return unauthorized("Read Job ${params.id}")
}
crontab = scheduledExecution.timeAndDateAsBooleanMap()
@@ -654,7 +654,7 @@ class ScheduledExecutionController {
return redirect(action:index, params:params)
}
- if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_UPDATE, AuthConstants.ACTION_READ], session.project)) {
+ if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_UPDATE, AuthConstants.ACTION_READ], scheduledExecution.project)) {
return unauthorized("Update Job ${params.id}")
}
//clear session workflow
@@ -771,7 +771,7 @@ class ScheduledExecutionController {
boolean failed=false
def ScheduledExecution scheduledExecution = scheduledExecutionService.getByIDorUUID( params.id )
- if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_UPDATE], session.project)) {
+ if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_UPDATE], scheduledExecution.project)) {
return [success:false,scheduledExecution:scheduledExecution,message:"Update Job ${scheduledExecution.extid}",unauthorized:true]
}
@@ -1336,7 +1336,7 @@ class ScheduledExecutionController {
redirect(action:index)
return;
}
- if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_READ], session.project)) {
+ if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_READ], scheduledExecution.project)) {
return unauthorized("Read Job ${params.id}")
}
def newScheduledExecution = new ScheduledExecution()
@@ -2204,7 +2204,7 @@ class ScheduledExecutionController {
failed=result.failed
//try to save workflow
- if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_CREATE], session.project)) {
+ if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_CREATE], scheduledExecution.project)) {
scheduledExecution.discard()
return [success: false, error: "Unauthorized: Create Job ${scheduledExecution.generateFullName()}", unauthorized: true, scheduledExecution:scheduledExecution]
}
@@ -2986,7 +2986,7 @@ class ScheduledExecutionController {
return chain(controller: 'api', action: 'renderError')
}
Framework framework = frameworkService.getFrameworkFromUserSession(session, request)
- if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_READ], session.project)) {
+ if (!frameworkService.authorizeProjectJobAll(framework, scheduledExecution, [AuthConstants.ACTION_READ], scheduledExecution.project)) {
request.errorCode = "api.error.item.unauthorized"
request.errorArgs = ['Read','Job ID', params.id]
return new ApiController().renderError()
diff --git a/rundeckapp/grails-app/services/ExecutionService.groovy b/rundeckapp/grails-app/services/ExecutionService.groovy
index fd0c18022f5..11c2bf5cd3b 100644
--- a/rundeckapp/grails-app/services/ExecutionService.groovy
+++ b/rundeckapp/grails-app/services/ExecutionService.groovy
@@ -410,7 +410,7 @@ class ExecutionService implements ApplicationContextAware, CommandInterpreter{
}
- public logExecution(uri,project,user,issuccess,framework,execId,Date startDate=null, jobExecId=null, jobName=null, jobSummary=null,iscancelled=false, nodesummary=null, abortedby=null){
+ public logExecution(uri,project,user,issuccess,execId,Date startDate=null, jobExecId=null, jobName=null, jobSummary=null,iscancelled=false, nodesummary=null, abortedby=null){
def reportMap=[:]
def internalLog = org.apache.log4j.Logger.getLogger("ExecutionService")
@@ -444,7 +444,7 @@ class ExecutionService implements ApplicationContextAware, CommandInterpreter{
reportMap.author=user
reportMap.title= jobSummary?jobSummary:"RunDeck Job Execution"
reportMap.status= issuccess ? "succeed":iscancelled?"cancel":"fail"
- reportMap.node= null!=nodesummary?nodesummary: framework.getFrameworkNodeName()
+ reportMap.node= null!=nodesummary?nodesummary: frameworkService.getFrameworkNodeName()
reportMap.message=(issuccess?'Job completed successfully':iscancelled?('Job killed by: '+(abortedby?:user)):'Job failed')
reportMap.dateCompleted=new Date()
@@ -1287,8 +1287,7 @@ class ExecutionService implements ApplicationContextAware, CommandInterpreter{
failedCount=failed.size()
totalCount=matched.size()
}
- def Framework fw = frameworkService.getFramework()
- logExecution(null, execution.project, execution.user, "true" == execution.status, fw, exId,
+ logExecution(null, execution.project, execution.user, "true" == execution.status, exId,
execution.dateStarted, jobid, jobname, summarizeJob(scheduledExecution, execution), props.cancelled,
node, execution.abortedby)
notificationService.triggerJobNotification(props.status == 'true' ? 'success' : 'failure', schedId, [execution: execution,nodestatus:[succeeded:sucCount,failed:failedCount,total:totalCount]])
diff --git a/rundeckapp/grails-app/services/FrameworkService.groovy b/rundeckapp/grails-app/services/FrameworkService.groovy
index aad6ab45ee9..04536f94b62 100644
--- a/rundeckapp/grails-app/services/FrameworkService.groovy
+++ b/rundeckapp/grails-app/services/FrameworkService.groovy
@@ -98,8 +98,22 @@ class FrameworkService implements ApplicationContextAware {
return framework.getAuthorizationMgr().authorizeScript(user,project,script)
}
- def authorizeProjectResource(Framework framework, Map resource, String action, String project){
+ def Set authorizeProjectResources(Framework framework, Set resources, Set actions, String project) {
+ if (null == project) {
+ throw new IllegalArgumentException("null project")
+ }
+ def Set decisions = framework.getAuthorizationMgr().evaluate(
+ resources,
+ framework.getAuthenticationMgr().subject,
+ actions,
+ Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE + "project"), project)))
+ return decisions
+ }
+ def authorizeProjectResource(Framework framework, Map resource, String action, String project){
+ if (null == project) {
+ throw new IllegalArgumentException("null project")
+ }
def Decision decision=framework.getAuthorizationMgr().evaluate(
resource,
framework.getAuthenticationMgr().subject,
@@ -108,7 +122,9 @@ class FrameworkService implements ApplicationContextAware {
return decision.isAuthorized()
}
def authorizeProjectResourceAll(Framework framework, Map resource, Collection actions, String project){
-
+ if(null==project){
+ throw new IllegalArgumentException("null project")
+ }
def decisions=framework.getAuthorizationMgr().evaluate(
[resource] as Set,
framework.getAuthenticationMgr().subject,
@@ -117,7 +133,9 @@ class FrameworkService implements ApplicationContextAware {
return !(decisions.find {!it.authorized})
}
def authorizeProjectJobAll(Framework framework, ScheduledExecution job, Collection actions, String project){
-
+ if (null == project) {
+ throw new IllegalArgumentException("null project")
+ }
def decisions=framework.getAuthorizationMgr().evaluate(
[[type:'job',job:job.jobName,group:job.groupPath?:'']] as Set,
framework.getAuthenticationMgr().subject,
@@ -164,9 +182,15 @@ class FrameworkService implements ApplicationContextAware {
framework.getAuthenticationMgr().subject,
actions as Set,
Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE + "application"), 'rundeck')))
-
return !(decisions.find {!it.authorized})
}
+
+ def getFrameworkNodeName() {
+ def rdbase= getRundeckBase()
+ def Framework fw = Framework.getInstance(rdbase)
+ fw.setAuthorizationMgr(new DenyAuthorization(fw, new File(Constants.getFrameworkConfigDir(rdbase))))
+ return fw.getFrameworkNodeName()
+ }
def getFrameworkFromUserSession( session, request){
if (!initialized) {
initialize()
@@ -178,9 +202,6 @@ class FrameworkService implements ApplicationContextAware {
}
return session.Framework;
}
- def getFramework(){
- return getFrameworkForUserAndRoles(null,null)
- }
def getFrameworkForUserAndRoles(String user, List rolelist){
return getFrameworkForUserAndRoles(user, rolelist, getRundeckBase())
}
@@ -197,6 +218,9 @@ class FrameworkService implements ApplicationContextAware {
def author = new SingleUserAclsAuthorization(fw,new File(Constants.getFrameworkConfigDir(rundeckbase)), user, rolelist.toArray(new String[0]))
fw.setAuthenticationMgr(authen)
fw.setAuthorizationMgr(author)
+ }else{
+ System.err.println("getFrameworkForUserAndRoles: No user/subject authorization")
+ throw new RuntimeException("Cannot get framework without user, roles: ${user}, ${rolelist}")
}
fw.setAllowUserInput(false)
return fw
@@ -208,6 +232,9 @@ class FrameworkService implements ApplicationContextAware {
def author = new UserSubjectAuthorization(fw,new File(Constants.getFrameworkConfigDir(rundeckbase)), user, subject)
fw.setAuthenticationMgr(authen)
fw.setAuthorizationMgr(author)
+ } else {
+ System.err.println("getFrameworkForUserAndSubject: No user/subject authorization")
+ throw new RuntimeException("Cannot get framework without user, subject: ${user}, ${subject}")
}
fw.setAllowUserInput(false)
return fw
diff --git a/rundeckapp/grails-app/services/ScheduledExecutionService.groovy b/rundeckapp/grails-app/services/ScheduledExecutionService.groovy
index 641677105b5..7dd654afbda 100644
--- a/rundeckapp/grails-app/services/ScheduledExecutionService.groovy
+++ b/rundeckapp/grails-app/services/ScheduledExecutionService.groovy
@@ -237,9 +237,8 @@ class ScheduledExecutionService {
}
// Filter the groups by what the user is authorized to see.
- def env = Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE+"project"), project))
- def decisions = framework.getAuthorizationMgr().evaluate(res, framework.getAuthenticationMgr().subject,
- new HashSet([AuthConstants.ACTION_READ]), env)
+ def decisions = frameworkService.authorizeProjectResources(framework,res,
+ new HashSet([AuthConstants.ACTION_READ]),project)
decisions.each{
if(it.authorized){
@@ -378,20 +377,10 @@ class ScheduledExecutionService {
}
def userAuthorizedForJob(request,ScheduledExecution se, Framework framework){
- def resource = ["job": se.getJobName(), "group": se.getGroupPath() ?: "",type:'job']
- def environment = Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE+"project"),
- se.project))
- def Decision d = framework.getAuthorizationMgr().evaluate(resource, request.subject,
- AuthConstants.ACTION_READ, environment)
- return d.isAuthorized()
+ return frameworkService.authorizeProjectJobAll(framework,se,[AuthConstants.ACTION_READ],se.project)
}
def userAuthorizedForAdhoc(request,ScheduledExecution se, Framework framework){
- def resource = [type:'adhoc']
- def environment = Collections.singleton(new Attribute(URI.create(EnvironmentalContext.URI_BASE + "project"),
- se.project))
- def Decision d = framework.getAuthorizationMgr().evaluate(resource, request.subject,
- AuthConstants.ACTION_RUN, environment)
- return d.isAuthorized()
+ return frameworkService.authorizeProjectResource(framework,[type: 'adhoc'], AuthConstants.ACTION_RUN,se.project)
}
def scheduleJob(ScheduledExecution se, String oldJobName, String oldGroupName) {
diff --git a/rundeckapp/grails-app/views/common/_error.gsp b/rundeckapp/grails-app/views/common/_error.gsp
index 6b7651c552a..ad7c474b608 100644
--- a/rundeckapp/grails-app/views/common/_error.gsp
+++ b/rundeckapp/grails-app/views/common/_error.gsp
@@ -2,13 +2,13 @@
- Error
+ ${flash.title ?: title ?: 'Error'}
- ${flash.title?flash.title:'Error'}
+ ${flash.title?:title?:'Error'}
diff --git a/test/api/test-jobs-import-yaml.sh b/test/api/test-jobs-import-yaml.sh
index 80d0a5aadb7..122d10afad1 100755
--- a/test/api/test-jobs-import-yaml.sh
+++ b/test/api/test-jobs-import-yaml.sh
@@ -58,6 +58,7 @@ skipcount=$($XMLSTARLET sel -T -t -v "/result/skipped/@count" $DIR/curl.out)
if [ "1" != "$succount" ] ; then
errorMsg "Upload was not successful."
+ echo $($XMLSTARLET sel -T -t -m "/result/failed" -v "job/error" $DIR/curl.out)
exit 2
else
echo "OK"
diff --git a/version.properties b/version.properties
index e5142358152..ff55fb4b93a 100644
--- a/version.properties
+++ b/version.properties
@@ -1,3 +1,3 @@
-version.number=1.4.0.1
+version.number=1.4.0.2
version.release.number=1
version.tag=GA