Skip to content

Commit

Permalink
Merge pull request #1161 from juhijariwala/secure-environment-var-at-…
Browse files Browse the repository at this point in the history
…stage-and-job-level

Allow secure variables (environment) on stages, jobs and environments level (#194).
  • Loading branch information
jyotisingh committed Jun 10, 2015
2 parents 07d6566 + 6b06a89 commit e87f2f4
Show file tree
Hide file tree
Showing 27 changed files with 566 additions and 285 deletions.
Expand Up @@ -204,6 +204,14 @@ public EnvironmentVariablesConfig getVariables() {
return variables;
}

public EnvironmentVariablesConfig getPlainTextVariables() {
return variables.getPlainTextVariables();
}

public EnvironmentVariablesConfig getSecureVariables() {
return variables.getSecureVariables();
}

public void setConfigAttributes(Object attributes) {
if (attributes == null) {
return;
Expand Down
Expand Up @@ -213,6 +213,10 @@ public boolean isSecure() {
return isSecure;
}

public boolean isPlain() {
return !isSecure();
}

private void setValue(String value){
if (isSecure) {
encryptedValue = new EncryptedVariableValueConfig(encrypt(value));
Expand Down
Expand Up @@ -121,24 +121,25 @@ public void setConfigAttributes(Object attributes) {
}
}

public List<EnvironmentVariableConfig> getSecureVariables() {
ArrayList<EnvironmentVariableConfig> secureVariables = new ArrayList<EnvironmentVariableConfig>();
public EnvironmentVariablesConfig getSecureVariables() {
EnvironmentVariablesConfig result = new EnvironmentVariablesConfig();
for (EnvironmentVariableConfig environmentVariableConfig : this) {
if (environmentVariableConfig.isSecure()) {
secureVariables.add(environmentVariableConfig);
result.add(environmentVariableConfig);
}
}
return secureVariables;
return result;
}

public List<EnvironmentVariableConfig> getPlainTextVariables() {
ArrayList<EnvironmentVariableConfig> plainTextVariables = new ArrayList<EnvironmentVariableConfig>();
public EnvironmentVariablesConfig getPlainTextVariables() {
EnvironmentVariablesConfig result = new EnvironmentVariablesConfig();
for (EnvironmentVariableConfig environmentVariableConfig : this) {
if (!environmentVariableConfig.isSecure()) {
plainTextVariables.add(environmentVariableConfig);
if (environmentVariableConfig.isPlain()) {
result.add(environmentVariableConfig);
}
}
return plainTextVariables;
return result;

}

}
Expand Up @@ -240,6 +240,14 @@ public EnvironmentVariablesConfig getVariables() {
return variables;
}

public EnvironmentVariablesConfig getPlainTextVariables() {
return variables.getPlainTextVariables();
}

public EnvironmentVariablesConfig getSecureVariables() {
return variables.getSecureVariables();
}

public boolean hasVariable(String variableName) {
return variables.hasVariable(variableName);
}
Expand Down
Expand Up @@ -478,16 +478,10 @@ public EnvironmentVariablesConfig getVariables() {
}

public EnvironmentVariablesConfig getPlainTextVariables() {
EnvironmentVariablesConfig result = new EnvironmentVariablesConfig();
for (EnvironmentVariableConfig variable : variables) {
if (!variable.isSecure()) {
result.add(variable);
}
}
return result;
return variables.getPlainTextVariables();
}

public List<EnvironmentVariableConfig> getSecureVariables() {
public EnvironmentVariablesConfig getSecureVariables() {
return variables.getSecureVariables();
}

Expand Down
Expand Up @@ -199,6 +199,14 @@ public EnvironmentVariablesConfig getVariables() {
return variables;
}

public EnvironmentVariablesConfig getPlainTextVariables() {
return variables.getPlainTextVariables();
}

public EnvironmentVariablesConfig getSecureVariables() {
return variables.getSecureVariables();
}

public boolean hasVariableInScope(String variableName) {
if (variables.hasVariable(variableName)) {
return true;
Expand Down
@@ -0,0 +1,81 @@
/*
* Copyright 2015 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var EnvironmentVariableEdit = function (options) {
var $ = jQuery;

var defaultOptions = {
tableRowSelector: '.environment-variable-edit-row',
editSelector: '.edit',
resetSelector: '.reset',
passwordFieldSelector: '[type=password]',
changedFieldSelector: '.is-changed-field',
originalValueSelector: '.original-secure-variable-value',
parentRowSelector: 'tr'
};

options = $.extend({}, defaultOptions, options);

var editSelector = options.tableRowSelector + ' ' + options.editSelector;
var resetSelector = options.tableRowSelector + ' ' + options.resetSelector;

var existingHooks = $.data(document.body, 'reset-environment-variable-on');

if (!existingHooks) {
existingHooks = $.data(document.body, 'reset-environment-variable-on', {});
}

if (existingHooks[editSelector] && existingHooks[resetSelector]) {
return;
} else {
existingHooks[editSelector] = true;
existingHooks[resetSelector] = true;
}

$(document).on('click.resetEnvironmentVariableOn', editSelector, function (evt) {
evt.preventDefault();
var element = $(evt.target),
environmentVariableRow = element.parents(options.parentRowSelector),
passwordField = environmentVariableRow.find(options.passwordFieldSelector),
editLink = environmentVariableRow.find(options.editSelector),
resetLink = environmentVariableRow.find(options.resetSelector),
isChangedHiddenField = environmentVariableRow.find(options.changedFieldSelector),
secureVariableOriginalValue = environmentVariableRow.find(options.originalValueSelector);

passwordField.removeAttr("readonly");
isChangedHiddenField.val(true);
passwordField.val('').focus();
editLink.toggle();
resetLink.toggle();
});

$(document).on('click.resetEnvironmentVariableOn', resetSelector, function (evt) {
evt.preventDefault();
var element = $(evt.target),
environmentVariableRow = element.parents(options.parentRowSelector),
passwordField = environmentVariableRow.find(options.passwordFieldSelector),
editLink = environmentVariableRow.find(options.editSelector),
resetLink = environmentVariableRow.find(options.resetSelector),
isChangedHiddenField = environmentVariableRow.find(options.changedFieldSelector),
secureVariableOriginalValue = environmentVariableRow.find(options.originalValueSelector);

passwordField.attr("readonly", "readonly");
isChangedHiddenField.val(false);
passwordField.val(secureVariableOriginalValue.val()).focus();
editLink.toggle();
resetLink.toggle();
})
};
Expand Up @@ -125,8 +125,7 @@ EnvironmentVariables = function() {
};

init.RowCreator.prototype.createRow = function(on_remove) {
var row = createWrapperElement(this.wrapper_element_type);
row.html(this.template_text);
var row = createWrapperElement(this.wrapper_element_type, this.template_text);
this.registerExistingRow(row, on_remove);
return row;
};
Expand All @@ -138,8 +137,12 @@ EnvironmentVariables = function() {
});
};

function createWrapperElement(tag_type) {
return jQuery(document.createElement(tag_type));
function createWrapperElement(tag_type, template_text) {
if(tag_type){
return jQuery(document.createElement(tag_type)).html(template_text);
} else {
return jQuery(template_text);
}
}

function mapAssoc(array, fn) {
Expand Down
Expand Up @@ -783,7 +783,7 @@ span.contextual_help.stage_approval{
}

a.add_item {
padding: 8px 5px 0 22px;
padding: 8px 5px 5px 22px;
background: image_url('g9/icon_add_16.png') no-repeat 1px 5px;
display: block;
width: 80px;
Expand Down
Expand Up @@ -569,3 +569,8 @@ input[type='submit'][disabled='disabled'].primary:hover{
}



button.edit-environment {
float: right;
margin-right: 5px;
}
34 changes: 17 additions & 17 deletions server/webapp/WEB-INF/rails.new/app/helpers/application_helper.rb
Expand Up @@ -53,10 +53,10 @@ def url_for_job(job)

def path_for_stage(stage_identifier)
stage_identifier = stage_identifier.getIdentifier() if stage_identifier.respond_to? :getIdentifier
stage_detail_tab_path :pipeline_name => stage_identifier.getPipelineName(),
:pipeline_counter => stage_identifier.getPipelineCounter(),
:stage_name => stage_identifier.getStageName(),
:stage_counter => stage_identifier.getStageCounter()
stage_detail_tab_path pipeline_name: stage_identifier.getPipelineName(),
pipeline_counter: stage_identifier.getPipelineCounter(),
stage_name: stage_identifier.getStageName(),
stage_counter: stage_identifier.getStageCounter()
end

def stage_identifier_for_locator(stage_locator_string)
Expand All @@ -70,10 +70,10 @@ def tab_for(name, options = {})
end

def tab_with_display_name(name, display_name, options={})
options.reverse_merge!(:link => :enabled, :class => "", :anchor_class => "", :url => name)
options.reverse_merge!(link: :enabled, class: "", anchor_class: "", url: name)
url = url_for_path(options[:url])
css_class = "current" if ((@current_tab_name == name) || url.match(/#{url_for}$/))
link_body = options[:link] != :enabled ? "<span>#{display_name}</span>" : link_to(display_name, url, :target => options[:target], :class => options[:anchor_class])
link_body = options[:link] != :enabled ? "<span>#{display_name}</span>" : link_to(display_name, url, target: options[:target], class: options[:anchor_class])
"<li id='cruise-header-tab-#{name.gsub(/\s+/, '-')}' class='#{css_class} #{options[:class]}'>\n" + link_body + "\n</li>"
end

Expand Down Expand Up @@ -110,7 +110,7 @@ def sanitize_for_dom_id(value)
def onclick_lambda(options)
on_click_lambda = ''
if options.has_key? :onclick_lambda
options.reverse_merge!(:id => random_dom_id)
options.reverse_merge!(id: random_dom_id)
on_click_lambda = "<script type='text/javascript'> Util.on_load(function() { Event.observe($('#{options[:id]}'), 'click', function(evt) { #{options.delete(:onclick_lambda)}(evt); }); }); </script>".html_safe
end
[on_click_lambda, options]
Expand All @@ -119,8 +119,8 @@ def onclick_lambda(options)
def submit_button name, options={}
# DESIGN TODO: this is used for action/submit buttons on environments, pipeline dashboard, etc. Probably not 100% complete to match the features above
options = HashWithIndifferentAccess.new(options)
options.reverse_merge!(:type => 'submit')
options.merge!(:disabled => 'disabled') unless system_environment.isServerActive()
options.reverse_merge!(type: 'submit')
options.merge!(disabled: 'disabled') unless system_environment.isServerActive()
options[:value] = name
lambda_text, options_without_onclick = onclick_lambda(options)
if( options[:type] == "image" )
Expand All @@ -146,7 +146,7 @@ def select_button name, options
if options[:text_color] == 'dark'
image_url = 'g9/button_select_icon_dark.png'
end
content_tag(:button, button_content(name, tag(:img, :src => image_path(image_url))), button_options(options), false)
content_tag(:button, button_content(name, tag(:img, src: image_path(image_url))), button_options(options), false)
end

def header_select_button name, options
Expand All @@ -159,7 +159,7 @@ def image_button name, options
options[:class] = add_class(options[:class], 'image')
options[:type] = "submit"
options[:title] = name
content_tag(:button, content_tag(:span, ' ', :title => name), button_options(options), false)
content_tag(:button, content_tag(:span, ' ', title: name), button_options(options), false)
end

def default_button name, options
Expand Down Expand Up @@ -226,7 +226,7 @@ def is_plugins_enabled?
end

def render_json(options={})
options = options.merge({:locals => {:scope => {}}}) unless options.has_key? :locals
options = options.merge({locals: {scope: {}}}) unless options.has_key? :locals
render(options).to_json
end

Expand Down Expand Up @@ -317,7 +317,7 @@ def remote_function_new(options)
"new Ajax.Updater(#{update}, "

url_options = options[:url]
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
url_options = url_options.merge(escape: false) if url_options.is_a?(Hash)
function << "'#{escape_javascript(url_for(url_options))}'"
function << ", #{javascript_options})"

Expand Down Expand Up @@ -377,9 +377,9 @@ def selections

def to_operation_result_json(localized_result, success_msg=localized_result.message(localizer))
if localized_result.isSuccessful()
{:success => success_msg}.to_json
{success: success_msg}.to_json
else
{:error => localized_result.message(localizer)}.to_json
{error: localized_result.message(localizer)}.to_json
end
end

Expand Down Expand Up @@ -440,7 +440,7 @@ def label_with_hint(form, name, text, hint, required)

def render_pluggable_template(task_view_model, options = {})
# The view is self here since this method will be called only from views.
options.merge!(:view => self)
options.merge!(view: self)
options.reject{|key, val| key.is_a?(String)}.map{|key, val| options[key.to_s] = val}
view_rendering_service.render(task_view_model, options)
end
Expand All @@ -463,7 +463,7 @@ def view_cache_key
end

def register_defaultable_list nested_name
"<input type=\"hidden\" name=\"default_as_empty_list[]\" value=\"#{nested_name}\"/>".html_safe
hidden_field_tag 'default_as_empty_list[]', nested_name, id: nil
end

def form_remote_tag_new(options = {})
Expand Down
@@ -1,20 +1,29 @@
<h3><%= l.string("ENVIRONMENT_VARIABLES") %></h3>
<span title="<%= l.string("TOOLTIP_ENVIRONMENT_VARIABLES") -%>" class="contextual_help has_go_tip_right">&nbsp;</span>
<%= form_for @job,
:as => :job,
:url => admin_job_update_path(:current_tab => "environment_variables"),
:html => {:method => :put,
:id => "job_edit_form",
:onsubmit => "return AjaxForm.jquery_ajax_submit(this, AjaxForm.ConfigFormEditHandler);",
:class => "popup_form"} do |f| %>
:as => :job,
:url => admin_job_update_path(:current_tab => "environment_variables"),
:html => {:method => :put,
:id => "job_edit_form",
:onsubmit => "return AjaxForm.jquery_ajax_submit(this, AjaxForm.ConfigFormEditHandler);",
:class => "popup_form"} do |f| %>
<%= render partial: 'admin/shared/environment_variable_fieldset', locals: {
form: f,
collection: @job.getPlainTextVariables(),
collection_name: :variables,
nested_name: 'job>variables'
} %>

<h3><%= l.string("SECURE_ENVIRONMENT_VARIABLES") %></h3>
<span title="<%= l.string("TOOLTIP_ENCRYPTED_ENVIRONMENT_VARIABLES") -%>" class="contextual_help has_go_tip_right">&nbsp;</span>
<div class="fieldset">
<%= md5_field %>
<%= register_defaultable_list("job>variables") %>
<%= render :partial => "admin/shared/name_value", :locals => {:scope => {:form => f, :collection => @job.getVariables(), :collection_name => :variables}} %>
<%= render :partial => "admin/shared/name_value", :locals => {:scope => {:form => f, :collection => @job.getSecureVariables(), :collection_name => :variables, :secure => true}} %>
<div class='clear'></div>
</div>
<%= render :partial => 'shared/convert_tool_tips.html', :locals => {:scope => {}} %>
<%= render :partial => "admin/shared/form_submit", :locals => {:scope => {:reset_url => admin_job_edit_path(:pipeline_name => @pipeline.name(), :stage_name => params[:stage_name], :job_name => params[:job_name], :current_tab => "environment_variables")}} %>
<% end %>
<script type="text/javascript">
AjaxForm.error_box_selector = '#form_parent';
AjaxForm.error_box_selector = '#form_parent';
</script>

0 comments on commit e87f2f4

Please sign in to comment.