diff --git a/admin/code/CampaignAdmin.php b/admin/code/CampaignAdmin.php index 997e74395..988eb9d39 100644 --- a/admin/code/CampaignAdmin.php +++ b/admin/code/CampaignAdmin.php @@ -27,9 +27,9 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider { private static $url_handlers = [ 'GET sets' => 'readCampaigns', + 'POST set/$ID/publish' => 'publishCampaign', 'POST set/$ID' => 'createCampaign', 'GET set/$ID/$Name' => 'readCampaign', - 'PUT set/$ID/publish' => 'publishCampaign', 'PUT set/$ID' => 'updateCampaign', 'DELETE set/$ID' => 'deleteCampaign', ]; @@ -66,7 +66,7 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider { 'itemListViewEndpoint' => $this->Link('set/:id/show'), 'publishEndpoint' => [ 'url' => $this->Link('set/:id/publish'), - 'method' => 'put' + 'method' => 'post' ] ]); } @@ -431,6 +431,12 @@ JSON; * @return SS_HTTPResponse */ public function publishCampaign(SS_HTTPRequest $request) { + // Protect against CSRF on destructive action + if(!SecurityToken::inst()->checkRequest($request)) { + return (new SS_HTTPResponse(json_encode(['status' => 'error']), 400)) + ->addHeader('Content-Type', 'application/json'); + } + $id = $request->param('ID'); if(!$id || !is_numeric($id)) { return (new SS_HTTPResponse(json_encode(['status' => 'error']), 400)) diff --git a/admin/code/LeftAndMain.php b/admin/code/LeftAndMain.php index b0bfd24f9..b4a12c6ae 100644 --- a/admin/code/LeftAndMain.php +++ b/admin/code/LeftAndMain.php @@ -194,6 +194,10 @@ class LeftAndMain extends Controller implements PermissionProvider { $combinedClientConfig['sections'][$className] = Injector::inst()->get($className)->getClientConfig(); } + // Get "global" CSRF token for use in JavaScript + $token = new SecurityToken(); + $combinedClientConfig[$token->getName()] = $token->getValue(); + return Convert::raw2json($combinedClientConfig); } diff --git a/admin/javascript/src/sections/campaign-admin/controller.js b/admin/javascript/src/sections/campaign-admin/controller.js index 37af80cf2..4df537b26 100644 --- a/admin/javascript/src/sections/campaign-admin/controller.js +++ b/admin/javascript/src/sections/campaign-admin/controller.js @@ -18,8 +18,9 @@ class CampaignAdminContainer extends SilverStripeComponent { this.addCampaign = this.addCampaign.bind(this); this.createFn = this.createFn.bind(this); this.publishApi = backend.createEndpointFetcher({ - url: this.props.config.publishEndpoint.url, - method: this.props.config.publishEndpoint.method, + url: this.props.sectionConfig.publishEndpoint.url, + method: this.props.sectionConfig.publishEndpoint.method, + defaultData: { SecurityID: this.props.config.SecurityID }, payloadSchema: { id: { urlReplacement: ':id', remove: true }, }, @@ -27,7 +28,7 @@ class CampaignAdminContainer extends SilverStripeComponent { } componentDidMount() { - window.ss.router(`/${this.props.config.campaignViewRoute}`, (ctx) => { + window.ss.router(`/${this.props.sectionConfig.campaignViewRoute}`, (ctx) => { this.props.actions.showCampaignView(ctx.params.id, ctx.params.view); }); } @@ -55,7 +56,7 @@ class CampaignAdminContainer extends SilverStripeComponent { * @return object */ renderIndexView() { - const schemaUrl = this.props.config.forms.editForm.schemaUrl; + const schemaUrl = this.props.sectionConfig.forms.editForm.schemaUrl; return (
@@ -80,7 +81,7 @@ class CampaignAdminContainer extends SilverStripeComponent { renderItemListView() { const props = { campaignId: this.props.campaignId, - itemListViewEndpoint: this.props.config.itemListViewEndpoint, + itemListViewEndpoint: this.props.sectionConfig.itemListViewEndpoint, publishApi: this.publishApi, }; @@ -105,7 +106,7 @@ class CampaignAdminContainer extends SilverStripeComponent { * @return object - Instanciated React component */ createFn(Component, props) { - const campaignViewRoute = this.props.config.campaignViewRoute; + const campaignViewRoute = this.props.sectionConfig.campaignViewRoute; if (props.component === 'GridField') { const extendedProps = Object.assign({}, props, { @@ -149,19 +150,23 @@ class CampaignAdminContainer extends SilverStripeComponent { } CampaignAdminContainer.propTypes = { - config: React.PropTypes.shape({ + sectionConfig: React.PropTypes.shape({ forms: React.PropTypes.shape({ editForm: React.PropTypes.shape({ schemaUrl: React.PropTypes.string, }), }), }), + config: React.PropTypes.shape({ + SecurityID: React.PropTypes.string, + }), sectionConfigKey: React.PropTypes.string.isRequired, }; function mapStateToProps(state, ownProps) { return { - config: state.config.sections[ownProps.sectionConfigKey], + config: state.config, + sectionConfig: state.config.sections[ownProps.sectionConfigKey], campaignId: state.campaign.campaignId, view: state.campaign.view, };