Allow request method restriction on domain (#174)
* add limit_except generator to domain * revert package-lock.json * change from limit param to if statement in restrict methods * add response code input * fix scsspull/184/head
							parent
							
								
									5daf54ebde
								
							
						
					
					
						commit
						d9d9a1a92a
					
				| 
						 | 
				
			
			@ -166,6 +166,18 @@ export default (domain, domains, global) => {
 | 
			
		|||
        serverConfig.push(...securityConf(domains, global));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Restrict Methods
 | 
			
		||||
    if (Object.keys(domain.restrict).find(k => domain.restrict[k].computed && k !== 'responseCode')) {
 | 
			
		||||
        const allowedKeys = Object.keys(domain.restrict)
 | 
			
		||||
                                .filter(k => !domain.restrict[k].computed && k !== 'responseCode')
 | 
			
		||||
                                .map(e => e.replace('Method', '').toUpperCase());
 | 
			
		||||
 | 
			
		||||
        serverConfig.push(['# restrict methods', '']);
 | 
			
		||||
        serverConfig.push([`if ($request_method !~ ^(${allowedKeys.join('|')})$)`, {
 | 
			
		||||
            'return': `'${domain.restrict.responseCode.computed}'`,
 | 
			
		||||
        }]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Access log or error log for domain
 | 
			
		||||
    if (domain.logging.accessLog.computed || domain.logging.errorLog.computed) {
 | 
			
		||||
        serverConfig.push(['# logging', '']);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,4 +44,5 @@ export default {
 | 
			
		|||
	logging: 'Logging',
 | 
			
		||||
    reverseProxy: 'Reverse proxy',
 | 
			
		||||
    reverseProxyLower: 'reverse proxy',
 | 
			
		||||
    restrict: 'Restrict',
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,5 +32,6 @@ import python from './python';
 | 
			
		|||
import reverseProxy from './reverse_proxy';
 | 
			
		||||
import routing from './routing';
 | 
			
		||||
import server from './server';
 | 
			
		||||
import restrict from './restrict';
 | 
			
		||||
 | 
			
		||||
export default { https, logging, php, presets, python, reverseProxy, routing, server };
 | 
			
		||||
export default { https, logging, php, presets, python, reverseProxy, routing, server, restrict };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2020 DigitalOcean
 | 
			
		||||
 | 
			
		||||
This code is licensed under the MIT License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions :
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    disableForThisDomain: 'disable for this domain',
 | 
			
		||||
    responseCode: 'Response code',
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +128,10 @@ THE SOFTWARE.
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    input.is-danger {
 | 
			
		||||
      border-color: $danger;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,3 +31,4 @@ export { default as Python } from './python';
 | 
			
		|||
export { default as ReverseProxy } from './reverse_proxy';
 | 
			
		||||
export { default as Routing } from './routing';
 | 
			
		||||
export { default as Logging } from './logging';
 | 
			
		||||
export { default as Restrict } from './restrict';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,294 @@
 | 
			
		|||
<!--
 | 
			
		||||
Copyright 2020 DigitalOcean
 | 
			
		||||
 | 
			
		||||
This code is licensed under the MIT License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions :
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <div class="columns">
 | 
			
		||||
            <div class="column">
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">GET</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${getMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="getMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">POST</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${postMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="postMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">PUT</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${putMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="putMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">PATCH</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${patchMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="patchMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">DELETE</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${deleteMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="deleteMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="column">
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">HEAD</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${headMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="headMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">CONNECT</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${connectMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="connectMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">OPTIONS</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${optionsMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="optionsMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="field is-horizontal">
 | 
			
		||||
                    <div class="field-label">
 | 
			
		||||
                        <label class="label">TRACE</label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="field-body">
 | 
			
		||||
                        <div class="field">
 | 
			
		||||
                            <div :class="`control${traceMethodChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                                <div class="checkbox">
 | 
			
		||||
                                    <PrettyCheck v-model="traceMethod" class="p-default p-curve p-fill p-icon">
 | 
			
		||||
                                        <i slot="extra" class="icon fas fa-check"></i>
 | 
			
		||||
                                        {{ i18n.templates.domainSections.restrict.disableForThisDomain }}
 | 
			
		||||
                                    </PrettyCheck>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-if="hasAtLeastOneEnabled" class="field is-horizontal">
 | 
			
		||||
            <div class="field-label">
 | 
			
		||||
                <label class="label">{{ i18n.templates.domainSections.restrict.responseCode }}</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="field-body">
 | 
			
		||||
                <div class="field">
 | 
			
		||||
                    <div :class="`control${responseCodeChanged ? ' is-changed' : ''}`">
 | 
			
		||||
                        <input v-model.number="responseCode"
 | 
			
		||||
                               :class="['input', validResponseCode ? '' : 'is-danger']"
 | 
			
		||||
                               type="number"
 | 
			
		||||
                               min="100"
 | 
			
		||||
                               step="1"
 | 
			
		||||
                               :placeholder="$props.data.responseCode.default"
 | 
			
		||||
                        />
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
    import PrettyCheck from 'pretty-checkbox-vue/check';
 | 
			
		||||
    import i18n from '../../i18n';
 | 
			
		||||
    import delegatedFromDefaults from '../../util/delegated_from_defaults';
 | 
			
		||||
    import computedFromDefaults from '../../util/computed_from_defaults';
 | 
			
		||||
 | 
			
		||||
    const defaults = {
 | 
			
		||||
        getMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        postMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        putMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        patchMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        deleteMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        headMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        connectMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        optionsMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        traceMethod: {
 | 
			
		||||
            default: false,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
        responseCode: {
 | 
			
		||||
            default: 405,
 | 
			
		||||
            enabled: true,
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    export default {
 | 
			
		||||
        name: 'DomainRestrict',                                  // Component name
 | 
			
		||||
        display: i18n.common.restrict,                           // Display name for tab
 | 
			
		||||
        key: 'restrict',                                         // Key for data in parent
 | 
			
		||||
        delegated: delegatedFromDefaults(defaults),             // Data the parent will present here
 | 
			
		||||
        components: {
 | 
			
		||||
            PrettyCheck,
 | 
			
		||||
        },
 | 
			
		||||
        props: {
 | 
			
		||||
            data: Object,                                       // Data delegated back to us from parent
 | 
			
		||||
        },
 | 
			
		||||
        data () {
 | 
			
		||||
            return {
 | 
			
		||||
                i18n,
 | 
			
		||||
                validResponseCode: true,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        computed: {
 | 
			
		||||
            ...computedFromDefaults(defaults, 'restrict'),    // Getters & setters for the delegated data
 | 
			
		||||
            hasAtLeastOneEnabled() {
 | 
			
		||||
                return (Object.keys(this.$props.data).filter(k => this.$props.data[k].computed && k !== 'responseCode')).length > 0;
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
        watch: {
 | 
			
		||||
            '$props.data.responseCode': {
 | 
			
		||||
                handler(data) {
 | 
			
		||||
                    if (data.computed && /^[1-5][0-9][0-9]$/.test(data.computed)) {
 | 
			
		||||
                        this.validResponseCode = true;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.validResponseCode = false;
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                deep: true,
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
</script>
 | 
			
		||||
		Loading…
	
		Reference in New Issue