forked from google/grr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
799 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
#!/usr/bin/env python | ||
"""Test flow copy UI.""" | ||
|
||
|
||
from grr.gui import gui_test_lib | ||
from grr.gui import runtests_test | ||
|
||
from grr.lib import aff4 | ||
from grr.lib import flags | ||
from grr.lib import flow | ||
from grr.lib import output_plugin | ||
from grr.lib import test_lib | ||
from grr.lib.flows.general import processes as flows_processes | ||
from grr.lib.output_plugins import email_plugin | ||
from grr.lib.rdfvalues import client as rdf_client | ||
|
||
|
||
class DummyOutputPlugin(output_plugin.OutputPlugin): | ||
"""Output plugin that does nothing.""" | ||
|
||
name = "dummy" | ||
description = "Dummy do do." | ||
args_type = flows_processes.ListProcessesArgs | ||
|
||
def ProcessResponses(self, responses): | ||
pass | ||
|
||
|
||
class TestFlowCopy(gui_test_lib.GRRSeleniumTest): | ||
|
||
def setUp(self): | ||
super(TestFlowCopy, self).setUp() | ||
|
||
# Prepare our fixture. | ||
with self.ACLChecksDisabled(): | ||
self.client_id = rdf_client.ClientURN("C.0000000000000001") | ||
test_lib.ClientFixture(self.client_id, self.token) | ||
self.RequestAndGrantClientApproval("C.0000000000000001") | ||
|
||
self.email_descriptor = output_plugin.OutputPluginDescriptor( | ||
plugin_name=email_plugin.EmailOutputPlugin.__name__, | ||
plugin_args=email_plugin.EmailOutputPluginArgs( | ||
email_address="test@localhost", emails_limit=42)) | ||
|
||
def testOriginalFlowArgsAreShownInCopyForm(self): | ||
args = flows_processes.ListProcessesArgs( | ||
filename_regex="test[a-z]*", fetch_binaries=True) | ||
flow.GRRFlow.StartFlow( | ||
flow_name=flows_processes.ListProcesses.__name__, | ||
args=args, | ||
client_id=self.client_id, | ||
output_plugins=[self.email_descriptor], | ||
token=self.token) | ||
|
||
# Navigate to client and select newly created flow. | ||
self.Open("/#c=C.0000000000000001") | ||
self.Click("css=a[grrtarget='client.flows']") | ||
self.Click("css=td:contains('ListProcesses')") | ||
|
||
# Open wizard and check if flow arguments are copied. | ||
self.Click("css=button[name=copy_flow]") | ||
|
||
self.WaitUntil(self.IsTextPresent, "Copy ListProcesses flow") | ||
|
||
self.WaitUntilEqual("test[a-z]*", self.GetValue, | ||
"css=label:contains('Filename Regex') ~ * input") | ||
|
||
self.WaitUntil(self.IsChecked, "css=label:contains('Fetch Binaries') " | ||
"~ * input[type=checkbox]") | ||
|
||
# Check that output plugin info is also copied. | ||
self.WaitUntilEqual("string:EmailOutputPlugin", self.GetValue, | ||
"css=label:contains('Plugin') ~ * select") | ||
self.WaitUntilEqual("test@localhost", self.GetValue, | ||
"css=label:contains('Email address') ~ * input") | ||
self.WaitUntilEqual("42", self.GetValue, | ||
"css=label:contains('Emails limit') ~ * input") | ||
|
||
def testCopyingFlowUpdatesFlowListAndSelectsNewFlow(self): | ||
args = flows_processes.ListProcessesArgs( | ||
filename_regex="test[a-z]*", fetch_binaries=True) | ||
flow.GRRFlow.StartFlow( | ||
flow_name=flows_processes.ListProcesses.__name__, | ||
args=args, | ||
client_id=self.client_id, | ||
token=self.token) | ||
|
||
# Navigate to client and select newly created flow. | ||
self.Open("/#c=C.0000000000000001") | ||
self.Click("css=a[grrtarget='client.flows']") | ||
self.Click("css=td:contains('ListProcesses')") | ||
|
||
# Check that there's only one ListProcesses flow. | ||
self.WaitUntilNot( | ||
self.IsElementPresent, | ||
"css=grr-client-flows-list tr:contains('ListProcesses'):nth(1)") | ||
|
||
# Open wizard and check if flow arguments are copied. | ||
self.Click("css=button[name=copy_flow]") | ||
self.Click("css=button:contains('Launch'):not([disabled])") | ||
|
||
# Check that flows list got updated and that the new flow is selected. | ||
self.WaitUntil( | ||
self.IsElementPresent, | ||
"css=grr-client-flows-list tr:contains('ListProcesses'):nth(1)") | ||
self.WaitUntil(self.IsElementPresent, "css=grr-client-flows-list " | ||
"tr:contains('ListProcesses'):nth(0).row-selected") | ||
|
||
def testAddingOutputPluginToCopiedFlowWorks(self): | ||
args = flows_processes.ListProcessesArgs( | ||
filename_regex="test[a-z]*", fetch_binaries=True) | ||
flow.GRRFlow.StartFlow( | ||
flow_name=flows_processes.ListProcesses.__name__, | ||
args=args, | ||
client_id=self.client_id, | ||
token=self.token) | ||
|
||
# Navigate to client and select newly created flow. | ||
self.Open("/#c=C.0000000000000001") | ||
self.Click("css=a[grrtarget='client.flows']") | ||
self.Click("css=td:contains('ListProcesses')") | ||
|
||
# Open wizard and check if flow arguments are copied. | ||
self.Click("css=button[name=copy_flow]") | ||
|
||
self.Click("css=label:contains('Output Plugins') ~ * button") | ||
self.WaitUntil(self.IsElementPresent, | ||
"css=label:contains('Plugin') ~ * select") | ||
|
||
def testUserChangesToCopiedFlowAreRespected(self): | ||
args = flows_processes.ListProcessesArgs( | ||
filename_regex="test[a-z]*", fetch_binaries=True) | ||
flow.GRRFlow.StartFlow( | ||
flow_name=flows_processes.ListProcesses.__name__, | ||
args=args, | ||
client_id=self.client_id, | ||
output_plugins=[self.email_descriptor], | ||
token=self.token) | ||
|
||
# Navigate to client and select newly created flow. | ||
self.Open("/#c=C.0000000000000001") | ||
self.Click("css=a[grrtarget='client.flows']") | ||
self.Click("css=td:contains('ListProcesses')") | ||
|
||
# Open wizard and change the arguments. | ||
self.Click("css=button[name=copy_flow]") | ||
|
||
self.Type("css=label:contains('Filename Regex') ~ * input", | ||
"somethingElse*") | ||
|
||
self.Click("css=label:contains('Fetch Binaries') ~ * input[type=checkbox]") | ||
|
||
# Change output plugin and add another one. | ||
self.Click("css=label:contains('Output Plugins') ~ * button") | ||
self.Select("css=grr-output-plugin-descriptor-form " | ||
"label:contains('Plugin') ~ * select:eq(0)", | ||
"DummyOutputPlugin") | ||
self.Type("css=grr-output-plugin-descriptor-form " | ||
"label:contains('Filename Regex'):eq(0) ~ * input:text", | ||
"foobar!") | ||
|
||
self.Click("css=button:contains('Launch')") | ||
|
||
# Check that flows list got updated and that the new flow is selected. | ||
self.WaitUntil( | ||
self.IsElementPresent, | ||
"css=grr-client-flows-list tr:contains('ListProcesses'):nth(1)") | ||
self.WaitUntil(self.IsElementPresent, "css=grr-client-flows-list " | ||
"tr:contains('ListProcesses'):nth(0).row-selected") | ||
|
||
# Now open the last flow and check that it has the changes we made. | ||
with self.ACLChecksDisabled(): | ||
fd = aff4.FACTORY.Open(self.client_id.Add("flows"), token=self.token) | ||
flows = sorted(fd.ListChildren(), key=lambda x: x.age) | ||
fobj = aff4.FACTORY.Open(flows[-1], token=self.token) | ||
|
||
self.assertEqual(fobj.args, | ||
flows_processes.ListProcessesArgs( | ||
filename_regex="somethingElse*",)) | ||
self.assertListEqual( | ||
list(fobj.runner_args.output_plugins), [ | ||
output_plugin.OutputPluginDescriptor( | ||
plugin_name=DummyOutputPlugin.__name__, | ||
plugin_args=flows_processes.ListProcessesArgs( | ||
filename_regex="foobar!")), self.email_descriptor | ||
]) | ||
|
||
|
||
def main(argv): | ||
# Run the full test suite | ||
runtests_test.SeleniumTestProgram(argv=argv) | ||
|
||
|
||
if __name__ == "__main__": | ||
flags.StartMain(main) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
grr/gui/static/angular-components/flow/copy-flow-form-directive.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
'use strict'; | ||
|
||
goog.provide('grrUi.flow.copyFlowFormDirective.CopyFlowFormController'); | ||
goog.provide('grrUi.flow.copyFlowFormDirective.CopyFlowFormDirective'); | ||
goog.require('grrUi.core.apiService.stripTypeInfo'); | ||
|
||
goog.scope(function() { | ||
|
||
var stripTypeInfo = grrUi.core.apiService.stripTypeInfo; | ||
|
||
/** | ||
* Controller for CopyFlowFormDirective. | ||
* | ||
* @param {!angular.Scope} $scope | ||
* @param {!grrUi.core.apiService.ApiService} grrApiService | ||
* @constructor | ||
* @ngInject | ||
*/ | ||
grrUi.flow.copyFlowFormDirective.CopyFlowFormController = | ||
function($scope, grrApiService) { | ||
/** @private {!angular.Scope} */ | ||
this.scope_ = $scope; | ||
|
||
/** @private {!grrUi.core.apiService.ApiService} */ | ||
this.grrApiService_ = grrApiService; | ||
|
||
/** @private {Object} */ | ||
this.flow; | ||
|
||
this.scope_.$watchGroup(['flowId', 'clientId'], | ||
this.onFlowIdClientIdChange_.bind(this)); | ||
}; | ||
|
||
var CopyFlowFormController = | ||
grrUi.flow.copyFlowFormDirective.CopyFlowFormController; | ||
|
||
|
||
/** | ||
* Handles flowId/clientId attribute changes. | ||
* | ||
* @param {Array<string>} newValues | ||
* @private | ||
*/ | ||
CopyFlowFormController.prototype.onFlowIdClientIdChange_ = function( | ||
newValues) { | ||
if (newValues.every(angular.isDefined)) { | ||
var flowUrl = ['clients', | ||
this.scope_['clientId'], | ||
'flows', | ||
this.scope_['flowId']].join('/'); | ||
this.grrApiService_.get(flowUrl).then(function(response) { | ||
this.flow = response['data']; | ||
}.bind(this)); | ||
} | ||
}; | ||
|
||
|
||
/** | ||
* Handles clicks on dialog's 'proceed' button. | ||
* | ||
* @return {!angular.$q.Promise} Promise that resolves to a message that's | ||
* displayed in a dialog. | ||
* @export | ||
*/ | ||
CopyFlowFormController.prototype.proceed = function() { | ||
var strippedFlow = stripTypeInfo(this.flow); | ||
|
||
return this.grrApiService_.post('clients/' + this.scope_['clientId'] + '/flows', { | ||
flow: { | ||
runner_args: strippedFlow['runner_args'], | ||
args: strippedFlow['args'] | ||
} | ||
|
||
}).then(function success(response) { | ||
this.scope_['onResolve']({'flow': response['data']}); | ||
return 'Flow was successfully launched!'; | ||
|
||
}.bind(this), function failure(response) { | ||
var e = response['data']['message'] || 'Unknown error'; | ||
this.scope_['onReject']({'error': e}); | ||
return e; | ||
|
||
}.bind(this)); | ||
}; | ||
|
||
/** | ||
* Displays a "start flow" form with fills prefilled from the existing flow. | ||
* | ||
* @return {angular.Directive} Directive definition object. | ||
*/ | ||
grrUi.flow.copyFlowFormDirective.CopyFlowFormDirective = function() { | ||
return { | ||
scope: { | ||
flowId: '=', | ||
clientId: '=', | ||
onResolve: '&', | ||
onReject: '&' | ||
}, | ||
restrict: 'E', | ||
templateUrl: '/static/angular-components/flow/copy-flow-form.html', | ||
controller: CopyFlowFormController, | ||
controllerAs: 'controller' | ||
}; | ||
}; | ||
|
||
|
||
/** | ||
* Directive's name in Angular. | ||
* | ||
* @const | ||
* @export | ||
*/ | ||
grrUi.flow.copyFlowFormDirective.CopyFlowFormDirective.directive_name = | ||
'grrCopyFlowForm'; | ||
|
||
}); // goog.scope |
Oops, something went wrong.