2719 lines
226 KiB
HTML
2719 lines
226 KiB
HTML
|
<!DOCTYPE html><html lang="fr">
|
|||
|
<head><meta charset="utf-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>Les ACL (Access Control Lists) sous Linux - YannStatic</title>
|
|||
|
|
|||
|
<meta name="description" content="Les droits standards et les droits étendus sont des fonctionnalités intéressantes mais qui ne s’applique que pour un seul utilisateur ou un seul groupe. Comm...">
|
|||
|
<link rel="canonical" href="https://static.rnmkcy.eu/2020/11/12/Les-ACL-(Access-Control-Lists)-sous-Linux.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
|
|||
|
|
|||
|
<!-- - include head/favicon.html - -->
|
|||
|
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
|
|||
|
<!-- end custom head snippets --><script>(function() {
|
|||
|
window.isArray = function(val) {
|
|||
|
return Object.prototype.toString.call(val) === '[object Array]';
|
|||
|
};
|
|||
|
window.isString = function(val) {
|
|||
|
return typeof val === 'string';
|
|||
|
};
|
|||
|
|
|||
|
window.hasEvent = function(event) {
|
|||
|
return 'on'.concat(event) in window.document;
|
|||
|
};
|
|||
|
|
|||
|
window.isOverallScroller = function(node) {
|
|||
|
return node === document.documentElement || node === document.body || node === window;
|
|||
|
};
|
|||
|
|
|||
|
window.isFormElement = function(node) {
|
|||
|
var tagName = node.tagName;
|
|||
|
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
|
|||
|
};
|
|||
|
|
|||
|
window.pageLoad = (function () {
|
|||
|
var loaded = false, cbs = [];
|
|||
|
window.addEventListener('load', function () {
|
|||
|
var i;
|
|||
|
loaded = true;
|
|||
|
if (cbs.length > 0) {
|
|||
|
for (i = 0; i < cbs.length; i++) {
|
|||
|
cbs[i]();
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
return {
|
|||
|
then: function(cb) {
|
|||
|
cb && (loaded ? cb() : (cbs.push(cb)));
|
|||
|
}
|
|||
|
};
|
|||
|
})();
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
window.throttle = function(func, wait) {
|
|||
|
var args, result, thisArg, timeoutId, lastCalled = 0;
|
|||
|
|
|||
|
function trailingCall() {
|
|||
|
lastCalled = new Date;
|
|||
|
timeoutId = null;
|
|||
|
result = func.apply(thisArg, args);
|
|||
|
}
|
|||
|
return function() {
|
|||
|
var now = new Date,
|
|||
|
remaining = wait - (now - lastCalled);
|
|||
|
|
|||
|
args = arguments;
|
|||
|
thisArg = this;
|
|||
|
|
|||
|
if (remaining <= 0) {
|
|||
|
clearTimeout(timeoutId);
|
|||
|
timeoutId = null;
|
|||
|
lastCalled = now;
|
|||
|
result = func.apply(thisArg, args);
|
|||
|
} else if (!timeoutId) {
|
|||
|
timeoutId = setTimeout(trailingCall, remaining);
|
|||
|
}
|
|||
|
return result;
|
|||
|
};
|
|||
|
};
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var Set = (function() {
|
|||
|
var add = function(item) {
|
|||
|
var i, data = this._data;
|
|||
|
for (i = 0; i < data.length; i++) {
|
|||
|
if (data[i] === item) {
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
this.size ++;
|
|||
|
data.push(item);
|
|||
|
return data;
|
|||
|
};
|
|||
|
|
|||
|
var Set = function(data) {
|
|||
|
this.size = 0;
|
|||
|
this._data = [];
|
|||
|
var i;
|
|||
|
if (data.length > 0) {
|
|||
|
for (i = 0; i < data.length; i++) {
|
|||
|
add.call(this, data[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
Set.prototype.add = add;
|
|||
|
Set.prototype.get = function(index) { return this._data[index]; };
|
|||
|
Set.prototype.has = function(item) {
|
|||
|
var i, data = this._data;
|
|||
|
for (i = 0; i < data.length; i++) {
|
|||
|
if (this.get(i) === item) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
};
|
|||
|
Set.prototype.is = function(map) {
|
|||
|
if (map._data.length !== this._data.length) { return false; }
|
|||
|
var i, j, flag, tData = this._data, mData = map._data;
|
|||
|
for (i = 0; i < tData.length; i++) {
|
|||
|
for (flag = false, j = 0; j < mData.length; j++) {
|
|||
|
if (tData[i] === mData[j]) {
|
|||
|
flag = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!flag) { return false; }
|
|||
|
}
|
|||
|
return true;
|
|||
|
};
|
|||
|
Set.prototype.values = function() {
|
|||
|
return this._data;
|
|||
|
};
|
|||
|
return Set;
|
|||
|
})();
|
|||
|
|
|||
|
window.Lazyload = (function(doc) {
|
|||
|
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
|
|||
|
var createNode = function(name, attrs) {
|
|||
|
var node = doc.createElement(name), attr;
|
|||
|
for (attr in attrs) {
|
|||
|
if (attrs.hasOwnProperty(attr)) {
|
|||
|
node.setAttribute(attr, attrs[attr]);
|
|||
|
}
|
|||
|
}
|
|||
|
return node;
|
|||
|
};
|
|||
|
var end = function(type, url) {
|
|||
|
var s, q, qi, cbs, i, j, cur, val, flag;
|
|||
|
if (type === 'js' || type ==='css') {
|
|||
|
s = sources[type], q = queue[type];
|
|||
|
s[url] = true;
|
|||
|
for (i = 0; i < q.length; i++) {
|
|||
|
cur = q[i];
|
|||
|
if (cur.urls.has(url)) {
|
|||
|
qi = cur, val = qi.urls.values();
|
|||
|
qi && (cbs = qi.callbacks);
|
|||
|
for (flag = true, j = 0; j < val.length; j++) {
|
|||
|
cur = val[j];
|
|||
|
if (!s[cur]) {
|
|||
|
flag = false;
|
|||
|
}
|
|||
|
}
|
|||
|
if (flag && cbs && cbs.length > 0) {
|
|||
|
for (j = 0; j < cbs.length; j++) {
|
|||
|
cbs[j].call(context);
|
|||
|
}
|
|||
|
qi.load = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
var load = function(type, urls, callback) {
|
|||
|
var s, q, qi, node, i, cur,
|
|||
|
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
|
|||
|
if (type === 'js' || type ==='css') {
|
|||
|
s = sources[type], q = queue[type];
|
|||
|
for (i = 0; i < q.length; i++) {
|
|||
|
cur = q[i];
|
|||
|
if (_urls.is(cur.urls)) {
|
|||
|
qi = cur;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
val = _urls.values();
|
|||
|
if (qi) {
|
|||
|
callback && (qi.load || qi.callbacks.push(callback));
|
|||
|
callback && (qi.load && callback());
|
|||
|
} else {
|
|||
|
q.push({
|
|||
|
urls: _urls,
|
|||
|
callbacks: callback ? [callback] : [],
|
|||
|
load: false
|
|||
|
});
|
|||
|
for (i = 0; i < val.length; i++) {
|
|||
|
node = null, url = val[i];
|
|||
|
if (s[url] === undefined) {
|
|||
|
(type === 'js' ) && (node = createNode('script', { src: url }));
|
|||
|
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
|
|||
|
if (node) {
|
|||
|
node.onload = (function(type, url) {
|
|||
|
return function() {
|
|||
|
end(type, url);
|
|||
|
};
|
|||
|
})(type, url);
|
|||
|
(doc.head || doc.body).appendChild(node);
|
|||
|
s[url] = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
return {
|
|||
|
js: function(url, callback) {
|
|||
|
load('js', url, callback);
|
|||
|
},
|
|||
|
css: function(url, callback) {
|
|||
|
load('css', url, callback);
|
|||
|
}
|
|||
|
};
|
|||
|
})(this.document);
|
|||
|
})();
|
|||
|
</script><script>
|
|||
|
(function() {
|
|||
|
var TEXT_VARIABLES = {
|
|||
|
version: '2.2.6',
|
|||
|
sources: {
|
|||
|
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
|
|||
|
jquery: '/assets/js/jquery.min.js',
|
|||
|
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
|
|||
|
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
|
|||
|
gitalk: {
|
|||
|
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
|
|||
|
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
|
|||
|
},
|
|||
|
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
|
|||
|
},
|
|||
|
site: {
|
|||
|
toc: {
|
|||
|
selectors: 'h1,h2,h3'
|
|||
|
}
|
|||
|
},
|
|||
|
paths: {
|
|||
|
search_js: '/assets/search.js'
|
|||
|
}
|
|||
|
};
|
|||
|
window.TEXT_VARIABLES = TEXT_VARIABLES;
|
|||
|
})();
|
|||
|
</script>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div class="root" data-is-touch="false">
|
|||
|
<div class="layout--page js-page-root">
|
|||
|
<!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
|
|||
|
|
|||
|
<div class="page__main-inner">
|
|||
|
<div class="page__header d-print-none">
|
|||
|
<header class="header"><div class="main">
|
|||
|
<div class="header__title">
|
|||
|
<div class="header__brand">
|
|||
|
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewbox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.516 C 137.230 220.679,138.000 221.92
|
|||
|
" href="/">YannStatic</a>
|
|||
|
</div>
|
|||
|
<!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
|
|||
|
<!-- Champ de recherche -->
|
|||
|
<div id="searchbox" class="search search--dark" style="visibility: visible">
|
|||
|
<div class="main">
|
|||
|
<div class="search__header"></div>
|
|||
|
<div class="search-bar">
|
|||
|
<div class="search-box js-search-box">
|
|||
|
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
|
|||
|
<input id="search-input" type="text">
|
|||
|
<!-- <div class="search-box__icon-clear js-icon-clear">
|
|||
|
<a><i class="fas fa-times"></i></a>
|
|||
|
</div> -->
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- Script pointing to search-script.js -->
|
|||
|
<script>/*!
|
|||
|
* Simple-Jekyll-Search
|
|||
|
* Copyright 2015-2020, Christian Fei
|
|||
|
* Licensed under the MIT License.
|
|||
|
*/
|
|||
|
|
|||
|
(function(){
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Templater_7 = {
|
|||
|
compile: compile,
|
|||
|
setOptions: setOptions
|
|||
|
}
|
|||
|
|
|||
|
const options = {}
|
|||
|
options.pattern = /\{(.*?)\}/g
|
|||
|
options.template = ''
|
|||
|
options.middleware = function () {}
|
|||
|
|
|||
|
function setOptions (_options) {
|
|||
|
options.pattern = _options.pattern || options.pattern
|
|||
|
options.template = _options.template || options.template
|
|||
|
if (typeof _options.middleware === 'function') {
|
|||
|
options.middleware = _options.middleware
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function compile (data) {
|
|||
|
return options.template.replace(options.pattern, function (match, prop) {
|
|||
|
const value = options.middleware(prop, data[prop], options.template)
|
|||
|
if (typeof value !== 'undefined') {
|
|||
|
return value
|
|||
|
}
|
|||
|
return data[prop] || match
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
function fuzzysearch (needle, haystack) {
|
|||
|
var tlen = haystack.length;
|
|||
|
var qlen = needle.length;
|
|||
|
if (qlen > tlen) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (qlen === tlen) {
|
|||
|
return needle === haystack;
|
|||
|
}
|
|||
|
outer: for (var i = 0, j = 0; i < qlen; i++) {
|
|||
|
var nch = needle.charCodeAt(i);
|
|||
|
while (j < tlen) {
|
|||
|
if (haystack.charCodeAt(j++) === nch) {
|
|||
|
continue outer;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
var _$fuzzysearch_1 = fuzzysearch;
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
|
|||
|
|
|||
|
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
|
|||
|
|
|||
|
function FuzzySearchStrategy () {
|
|||
|
this.matches = function (string, crit) {
|
|||
|
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
|
|||
|
|
|||
|
function LiteralSearchStrategy () {
|
|||
|
this.matches = function (str, crit) {
|
|||
|
if (!str) return false
|
|||
|
|
|||
|
str = str.trim().toLowerCase()
|
|||
|
crit = crit.trim().toLowerCase()
|
|||
|
|
|||
|
return crit.split(' ').filter(function (word) {
|
|||
|
return str.indexOf(word) >= 0
|
|||
|
}).length === crit.split(' ').length
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Repository_4 = {
|
|||
|
put: put,
|
|||
|
clear: clear,
|
|||
|
search: search,
|
|||
|
setOptions: __setOptions_4
|
|||
|
}
|
|||
|
|
|||
|
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
|
|||
|
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
|
|||
|
|
|||
|
function NoSort () {
|
|||
|
return 0
|
|||
|
}
|
|||
|
|
|||
|
const data = []
|
|||
|
let opt = {}
|
|||
|
|
|||
|
opt.fuzzy = false
|
|||
|
opt.limit = 10
|
|||
|
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = NoSort
|
|||
|
opt.exclude = []
|
|||
|
|
|||
|
function put (data) {
|
|||
|
if (isObject(data)) {
|
|||
|
return addObject(data)
|
|||
|
}
|
|||
|
if (isArray(data)) {
|
|||
|
return addArray(data)
|
|||
|
}
|
|||
|
return undefined
|
|||
|
}
|
|||
|
function clear () {
|
|||
|
data.length = 0
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function isObject (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
|
|||
|
}
|
|||
|
|
|||
|
function isArray (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
|
|||
|
}
|
|||
|
|
|||
|
function addObject (_data) {
|
|||
|
data.push(_data)
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function addArray (_data) {
|
|||
|
const added = []
|
|||
|
clear()
|
|||
|
for (let i = 0, len = _data.length; i < len; i++) {
|
|||
|
if (isObject(_data[i])) {
|
|||
|
added.push(addObject(_data[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
return added
|
|||
|
}
|
|||
|
|
|||
|
function search (crit) {
|
|||
|
if (!crit) {
|
|||
|
return []
|
|||
|
}
|
|||
|
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
|
|||
|
}
|
|||
|
|
|||
|
function __setOptions_4 (_opt) {
|
|||
|
opt = _opt || {}
|
|||
|
|
|||
|
opt.fuzzy = _opt.fuzzy || false
|
|||
|
opt.limit = _opt.limit || 10
|
|||
|
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = _opt.sort || NoSort
|
|||
|
opt.exclude = _opt.exclude || []
|
|||
|
}
|
|||
|
|
|||
|
function findMatches (data, crit, strategy, opt) {
|
|||
|
const matches = []
|
|||
|
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
|
|||
|
const match = findMatchesInObject(data[i], crit, strategy, opt)
|
|||
|
if (match) {
|
|||
|
matches.push(match)
|
|||
|
}
|
|||
|
}
|
|||
|
return matches
|
|||
|
}
|
|||
|
|
|||
|
function findMatchesInObject (obj, crit, strategy, opt) {
|
|||
|
for (const key in obj) {
|
|||
|
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
|
|||
|
return obj
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isExcluded (term, excludedTerms) {
|
|||
|
for (let i = 0, len = excludedTerms.length; i < len; i++) {
|
|||
|
const excludedTerm = excludedTerms[i]
|
|||
|
if (new RegExp(excludedTerm).test(term)) {
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
/* globals ActiveXObject:false */
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$JSONLoader_2 = {
|
|||
|
load: load
|
|||
|
}
|
|||
|
|
|||
|
function load (location, callback) {
|
|||
|
const xhr = getXHR()
|
|||
|
xhr.open('GET', location, true)
|
|||
|
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
|
|||
|
xhr.send()
|
|||
|
}
|
|||
|
|
|||
|
function createStateChangeListener (xhr, callback) {
|
|||
|
return function () {
|
|||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|||
|
try {
|
|||
|
callback(null, JSON.parse(xhr.responseText))
|
|||
|
} catch (err) {
|
|||
|
callback(err, null)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function getXHR () {
|
|||
|
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$OptionsValidator_3 = function OptionsValidator (params) {
|
|||
|
if (!validateParams(params)) {
|
|||
|
throw new Error('-- OptionsValidator: required options missing')
|
|||
|
}
|
|||
|
|
|||
|
if (!(this instanceof OptionsValidator)) {
|
|||
|
return new OptionsValidator(params)
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = params.required
|
|||
|
|
|||
|
this.getRequiredOptions = function () {
|
|||
|
return requiredOptions
|
|||
|
}
|
|||
|
|
|||
|
this.validate = function (parameters) {
|
|||
|
const errors = []
|
|||
|
requiredOptions.forEach(function (requiredOptionName) {
|
|||
|
if (typeof parameters[requiredOptionName] === 'undefined') {
|
|||
|
errors.push(requiredOptionName)
|
|||
|
}
|
|||
|
})
|
|||
|
return errors
|
|||
|
}
|
|||
|
|
|||
|
function validateParams (params) {
|
|||
|
if (!params) {
|
|||
|
return false
|
|||
|
}
|
|||
|
return typeof params.required !== 'undefined' && params.required instanceof Array
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$utils_9 = {
|
|||
|
merge: merge,
|
|||
|
isJSON: isJSON
|
|||
|
}
|
|||
|
|
|||
|
function merge (defaultParams, mergeParams) {
|
|||
|
const mergedOptions = {}
|
|||
|
for (const option in defaultParams) {
|
|||
|
mergedOptions[option] = defaultParams[option]
|
|||
|
if (typeof mergeParams[option] !== 'undefined') {
|
|||
|
mergedOptions[option] = mergeParams[option]
|
|||
|
}
|
|||
|
}
|
|||
|
return mergedOptions
|
|||
|
}
|
|||
|
|
|||
|
function isJSON (json) {
|
|||
|
try {
|
|||
|
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
|
|||
|
return true
|
|||
|
}
|
|||
|
return false
|
|||
|
} catch (err) {
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var _$src_8 = {};
|
|||
|
(function (window) {
|
|||
|
'use strict'
|
|||
|
|
|||
|
let options = {
|
|||
|
searchInput: null,
|
|||
|
resultsContainer: null,
|
|||
|
json: [],
|
|||
|
success: Function.prototype,
|
|||
|
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
|
|||
|
templateMiddleware: Function.prototype,
|
|||
|
sortMiddleware: function () {
|
|||
|
return 0
|
|||
|
},
|
|||
|
noResultsText: 'No results found',
|
|||
|
limit: 10,
|
|||
|
fuzzy: false,
|
|||
|
debounceTime: null,
|
|||
|
exclude: []
|
|||
|
}
|
|||
|
|
|||
|
let debounceTimerHandle
|
|||
|
const debounce = function (func, delayMillis) {
|
|||
|
if (delayMillis) {
|
|||
|
clearTimeout(debounceTimerHandle)
|
|||
|
debounceTimerHandle = setTimeout(func, delayMillis)
|
|||
|
} else {
|
|||
|
func.call()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
|
|||
|
|
|||
|
/* removed: const _$Templater_7 = require('./Templater') */;
|
|||
|
/* removed: const _$Repository_4 = require('./Repository') */;
|
|||
|
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
|
|||
|
const optionsValidator = _$OptionsValidator_3({
|
|||
|
required: requiredOptions
|
|||
|
})
|
|||
|
/* removed: const _$utils_9 = require('./utils') */;
|
|||
|
|
|||
|
window.SimpleJekyllSearch = function (_options) {
|
|||
|
const errors = optionsValidator.validate(_options)
|
|||
|
if (errors.length > 0) {
|
|||
|
throwError('You must specify the following required options: ' + requiredOptions)
|
|||
|
}
|
|||
|
|
|||
|
options = _$utils_9.merge(options, _options)
|
|||
|
|
|||
|
_$Templater_7.setOptions({
|
|||
|
template: options.searchResultTemplate,
|
|||
|
middleware: options.templateMiddleware
|
|||
|
})
|
|||
|
|
|||
|
_$Repository_4.setOptions({
|
|||
|
fuzzy: options.fuzzy,
|
|||
|
limit: options.limit,
|
|||
|
sort: options.sortMiddleware,
|
|||
|
exclude: options.exclude
|
|||
|
})
|
|||
|
|
|||
|
if (_$utils_9.isJSON(options.json)) {
|
|||
|
initWithJSON(options.json)
|
|||
|
} else {
|
|||
|
initWithURL(options.json)
|
|||
|
}
|
|||
|
|
|||
|
const rv = {
|
|||
|
search: search
|
|||
|
}
|
|||
|
|
|||
|
typeof options.success === 'function' && options.success.call(rv)
|
|||
|
return rv
|
|||
|
}
|
|||
|
|
|||
|
function initWithJSON (json) {
|
|||
|
_$Repository_4.put(json)
|
|||
|
registerInput()
|
|||
|
}
|
|||
|
|
|||
|
function initWithURL (url) {
|
|||
|
_$JSONLoader_2.load(url, function (err, json) {
|
|||
|
if (err) {
|
|||
|
throwError('failed to get JSON (' + url + ')')
|
|||
|
}
|
|||
|
initWithJSON(json)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function emptyResultsContainer () {
|
|||
|
options.resultsContainer.innerHTML = ''
|
|||
|
}
|
|||
|
|
|||
|
function appendToResultsContainer (text) {
|
|||
|
options.resultsContainer.innerHTML += text
|
|||
|
}
|
|||
|
|
|||
|
function registerInput () {
|
|||
|
options.searchInput.addEventListener('input', function (e) {
|
|||
|
if (isWhitelistedKey(e.which)) {
|
|||
|
emptyResultsContainer()
|
|||
|
debounce(function () { search(e.target.value) }, options.debounceTime)
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function search (query) {
|
|||
|
if (isValidQuery(query)) {
|
|||
|
emptyResultsContainer()
|
|||
|
render(_$Repository_4.search(query), query)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function render (results, query) {
|
|||
|
const len = results.length
|
|||
|
if (len === 0) {
|
|||
|
return appendToResultsContainer(options.noResultsText)
|
|||
|
}
|
|||
|
for (let i = 0; i < len; i++) {
|
|||
|
results[i].query = query
|
|||
|
appendToResultsContainer(_$Templater_7.compile(results[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isValidQuery (query) {
|
|||
|
return query && query.length > 0
|
|||
|
}
|
|||
|
|
|||
|
function isWhitelistedKey (key) {
|
|||
|
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
|
|||
|
}
|
|||
|
|
|||
|
function throwError (message) {
|
|||
|
throw new Error('SimpleJekyllSearch --- ' + message)
|
|||
|
}
|
|||
|
})(window)
|
|||
|
|
|||
|
}());
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Configuration -->
|
|||
|
<script>
|
|||
|
SimpleJekyllSearch({
|
|||
|
searchInput: document.getElementById('search-input'),
|
|||
|
resultsContainer: document.getElementById('results-container'),
|
|||
|
json: '/search.json',
|
|||
|
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date} {title}</a></li>'
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {title}</a></li>'
|
|||
|
})
|
|||
|
</script>
|
|||
|
<!-- Fin déclaration champ de recherche -->
|
|||
|
</div>
|
|||
|
<nav class="navigation">
|
|||
|
<ul>
|
|||
|
<li class="navigation__item"><a href="/archive.html">Etiquettes</a></li>
|
|||
|
<li class="navigation__item"><a href="/htmldoc.html">Documents</a></li>
|
|||
|
<li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li>
|
|||
|
<li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li>
|
|||
|
</ul>
|
|||
|
</nav>
|
|||
|
</div>
|
|||
|
</header>
|
|||
|
|
|||
|
</div>
|
|||
|
<div class="page__content"><div class="main"><div class="grid grid--reverse">
|
|||
|
<div class="col-main cell cell--auto">
|
|||
|
<!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div>
|
|||
|
<!-- end custom main top snippet -->
|
|||
|
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">Les ACL (Access Control Lists) sous Linux</h1></header></div>
|
|||
|
<meta itemprop="headline" content="Les ACL (Access Control Lists) sous Linux">
|
|||
|
<div class="article__info clearfix">
|
|||
|
<ul class="left-col menu"><li>
|
|||
|
<a class="button button--secondary button--pill button--sm" href="/archive.html?tag=cli">cli</a>
|
|||
|
</li></ul>
|
|||
|
<ul class="right-col menu"><li>
|
|||
|
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF">12 nov. 2020</span>
|
|||
|
|
|||
|
<span title="Modification" style="color:#00FF7F">27 mars 2024</span>
|
|||
|
</li></ul>
|
|||
|
</div>
|
|||
|
<meta itemprop="datePublished" content="2024-03-27T00:00:00+01:00">
|
|||
|
<meta itemprop="keywords" content="cli">
|
|||
|
<div class="js-article-content">
|
|||
|
<div class="layout--article">
|
|||
|
<!-- start custom article top snippet -->
|
|||
|
<style>
|
|||
|
#myBtn {
|
|||
|
display: none;
|
|||
|
position: fixed;
|
|||
|
bottom: 10px;
|
|||
|
right: 10px;
|
|||
|
z-index: 99;
|
|||
|
font-size: 12px;
|
|||
|
font-weight: bold;
|
|||
|
border: none;
|
|||
|
outline: none;
|
|||
|
background-color: white;
|
|||
|
color: black;
|
|||
|
cursor: pointer;
|
|||
|
padding: 5px;
|
|||
|
border-radius: 4px;
|
|||
|
}
|
|||
|
|
|||
|
#myBtn:hover {
|
|||
|
background-color: #555;
|
|||
|
}
|
|||
|
</style>
|
|||
|
|
|||
|
<button onclick="topFunction()" id="myBtn" title="Haut de page">⇧</button>
|
|||
|
|
|||
|
<script>
|
|||
|
//Get the button
|
|||
|
var mybutton = document.getElementById("myBtn");
|
|||
|
|
|||
|
// When the user scrolls down 20px from the top of the document, show the button
|
|||
|
window.onscroll = function() {scrollFunction()};
|
|||
|
|
|||
|
function scrollFunction() {
|
|||
|
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
|
|||
|
mybutton.style.display = "block";
|
|||
|
} else {
|
|||
|
mybutton.style.display = "none";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// When the user clicks on the button, scroll to the top of the document
|
|||
|
function topFunction() {
|
|||
|
document.body.scrollTop = 0;
|
|||
|
document.documentElement.scrollTop = 0;
|
|||
|
}
|
|||
|
</script>
|
|||
|
|
|||
|
|
|||
|
<!-- end custom article top snippet -->
|
|||
|
<div class="article__content" itemprop="articleBody">
|
|||
|
<details>
|
|||
|
<summary><b>Afficher/cacher Sommaire</b></summary>
|
|||
|
<!-- affichage sommaire -->
|
|||
|
<div class="toc-aside js-toc-root"></div>
|
|||
|
</details><p><em>Les droits standards et les droits étendus sont des fonctionnalités intéressantes mais qui ne s’applique que pour un seul utilisateur ou un seul groupe. Comment définir des permissions spécifiques, voire différents, pour d’autres utilisateurs ou groupes que les propriétaires ? Les ACLs (Access control lists) offrent une réponse à cette question.</em></p>
|
|||
|
|
|||
|
<p><img src="/images/acl-logo.png" alt=""></p>
|
|||
|
|
|||
|
<h2 id="acl---listes-contrôle-accès">ACL - Listes contrôle accès</h2>
|
|||
|
|
|||
|
<p><em>Vous avez surement déjà été confronté à un problème de permissions de fichiers et dossiers, par exemple vous voulez créer un dossier partagé, mais vous ne souhaitez que certains sous-dossiers ne soient accessibles qu’à certains utilisateurs, modifiables par d’autres, etc… <br>
|
|||
|
Les Access Control Lists (ACL) vous permettent de créer des permissions à la carte, toutes les combinaisons sont possibles.</em></p>
|
|||
|
|
|||
|
<h3 id="installation">Installation</h3>
|
|||
|
|
|||
|
<p><em>Les droits standards et les droits étendus sont des fonctionnalités intéressantes mais qui ne s’applique que pour un seul utilisateur ou un seul groupe. Comment définir des permissions spécifiques, voire différents, pour d’autres utilisateurs ou groupes que les propriétaires ? Les ACLs offrent une réponse à cette question.</em></p>
|
|||
|
|
|||
|
<p>Il existe deux commandes essentielles : l’une pour manipuler l’ACL d’un fichier (<code class="language-plaintext highlighter-rouge">setfacl</code>) et l’autre pour la consulter (<code class="language-plaintext highlighter-rouge">getfacl</code>). Les commandes traditionnelles <code class="language-plaintext highlighter-rouge">chmod</code> et <code class="language-plaintext highlighter-rouge">chown</code> ne peuvent accéder aux ACL.</p>
|
|||
|
|
|||
|
<p>Ces deux commandes nécessitent, sous Debian (et distributions dérivées, comme Knoppix ou Ubuntu), l’installation du paquetage « acl ».<br>
|
|||
|
Pour l’installer :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install acl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour les distributions à base de RedHat (donc aussi Fedora, Mandriva), il faut installer les paquetages acl.<em>.rpm et libacl1.</em>.rpm (leur nom contient leur numéro de version).</p>
|
|||
|
|
|||
|
<p class="info">si la partition concernée par le partage est de type ext4 le support des acl est actif par défaut: l’option de montage “acl” a été remplacée par “noacl”, qui devient donc celle à utiliser si on veut… désactiver le support des acl</p>
|
|||
|
|
|||
|
<p><strong>Autres Systèmes fichiers</strong><br>
|
|||
|
Quand le noyau est disposé à gérer les ACL, on doit préparer les partitions montées dans un système de fichiers adapté (par exemple, il est exclu de vouloir utiliser ces permissions avec du vfat).</p>
|
|||
|
|
|||
|
<p>Montage et démontage à la volée, il faut monter les partitions voulues avec l’option acl. Par exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount -t ext3 -o defaults,acl /dev/hda2/ /var/www/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si la partition est déjà montée, on peut modifier ces paramètres à la volée :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount -o remount,acl /var/www/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>L’inscription dans <code class="language-plaintext highlighter-rouge">/etc/fstab</code> des options de gestion des ACL est recommandée quand leur utilisation est régulière. <br>
|
|||
|
Par exemple, notre même couple partition / point de montage serait déclaré ainsi :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/dev/hda2 /var/www ext3 defaults,acl 0 0
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>À chaque montage automatique des partitions, le support des ACL sera activé.</p>
|
|||
|
|
|||
|
<h3 id="explications">Explications</h3>
|
|||
|
|
|||
|
<p>l’attribution des droits se fait grâce à la commande <strong>setfacl</strong>, la lecture des droits avec <strong>getfacl</strong></p>
|
|||
|
|
|||
|
<p>Ainsi les deux commandes suivantes sont équivalentes :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod u=rw fichier
|
|||
|
setfacl -m u::rw
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Un fichier dont les ACL auront été spécifiés verra s’ajouter un <strong>+</strong> à la fin de la liste des droits avec la commande :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ls -l fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><strong>getfacl</strong> permet d’afficher l’ensemble des permissions définies :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: fichier
|
|||
|
# owner: utilisateur
|
|||
|
# group: utilisateur
|
|||
|
user::rwx
|
|||
|
user:utilisateur1:rw-
|
|||
|
user:utilisateur2:r--
|
|||
|
group::r--
|
|||
|
mask::rwx
|
|||
|
other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ici on peut voir que le propriétaire du fichier (utilisateur) a les droits rwx, utilisateur1 rw- et utilisateur2 r–, les autres utilisateurs n’ont aucun droit</p>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">getfacl --omit-header ...</code> supprime de l’affichage les 3 premières lignes, le nom du fichier, le propriétaire et le groupe.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir dir
|
|||
|
ls -ld dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>drwxr-xr-x 2 root root 4096 Mar 12 13:54 dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl --omit-header dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user::rwx
|
|||
|
group::r-x
|
|||
|
other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sans acl, la commande getfacl donne les mêmes informations que <code class="language-plaintext highlighter-rouge">ls -ld</code></p>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">setfacl -d ...</code> spécifie des acl par défaut, qui ne peuvent s’appliquer qu’aux dossiers.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m user:fdsadmin:rwx dir
|
|||
|
setfacl -d -m group:nasgrp:r-x dir
|
|||
|
getfacl --omit-header dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user::rwx
|
|||
|
user:fdsadmin:rwx
|
|||
|
group::r-x
|
|||
|
mask::rwx
|
|||
|
other::r-x
|
|||
|
default:user::rwx
|
|||
|
default:group::r-x
|
|||
|
default:group:nasgrp:r-x
|
|||
|
default:mask::r-x
|
|||
|
default:other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><code class="language-plaintext highlighter-rouge">getfacl --access --default ...</code> L’affichage précédent peut se décomposer en droits d’accès fichier, et en droits par défaut :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl --omit-header --access dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user::rwx
|
|||
|
user:fdsadmin:rwx
|
|||
|
group::r-x
|
|||
|
mask::rwx
|
|||
|
other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl --omit-header --default dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user::rwx
|
|||
|
group::r-x
|
|||
|
group:nasgrp:r-x
|
|||
|
mask::r-x
|
|||
|
other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les <strong>permissions effectives</strong> sont affichées individuellement pour les utilisateurs ou les groupes qui subissent un droit de hiérarchie supérieure différent :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod g-w dir
|
|||
|
ls -ld dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>drwxr-xr-x+ 2 root root 4096 Mar 12 13:54 dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl --omit-header dir
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user::rwx
|
|||
|
user:fdsadmin:rwx #effective:r-x
|
|||
|
group::r-x
|
|||
|
mask::r-x
|
|||
|
other::r-x
|
|||
|
default:user::rwx
|
|||
|
default:group::r-x
|
|||
|
default:group:nasgrp:r-x
|
|||
|
default:mask::r-x
|
|||
|
default:other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ici l’utilisateur fdsadmin qui avait pourtant les droits rwx s’est vu amputer du droit w supprimé au groupe.</p>
|
|||
|
|
|||
|
<p>autoriser à “utilisateur” la lecture et l’écriture sur “fichier”</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m user:utilisateur:rw fichier
|
|||
|
setfacl -m u:utilisateur:rw fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>La même commande est disponible pour les groupes. Il suffit de remplacer <strong>u</strong>/<strong>user</strong> par <strong>g</strong>/<strong>group</strong></p>
|
|||
|
|
|||
|
<p>modifier les permissions de plusieurs utilisateurs/groupes sur “fichier” en même temps</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m user:utilisateur:rwx,user:utilisateur2:r,group:groupe:rw fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>définir l’accès en lecture par défaut pour “utilisateur” pour les nouveaux fichiers créés dans “dossier”</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m d:u:utilisateur:r dossier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>supprimer les ACL pour un utilisateur sur une arborescence</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -R -x user::nom_user repertoire_base_arborescence
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>supprimer les ACL sur un fichier/dossier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -b fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="commande-setfacl">Commande setfacl</h3>
|
|||
|
|
|||
|
<p>Le nom de la commande se comprend set file’s ACL (« régler l’ACL du fichier »). Elle possède de nombreuses options dont il convient de prendre connaissance en consultant la page de manuel (<code class="language-plaintext highlighter-rouge">man setfacl</code>). La commande fonctionne bien sûr aussi de manière récursive (option -R) :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -Rm u:khadija:rw /var/www/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>create: 2020-11-12
|
|||
|
modifie l’ACL de tous les fichiers situés sous /var/www/ en attribuant une permission de lecture et d’écriture à l’utilisateur khadija.</p>
|
|||
|
|
|||
|
<p><strong>Ajouter des permissions</strong></p>
|
|||
|
|
|||
|
<p>La commande <code class="language-plaintext highlighter-rouge">setfacl -m u:khadija:rw /var/www/index.php</code> modifiera (<code class="language-plaintext highlighter-rouge">-m</code>) l’ACL de /var/www/index.php en attribuant à l’utilisateur (préfixe u:) khadija les droits rw et en lui refusant le droit d’exécution (qui n’a pas été mentionné dans la commande).</p>
|
|||
|
|
|||
|
<p>Les principaux paramètres à connaître sont :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>préfixes :
|
|||
|
<ul>
|
|||
|
<li>
|
|||
|
<strong>u:</strong> (droits pour un utilisateur, nommé ou désigné par son uid) ;</li>
|
|||
|
<li>
|
|||
|
<strong>g:</strong> (droits pour un groupe, nommé ou désigné par son gid) ;</li>
|
|||
|
<li>
|
|||
|
<strong>o:</strong> (droits pour other, le reste du monde) ;</li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li>permissions : elles sont codées dans l’ordre <code class="language-plaintext highlighter-rouge">r, w</code> et <code class="language-plaintext highlighter-rouge">x</code> ou <code class="language-plaintext highlighter-rouge">X</code> (ce dernier représentant, comme avec chmod, le droit d’entrée dans les répertoires ou celui d’exécution pour les fichiers qui ont déjà un marqueur x).<br>
|
|||
|
On les remplace par <code class="language-plaintext highlighter-rouge">-</code> pour une interdiction explicite. Ne pas mentionner un droit revient aussi à une interdiction :<code class="language-plaintext highlighter-rouge"> setfacl -m u:khadija:w /var/www/index.php et setfacl -m u:khadija:-w- /var/www/index.php</code> reviennent au même.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>On peut construire des commandes plus complexes en enchaînant les entrées dans l’ACL :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m u:khadija:rw,g:site1:r--,o:--- /var/www/index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>définit des permissions dans l’ACL de <code class="language-plaintext highlighter-rouge">/var/www/index.php</code> pour l’utilisateur khadija, le groupe site1 et le reste du monde.</p>
|
|||
|
|
|||
|
<p>Cette commande permet aussi de modifier les permissions classiques (et remplace dans ce cas <code class="language-plaintext highlighter-rouge">chmod</code>) : l’utilisateur, le groupe et le reste du monde initiaux du fichier sont simplement désignés par le préfixe (<code class="language-plaintext highlighter-rouge">u:, g:, o:</code>) suivi d’un nom vide <code class="language-plaintext highlighter-rouge">:</code> si un fichier <strong>index.php</strong> appartient à <code class="language-plaintext highlighter-rouge">luce:www-data</code> avec les droits <code class="language-plaintext highlighter-rouge">r--r-----</code>, pour donner à l’utilisateur et le groupe les droits en lecture et écriture il suffit d’une commande <code class="language-plaintext highlighter-rouge">setfacl -m u::rw,g::rw /var/www/index.php</code><br>
|
|||
|
Si l’utilisateur et le groupe possèdent déjà un droit qui ne serait pas mentionné dans la commande setfacl, ce droit sera annulé. Soit le fichier index.php avec les droits <code class="language-plaintext highlighter-rouge">rw-r-----</code> pour <code class="language-plaintext highlighter-rouge">luce:www-data</code><br>
|
|||
|
La commande <code class="language-plaintext highlighter-rouge">setfacl -m u::r,g::x index.php</code> modifiera les droits à <code class="language-plaintext highlighter-rouge">r----x---</code> pour pour <code class="language-plaintext highlighter-rouge">luce:www-data</code></p>
|
|||
|
|
|||
|
<p><strong>Droits par défaut et héritage des droits étendus</strong></p>
|
|||
|
|
|||
|
<p>Les droits étendus d’un objet parent ne sont pas automatiquement hérités par les objets contenus. Par exemple, si un répertoire (<code class="language-plaintext highlighter-rouge">root:www-data, rwxr-x-r-x</code>) possède une ACL <code class="language-plaintext highlighter-rouge">u:luce:rwx</code>, un fichier créé à l’intérieur (ou déjà présent avant l’adjonction de l’ACL) ne reçoit pas cette ACL et ses droits sont ceux impliqués par l’<a href="https://lea-linux.org/documentations/Fstab">umask</a> défini.</p>
|
|||
|
|
|||
|
<p>On peut modifier ce comportement en ajoutant, <strong>aux répertoires seulement</strong>, un attribut default, codé <code class="language-plaintext highlighter-rouge">d:</code>, qui se transmet à tous les fichiers créés dans le répertoire après l’ajout de l’ACL par défaut.<br>
|
|||
|
Par exemple, <code class="language-plaintext highlighter-rouge">setfacl -m d:u:luce:rwX /var/www</code> donne à luce les droits de lecture et écriture (ainsi qu’« exécution » quand il s’agit de répertoires) pour tous les fichiers qui seront créés sous <code class="language-plaintext highlighter-rouge">/var/www</code> à partir de ce moment, jusqu’à ce que cette ACL « par défaut » soit annulé ou remplacé.</p>
|
|||
|
|
|||
|
<p><strong>Retirer des permissions</strong></p>
|
|||
|
|
|||
|
<p>Pour annuler tout ou partie d’une ACL :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -b /var/www/index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ôte tout le contenu de l’ACL du fichier, tandis que</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -x u:khadija,g:site1 /var/www/index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>retire les permissions propres à khadija et au groupe site1.</p>
|
|||
|
|
|||
|
<p>Les permissions ACL par défaut d’un répertoire (<code class="language-plaintext highlighter-rouge">d:</code>) s’annulent par <code class="language-plaintext highlighter-rouge">setfacl -k</code></p>
|
|||
|
|
|||
|
<p><strong>Le masque</strong></p>
|
|||
|
|
|||
|
<p>Le masque est une synthèse des valeurs les plus permissives que possède un fichier doté d’une ACL. Les droits de l’utilisateur fondamental ne sont cependant pas pris en compte.<br>
|
|||
|
Le masque est calculé automatiquement :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown luce:www-data index.php chmod 640 index.php
|
|||
|
ls -l index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> -rw-r----- 1 luce www-data 5055 2005-10-16 18:53 index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # file: index.php
|
|||
|
# owner: luce
|
|||
|
# group: www-data
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ce fichier n’a pas d’ACL donc pas de masque.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m u:jean:rw,g:web:rw index.php getfacl index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # file: index.php
|
|||
|
# owner: luce
|
|||
|
# group: www-data
|
|||
|
user::rw-
|
|||
|
user:jean:rw-
|
|||
|
group::r--
|
|||
|
group:web:rw-
|
|||
|
mask::rw-
|
|||
|
other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Maintenant que le fichier possède une ACL, il a reçu un masque : les permissions les plus élevées (utilisateur exclu) étant <code class="language-plaintext highlighter-rouge">rw</code>, c’est aussi la valeur du masque.</p>
|
|||
|
|
|||
|
<p>L’intérêt du masque est de pouvoir limiter d’un coup toutes les permissions d’un fichier (étendues ou non), sauf celles du propriétaire ; on utilise pour cela le préfixe <code class="language-plaintext highlighter-rouge">m:</code> suivi du droit maximal à accorder :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # file: index.php
|
|||
|
# owner: luce
|
|||
|
# group: www-data
|
|||
|
user::rw-
|
|||
|
user:jean:rw-
|
|||
|
group::r--
|
|||
|
group:web:rw-
|
|||
|
mask::rw-
|
|||
|
other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m m:r index.php getfacl index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # file: index.php
|
|||
|
# owner: luce
|
|||
|
# group: www-data
|
|||
|
user::rw-
|
|||
|
user:jean:rw- #effective:r--
|
|||
|
group::r--
|
|||
|
group:web:rw- #effective:r--
|
|||
|
mask::r--
|
|||
|
other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Les valeurs modifiées sont indiquées par le commentaire « effective: » suivi des permissions effectives après l’application du masque (ici, jean et web n’ont plus que le droit r, la situation reste la même pour www-data).</p>
|
|||
|
|
|||
|
<h3 id="commande-getfacl">Commande getfacl</h3>
|
|||
|
|
|||
|
<p>Cette commande suivie d’un nom de fichier affiche l’ACL de ce fichier (get file’s ACL « récupérer l’ACL du fichier »). Par exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl /var/www
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # file: var/www
|
|||
|
# owner: root
|
|||
|
# group: www-data
|
|||
|
user::rwx
|
|||
|
user:luce:rwx
|
|||
|
group::rwx
|
|||
|
mask::rwx
|
|||
|
other::r-x
|
|||
|
default:user::rwx
|
|||
|
default:user:khadija:rwx
|
|||
|
default:group::rwx
|
|||
|
default:group:www-data:r-x
|
|||
|
default:mask::rwx
|
|||
|
default:other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>On voit qu’outre les droits traditionnels attribués à <code class="language-plaintext highlighter-rouge">root:www-data</code> (droits indiqués après <code class="language-plaintext highlighter-rouge">user::</code> et <code class="language-plaintext highlighter-rouge">group::</code>), sont aussi définis :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>des droits complets pour luce (<code class="language-plaintext highlighter-rouge">user:luce:rwx</code>) ;</li>
|
|||
|
<li>une permission ACL par défaut donnant des droits complets à khadija sur tous les nouveaux fichiers créés sous /var/www/ (<code class="language-plaintext highlighter-rouge">default:user:khadija:rwx</code>) ;</li>
|
|||
|
<li>une autre permission ACL par défaut donnant des droits de lecture et d’exécution au groupe www-data sur les mêmes fichiers (<code class="language-plaintext highlighter-rouge">default:group:www-data:r-x</code>).</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Noter que <code class="language-plaintext highlighter-rouge">user::, group::</code> et <code class="language-plaintext highlighter-rouge">other::</code> représentent le triplet <strong>utilisateur / groupe / reste du monde</strong> des permissions classiques. Appliquer cette commande sur un fichier qui n’a pas d’ACL définie donne les mêmes informations que <code class="language-plaintext highlighter-rouge">ls -l</code>, dans un format différent :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -b index.php # retirer les ACL pouvant exister
|
|||
|
ls -l index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> -rw-r----- 1 root www-data 5055 2005-10-16 18:53 index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # file: index.php
|
|||
|
# owner: root
|
|||
|
# group: www-data
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="commandes-ls-cp-et-mv">Commandes ls, cp et mv</h3>
|
|||
|
|
|||
|
<p>Ces commandes doivent pouvoir lister, copier et déplacer les ACL en même temps que les fichiers. Pour les deux premières commandes, il faut préciser explicitement que l’on veut afficher/conserver les droits (ce qui est aussi le cas quand on ne travaille que sur les droits classiques) : <code class="language-plaintext highlighter-rouge">ls -l</code>, <code class="language-plaintext highlighter-rouge">cp -a</code><br>
|
|||
|
La commande <code class="language-plaintext highlighter-rouge">mv</code>, quant à elle, préserve toujours les droits.</p>
|
|||
|
|
|||
|
<p>Quand les droits étendus ne peuvent être conservés (déplacement ou copie vers un système de fichier qui n’est pas configuré pour les recevoir ou utilisation d’une version de cp trop ancienne), un message d’avertissement en informe l’utilisateur. Par exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m u:luce:rw index.php cp -a index.php /mnt/vfat
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cp: preserving permissions for `/mnt/vfat/index.php': Opération non supportée
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Noter qu’un fichier comportant une ACL qu’on veut lister par <code class="language-plaintext highlighter-rouge">ls -l</code> n’affiche qu’un <code class="language-plaintext highlighter-rouge">+</code> à la suite de ses permissions.<br>
|
|||
|
Seule la commande <code class="language-plaintext highlighter-rouge">getfacl</code>, pour l’instant, permet d’avoir connaissance du détail.<br>
|
|||
|
Par exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m u:khadija:rw /var/www/index.php ls -l /var/www/index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> -rw-rw----+ 1 khadija www-data 5055 2005-10-16 18:53 /var/www/index.php
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Avec <code class="language-plaintext highlighter-rouge">-rw-rw----+</code>, on sait que le fichier possède une ACL (+), sans en connaître les constituants.</p>
|
|||
|
|
|||
|
<h3 id="sauvegarde-des-données">Sauvegarde des données</h3>
|
|||
|
|
|||
|
<p>Sauvegarder des données dotées d’ACL nécessite :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>l’utilisation d’un système de fichiers pour le stockage qui soit compatible ;</li>
|
|||
|
<li>et l’utilisation d’un logiciel de sauvegarde qui soit tout autant compatible.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>À titre indicatif, <code class="language-plaintext highlighter-rouge">tar</code> et <code class="language-plaintext highlighter-rouge">cpio</code> et <code class="language-plaintext highlighter-rouge">rsync</code> ne le sont pas (à moins d’être patchés), <code class="language-plaintext highlighter-rouge">star</code> et <code class="language-plaintext highlighter-rouge">pax</code> le sont.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p><strong>Pour contourner le problème de sauvegarde</strong>, il est possible d’écrire toutes les ACL dans un fichier qui servira de base à une restauration ultérieure :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl --skip-base -R /dossier/dossier/ > fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>récupère les informations récursivement et les inscrit dans un simple fichier.</p>
|
|||
|
|
|||
|
<p>La restauration se fait au moyen de</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --restore=fichier
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Il faut, pour qu’elle fonctionne, se placer à la racine contenant l’arborescence, en raison de la notation relative des chemins (d’où le message <code class="language-plaintext highlighter-rouge">Removing leading '/' from absolute path names</code> que l’on peut souvent lire en tapant des commandes avec ces programmes).<br>
|
|||
|
Le chemin d’un répertoire <code class="language-plaintext highlighter-rouge">/tmp/test</code> est enregistré comme <code class="language-plaintext highlighter-rouge">tmp/test</code> : on doit donc, pour restaurer, lancer la commande depuis la racine de <code class="language-plaintext highlighter-rouge">/tmp</code>, c’est-à-dire <code class="language-plaintext highlighter-rouge">/</code></p>
|
|||
|
|
|||
|
<p><strong>Par exemple :</strong> le répertoire <code class="language-plaintext highlighter-rouge">/tmp/test</code> contient trois fichiers à ACL.<br>
|
|||
|
On sauvegarde les ACL avec</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl --skip-base -R /tmp/test > acl.acl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Pour restaurer, on se place à la racine</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /
|
|||
|
# et on lance
|
|||
|
setfacl --restore=acl.acl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Si on avait lancé la commande depuis <code class="language-plaintext highlighter-rouge">/test</code>, setfacl aurait renvoyé les erreurs : setfacl: tmp/test: Aucun fichier ou répertoire de ce type <code class="language-plaintext highlighter-rouge">setfacl: tmp/test/a: Aucun fichier ou répertoire de ce type setfacl: tmp/test/b: Aucun fichier ou répertoire de ce type setfacl: tmp/test/c: Aucun fichier ou répertoire de ce type</code></p>
|
|||
|
|
|||
|
<h3 id="compatibilité">Compatibilité</h3>
|
|||
|
|
|||
|
<p>Tous les utilitaires (sauvegarde, copie, déplacement de fichiers) ne sont pas nécessairement compatibles avec les ACLs. Il sera donc indiqué de sauvegarder les ACLs définies pour un dossier afin de les repousser sur une copie des fichiers.</p>
|
|||
|
|
|||
|
<p>Par exemple, on copie le répertoire /opt/partagedans opt/p2 avec récursion (option -R) :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cp -R /opt/partage /opt/p2
|
|||
|
getfacl /opt/p2
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl : suppression du premier « / » des noms de chemins absolus
|
|||
|
# file: opt/p2
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rwx
|
|||
|
group::r-x
|
|||
|
other::r-x
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Sauvegarde de l’ACL originale :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl -R /opt/partage > acls
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl : suppression du premier « / » des noms de chemins absolus
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Adaptation des nouveaux droits :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sed -i -e "s/opt\/partage/\/opt\/p2/g" acls
|
|||
|
getfacl /opt/p2
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>getfacl : suppression du premier « / » des noms de chemins absolus
|
|||
|
# file: opt/p2
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rwx
|
|||
|
user:alpha:r-x
|
|||
|
group::r-x
|
|||
|
group:omega:r-x
|
|||
|
mask::r-x
|
|||
|
other::r-x
|
|||
|
default:user::rwx
|
|||
|
default:user:alpha:r-x
|
|||
|
default:group::r-x
|
|||
|
default:mask::r-x
|
|||
|
default:other::---
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Restauration de l’ACL :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --restore=acls
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="astuces">Astuces</h3>
|
|||
|
|
|||
|
<p>Afficher la version de la commande setfacl</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --version
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>apt-cache policy acl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter des ACL étendues sur un fichier/dossier à partir de la ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m u:user:(rwx),g:group:(rwx) fichiers|dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :<br>
|
|||
|
Avant modification :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modification des ACL étendues :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m u:adminsys:rw,g:adminsys:r file.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Après modification :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
user:adminsys:rw-
|
|||
|
group::r--
|
|||
|
group:adminsys:r--
|
|||
|
mask::rw-
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Remarque :<br>
|
|||
|
Un fichier/dossier possédant des ACL étendues est identifié par un « + » lors de la commande ls :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> -rw-rw-r--+ 1 root root 0 nov. 19 23:46 file.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter des ACL étendues sur un fichier/dossier à partir d’un fichier de référence</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -M aclfilename fichiers|dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :<br>
|
|||
|
Avant modification :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Modification des ACL étendues :</p>
|
|||
|
|
|||
|
<p>Contenu du fichier listing.acl :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> u:adminsys:rw
|
|||
|
g:adminsys:r
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -M listing.acl file.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Après modification :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
user:adminsys:rw-
|
|||
|
group::r--
|
|||
|
group:adminsys:r--
|
|||
|
mask::rw-
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Remarque :<br>
|
|||
|
Un fichier/dossier possédant des ACL étendues est identifié par un « + » lors de la commande ls :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> -rw-rw-r--+ 1 root root 0 nov. 19 23:46 file.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter des ACL étendues récursivement sur un dossier
|
|||
|
A partir de la ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -Rm u:user:(rwx),g:group:(rwx) dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>A partir d’un fichier de référence</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -RM aclfilename dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Remarque :<br>
|
|||
|
Tous les fichiers et dossiers contenu dans le dossier spécifié en argument se verront affecter les mêmes ACL étendues.
|
|||
|
Il est souvent plus cohérent d’attribuer des ACL différentes aux fichiers et aux dossiers, pour cela utiliser la commande find.</p>
|
|||
|
|
|||
|
<p>Ajouter des ACL étendues récursivement uniquement sur les fichiers
|
|||
|
A partir de la ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -print0 | xargs -0 setfacl -m u:user:(rwx),g:group:(rwx)
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -exec setfacl -m u:user:(rwx),g:group:(rwx) {} +
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -exec setfacl -m u:user:(rwx),g:group:(rwx) {} \;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>A partir d’un fichier de référence</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -print0 | xargs -0 setfacl -M aclfilename
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -exec setfacl -M aclfilename {} +
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -exec setfacl -M aclfilename {} \;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajouter des ACL étendues récursivement uniquement sur les dossiers
|
|||
|
A partir de la ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -print0 | xargs -0 setfacl -m u:user:(rwx),g:group:(rwx)
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -exec setfacl -m u:user:(rwx),g:group:(rwx) {} +
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -exec setfacl -m u:user:(rwx),g:group:(rwx) {} \;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>A partir d’un fichier de référence</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -print0 | xargs -0 setfacl -M aclfilename
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -exec setfacl -M aclfilename {} +
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>ou</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -exec setfacl -M aclfilename {} \;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Supprimer toutes les ACL étendues sur un fichier/dossier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -b fichiers|dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Supprimer toutes les ACL étendues récursivement sur un dossier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -Rb dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Uniquement sur les fichiers contenus dans ce dossier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type f -print0 | xargs -0 setfacl -b
|
|||
|
|
|||
|
find . -type f -exec setfacl -b {} +
|
|||
|
|
|||
|
find . -type f -exec setfacl -b {} \;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Uniquement sur les sous-dossiers contenus dans ce dossier</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find . -type d -print0 | xargs -0 setfacl -b
|
|||
|
|
|||
|
find . -type d -exec setfacl -b {} +
|
|||
|
|
|||
|
find . -type d -exec setfacl -b {} \;
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Supprimer des ACL sur un fichier/dossier
|
|||
|
A partir de la ligne de commande</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -x u:user,g:group fichiers|dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -x u:adminsys,g:adminsys file.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Récursivement : retirera toute ACL faisant référence à l’utilisateur ou au groupe spécifié (ne retournera pas d’erreur s’ils ne sont pas trouvés)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -Rx u:user,g:group dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>A partir d’un fichier de référence</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -X aclfilename fichiers|dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :<br>
|
|||
|
Avec listing.acl :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>u:adminsys
|
|||
|
g:adminsys
|
|||
|
|
|||
|
setfacl -X listing.acl file.txt
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Récursivement : retirera toute ACL faisant référence à l’utilisateur ou au groupe spécifié dans le fichier (ne retournera pas d’erreur s’ils ne sont pas trouvés)</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -RX aclfilename dossiers
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Restaurer les ACL d’une arborescence à partir d’un fichier de sauvegarde</p>
|
|||
|
|
|||
|
<p>L’emplacement de la restauration dépend du chemin spécifié lors de la sauvegarde.<br>
|
|||
|
Si les chemins sont relatifs à la racine, alors il faudra se placer à la racine pour restaurer les permissions et droits.<br>
|
|||
|
Si les chemins sont relatifs au dossier en question, alors il faudra se placer dans son répertoire parent.<br>
|
|||
|
Si les chemins sont absolus alors la restauration pourra se faire depuis n’importe quel dossier.<br>
|
|||
|
Cas d’un chemin relatif à un dossier :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: test/
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rwx
|
|||
|
group::r-x
|
|||
|
other::r-x
|
|||
|
|
|||
|
# file: test//file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --restore=filename
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --restore=/home/adminsys/backupacl.acl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Cas d’un chemin relatif à la racine :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: home/adminsys/test/
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rwx
|
|||
|
group::r-x
|
|||
|
other::r-x
|
|||
|
|
|||
|
# file: home/adminsys/test//file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(cd / ; setfacl --restore=filename)
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(cd / ; setfacl --restore=/home/adminsys/backupacl.acl)
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Cas d’un chemin absolu</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># file: /home/adminsys/test/
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rwx
|
|||
|
group::r-x
|
|||
|
other::r-x
|
|||
|
|
|||
|
# file: /home/adminsys/test//file.txt
|
|||
|
# owner: root
|
|||
|
# group: root
|
|||
|
user::rw-
|
|||
|
group::r--
|
|||
|
other::r--
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --restore=filename
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl --restore=/home/adminsys/backupacl.acl
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Définir des ACL par défaut sur un dossier</p>
|
|||
|
|
|||
|
<p>Tous les nouveaux fichiers créés dans ce dossier hériteront de ces ACL par défaut.</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m d:u:username:{rwx},d:g:groupname:{rwx},d:o::{rwx} directory/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exemple :</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setfacl -m d:u:adminsys:rwx,d:g:adminsys:rwx,d:o::rwx reptest/
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<h3 id="exemple-dossier-multimédia">Exemple Dossier multimédia</h3>
|
|||
|
|
|||
|
<p><em>Création dossier multimedia géré par les ACL</em></p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano /home/yani/creation_dossier_multimedia.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<details>
|
|||
|
<summary><b>Etendre Réduire</b></summary>
|
|||
|
|
|||
|
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">
|
|||
|
<span class="c">#!/bin/bash</span>
|
|||
|
|
|||
|
<span class="c"># Script de création des dossiers multimédia</span>
|
|||
|
|
|||
|
<span class="nv">GROUPE_MEDIA</span><span class="o">=</span>multimedia
|
|||
|
<span class="nv">DOSSIER_MEDIA</span><span class="o">=</span>/home/multimedia
|
|||
|
|
|||
|
<span class="c">## Création du groupe multimedia</span>
|
|||
|
<span class="nb">sudo </span>groupadd <span class="nt">-f</span> <span class="nv">$GROUPE_MEDIA</span>
|
|||
|
|
|||
|
<span class="c">## Création des dossiers génériques</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share/Music"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share/Picture"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share/Video"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share/eBook"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share/Divers"</span>
|
|||
|
|
|||
|
<span class="c">## Création des dossiers utilisateurs</span>
|
|||
|
<span class="k">while </span><span class="nb">read </span>user <span class="c">#USER en majuscule est une variable système, à éviter.</span>
|
|||
|
<span class="k">do
|
|||
|
</span><span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">/Music"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">/Picture"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">/Video"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">/eBook"</span>
|
|||
|
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">/Divers"</span>
|
|||
|
<span class="nb">sudo ln</span> <span class="nt">-sfn</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/share"</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">/Share"</span>
|
|||
|
<span class="c"># Création du lien symbolique dans le home de l'utilisateur.</span>
|
|||
|
<span class="nb">sudo ln</span> <span class="nt">-sfn</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">"</span> <span class="s2">"/home/</span><span class="nv">$user</span><span class="s2">/Multimedia"</span>
|
|||
|
<span class="c"># Propriétaires des dossiers utilisateurs.</span>
|
|||
|
<span class="nb">sudo chown</span> <span class="nt">-R</span> <span class="nv">$user</span> <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">/</span><span class="nv">$user</span><span class="s2">"</span>
|
|||
|
<span class="k">done</span> <span class="o"><<<</span> <span class="s2">"</span><span class="si">$(</span>less /etc/passwd|grep <span class="s2">"/bin/bash"</span> |awk <span class="nt">-F</span>: <span class="s1">'{ print $1}'</span> | <span class="nb">grep</span> <span class="nt">-Fv</span> <span class="s1">'root'</span><span class="si">)</span><span class="s2">"</span><span class="c"># Liste les utilisateurs avec exclusion de root</span>
|
|||
|
<span class="c"># Le triple chevron <<< permet de prendre la sortie de commande en entrée de boucle.</span>
|
|||
|
|
|||
|
<span class="c">## Application des droits étendus sur le dossier multimedia.</span>
|
|||
|
<span class="c"># Droit d'écriture pour le groupe et le groupe multimedia en acl et droit de lecture pour other:</span>
|
|||
|
<span class="nb">sudo </span>setfacl <span class="nt">-RnL</span> <span class="nt">-m</span> g:<span class="nv">$GROUPE_MEDIA</span>:rwX,g::rwX,o:r-X <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">"</span>
|
|||
|
<span class="c"># Application de la même règle que précédemment, mais par défaut pour les nouveaux fichiers.</span>
|
|||
|
<span class="nb">sudo </span>setfacl <span class="nt">-RnL</span> <span class="nt">-m</span> d:g:<span class="nv">$GROUPE_MEDIA</span>:rwX,g::rwX,o:r-X <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">"</span>
|
|||
|
<span class="c"># Réglage du masque par défaut. Qui garantie (en principe...) un droit maximal à rwx. Donc pas de restriction de droits par l'acl.</span>
|
|||
|
<span class="nb">sudo </span>setfacl <span class="nt">-RL</span> <span class="nt">-m</span> m::rwx <span class="s2">"</span><span class="nv">$DOSSIER_MEDIA</span><span class="s2">"</span></code></pre></figure>
|
|||
|
|
|||
|
</details>
|
|||
|
|
|||
|
<p>Droits en exécution</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod +x /home/yani/creation_dossier_multimedia.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Exécuter le script</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.//home/yani/creation_dossier_multimedia.sh
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajout utilisateur au groupe multimedia</p>
|
|||
|
|
|||
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo usermod -a -G multimedia $USER
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p>Ajout nextcloud au groupe multimedia</p>
|
|||
|
|
|||
|
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>usermod <span class="nt">-a</span> <span class="nt">-G</span> multimedia nextcloud
|
|||
|
</code></pre></div></div>
|
|||
|
|
|||
|
<p><code class="language-plaintext info highlighter-rouge">Se reconnecter pour la prise en compte</code></p>
|
|||
|
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<div class="d-print-none">
|
|||
|
<footer class="article__footer"><meta itemprop="dateModified" content="2020-11-12T00:00:00+01:00">
|
|||
|
<!-- start custom article footer snippet -->
|
|||
|
|
|||
|
<!-- end custom article footer snippet -->
|
|||
|
<!--
|
|||
|
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
|
|||
|
|
|||
|
 </div>
|
|||
|
-->
|
|||
|
</footer>
|
|||
|
<div class="article__section-navigator clearfix">
|
|||
|
<div class="previous">
|
|||
|
<span>PRÉCÉDENT</span><a href="/2020/11/11/Serveur_A20-OLinuXino-buster-minimal_Yunohost_xoyize.xyz.html">Serveur Debian A20-OLinuXino-buster-minimal Yunohost xoyize.xyz</a>
|
|||
|
</div>
|
|||
|
<div class="next">
|
|||
|
<span>SUIVANT</span><a href="/2020/11/14/KVM-QEMU-Network-Bridge-(Pont-reseau).html">KVM/QEMU Network Bridge (Pont réseau)</a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
$(function() {
|
|||
|
var $this ,$scroll;
|
|||
|
var $articleContent = $('.js-article-content');
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
|
|||
|
$scroll = $(scroll);
|
|||
|
|
|||
|
$articleContent.find('.highlight').each(function() {
|
|||
|
$this = $(this);
|
|||
|
$this.attr('data-lang', $this.find('code').attr('data-lang'));
|
|||
|
});
|
|||
|
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
|
|||
|
$this = $(this);
|
|||
|
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
|
|||
|
});
|
|||
|
$articleContent.on('click', '.anchor', function() {
|
|||
|
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
|
|||
|
});
|
|||
|
});
|
|||
|
});
|
|||
|
})();
|
|||
|
</script>
|
|||
|
|
|||
|
</div>
|
|||
|
<section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
|
|||
|
<!-- end custom main bottom snippet -->
|
|||
|
</div>
|
|||
|
</div></div></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $body = $('body'), $window = $(window);
|
|||
|
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
|
|||
|
var activeCount = 0;
|
|||
|
function modal(options) {
|
|||
|
var $root = this, visible, onChange, hideWhenWindowScroll = false;
|
|||
|
var scrollTop;
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
visible = _options.initialVisible === undefined ? false : show;
|
|||
|
onChange = _options.onChange;
|
|||
|
hideWhenWindowScroll = _options.hideWhenWindowScroll;
|
|||
|
}
|
|||
|
function init() {
|
|||
|
setState(visible);
|
|||
|
}
|
|||
|
function setState(isShow) {
|
|||
|
if (isShow === visible) {
|
|||
|
return;
|
|||
|
}
|
|||
|
visible = isShow;
|
|||
|
if (visible) {
|
|||
|
activeCount++;
|
|||
|
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
|
|||
|
$root.addClass('modal--show');
|
|||
|
$pageMain.scrollTop(scrollTop);
|
|||
|
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
|
|||
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
|
|||
|
$window.on('keyup', handleKeyup);
|
|||
|
} else {
|
|||
|
activeCount > 0 && activeCount--;
|
|||
|
$root.removeClass('modal--show');
|
|||
|
$window.scrollTop(scrollTop);
|
|||
|
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
|
|||
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
|
|||
|
$window.off('keyup', handleKeyup);
|
|||
|
}
|
|||
|
onChange && onChange(visible);
|
|||
|
}
|
|||
|
function show() {
|
|||
|
setState(true);
|
|||
|
}
|
|||
|
function hide() {
|
|||
|
setState(false);
|
|||
|
}
|
|||
|
function handleKeyup(e) {
|
|||
|
// Char Code: 27 ESC
|
|||
|
if (e.which === 27) {
|
|||
|
hide();
|
|||
|
}
|
|||
|
}
|
|||
|
setOptions(options);
|
|||
|
init();
|
|||
|
return {
|
|||
|
show: show,
|
|||
|
hide: hide,
|
|||
|
$el: $root
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.modal = modal;
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal">
|
|||
|
<script>
|
|||
|
(function () {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
// search panel
|
|||
|
var search = (window.search || (window.search = {}));
|
|||
|
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
|
|||
|
true : window.useDefaultSearchBox ;
|
|||
|
|
|||
|
var $searchModal = $('.js-page-search-modal');
|
|||
|
var $searchToggle = $('.js-search-toggle');
|
|||
|
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
|
|||
|
var modalVisible = false;
|
|||
|
search.searchModal = searchModal;
|
|||
|
|
|||
|
var $searchBox = null;
|
|||
|
var $searchInput = null;
|
|||
|
var $searchClear = null;
|
|||
|
|
|||
|
function getModalVisible() {
|
|||
|
return modalVisible;
|
|||
|
}
|
|||
|
search.getModalVisible = getModalVisible;
|
|||
|
|
|||
|
function handleModalChange(visible) {
|
|||
|
modalVisible = visible;
|
|||
|
if (visible) {
|
|||
|
search.onShow && search.onShow();
|
|||
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
|
|||
|
} else {
|
|||
|
search.onShow && search.onHide();
|
|||
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
|
|||
|
setTimeout(function() {
|
|||
|
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
|
|||
|
search.clear && search.clear();
|
|||
|
window.pageAsideAffix && window.pageAsideAffix.refresh();
|
|||
|
}, 400);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$searchToggle.on('click', function() {
|
|||
|
modalVisible ? searchModal.hide() : searchModal.show();
|
|||
|
});
|
|||
|
// Char Code: 83 S, 191 /
|
|||
|
$(window).on('keyup', function(e) {
|
|||
|
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
|
|||
|
modalVisible || searchModal.show();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
if (useDefaultSearchBox) {
|
|||
|
$searchBox = $('.js-search-box');
|
|||
|
$searchInput = $searchBox.children('input');
|
|||
|
$searchClear = $searchBox.children('.js-icon-clear');
|
|||
|
search.getSearchInput = function() {
|
|||
|
return $searchInput.get(0);
|
|||
|
};
|
|||
|
search.getVal = function() {
|
|||
|
return $searchInput.val();
|
|||
|
};
|
|||
|
search.setVal = function(val) {
|
|||
|
$searchInput.val(val);
|
|||
|
};
|
|||
|
|
|||
|
$searchInput.on('focus', function() {
|
|||
|
$(this).addClass('focus');
|
|||
|
});
|
|||
|
$searchInput.on('blur', function() {
|
|||
|
$(this).removeClass('focus');
|
|||
|
});
|
|||
|
$searchInput.on('input', window.throttle(function() {
|
|||
|
var val = $(this).val();
|
|||
|
if (val === '' || typeof val !== 'string') {
|
|||
|
search.clear && search.clear();
|
|||
|
} else {
|
|||
|
$searchBox.addClass('not-empty');
|
|||
|
search.onInputNotEmpty && search.onInputNotEmpty(val);
|
|||
|
}
|
|||
|
}, 400));
|
|||
|
$searchClear.on('click', function() {
|
|||
|
$searchInput.val(''); $searchBox.removeClass('not-empty');
|
|||
|
search.clear && search.clear();
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><div class="search search--dark">
|
|||
|
<div class="main">
|
|||
|
<div class="search__header">Recherche</div>
|
|||
|
<div class="search-bar">
|
|||
|
<div class="search-box js-search-box">
|
|||
|
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
|
|||
|
<input id="search-input" type="text">
|
|||
|
<div class="search-box__icon-clear js-icon-clear">
|
|||
|
<a><i class="fas fa-times"></i></a>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
|
|||
|
Annuler</button>
|
|||
|
</div>
|
|||
|
<div id="results-container" class="search-result js-search-result"></div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<!-- Script pointing to search-script.js -->
|
|||
|
<script>/*!
|
|||
|
* Simple-Jekyll-Search
|
|||
|
* Copyright 2015-2020, Christian Fei
|
|||
|
* Licensed under the MIT License.
|
|||
|
*/
|
|||
|
|
|||
|
(function(){
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Templater_7 = {
|
|||
|
compile: compile,
|
|||
|
setOptions: setOptions
|
|||
|
}
|
|||
|
|
|||
|
const options = {}
|
|||
|
options.pattern = /\{(.*?)\}/g
|
|||
|
options.template = ''
|
|||
|
options.middleware = function () {}
|
|||
|
|
|||
|
function setOptions (_options) {
|
|||
|
options.pattern = _options.pattern || options.pattern
|
|||
|
options.template = _options.template || options.template
|
|||
|
if (typeof _options.middleware === 'function') {
|
|||
|
options.middleware = _options.middleware
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function compile (data) {
|
|||
|
return options.template.replace(options.pattern, function (match, prop) {
|
|||
|
const value = options.middleware(prop, data[prop], options.template)
|
|||
|
if (typeof value !== 'undefined') {
|
|||
|
return value
|
|||
|
}
|
|||
|
return data[prop] || match
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
'use strict';
|
|||
|
|
|||
|
function fuzzysearch (needle, haystack) {
|
|||
|
var tlen = haystack.length;
|
|||
|
var qlen = needle.length;
|
|||
|
if (qlen > tlen) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
if (qlen === tlen) {
|
|||
|
return needle === haystack;
|
|||
|
}
|
|||
|
outer: for (var i = 0, j = 0; i < qlen; i++) {
|
|||
|
var nch = needle.charCodeAt(i);
|
|||
|
while (j < tlen) {
|
|||
|
if (haystack.charCodeAt(j++) === nch) {
|
|||
|
continue outer;
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
var _$fuzzysearch_1 = fuzzysearch;
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
|
|||
|
|
|||
|
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
|
|||
|
|
|||
|
function FuzzySearchStrategy () {
|
|||
|
this.matches = function (string, crit) {
|
|||
|
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
|
|||
|
|
|||
|
function LiteralSearchStrategy () {
|
|||
|
this.matches = function (str, crit) {
|
|||
|
if (!str) return false
|
|||
|
|
|||
|
str = str.trim().toLowerCase()
|
|||
|
crit = crit.trim().toLowerCase()
|
|||
|
|
|||
|
return crit.split(' ').filter(function (word) {
|
|||
|
return str.indexOf(word) >= 0
|
|||
|
}).length === crit.split(' ').length
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$Repository_4 = {
|
|||
|
put: put,
|
|||
|
clear: clear,
|
|||
|
search: search,
|
|||
|
setOptions: __setOptions_4
|
|||
|
}
|
|||
|
|
|||
|
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
|
|||
|
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
|
|||
|
|
|||
|
function NoSort () {
|
|||
|
return 0
|
|||
|
}
|
|||
|
|
|||
|
const data = []
|
|||
|
let opt = {}
|
|||
|
|
|||
|
opt.fuzzy = false
|
|||
|
opt.limit = 10
|
|||
|
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = NoSort
|
|||
|
opt.exclude = []
|
|||
|
|
|||
|
function put (data) {
|
|||
|
if (isObject(data)) {
|
|||
|
return addObject(data)
|
|||
|
}
|
|||
|
if (isArray(data)) {
|
|||
|
return addArray(data)
|
|||
|
}
|
|||
|
return undefined
|
|||
|
}
|
|||
|
function clear () {
|
|||
|
data.length = 0
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function isObject (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
|
|||
|
}
|
|||
|
|
|||
|
function isArray (obj) {
|
|||
|
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
|
|||
|
}
|
|||
|
|
|||
|
function addObject (_data) {
|
|||
|
data.push(_data)
|
|||
|
return data
|
|||
|
}
|
|||
|
|
|||
|
function addArray (_data) {
|
|||
|
const added = []
|
|||
|
clear()
|
|||
|
for (let i = 0, len = _data.length; i < len; i++) {
|
|||
|
if (isObject(_data[i])) {
|
|||
|
added.push(addObject(_data[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
return added
|
|||
|
}
|
|||
|
|
|||
|
function search (crit) {
|
|||
|
if (!crit) {
|
|||
|
return []
|
|||
|
}
|
|||
|
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
|
|||
|
}
|
|||
|
|
|||
|
function __setOptions_4 (_opt) {
|
|||
|
opt = _opt || {}
|
|||
|
|
|||
|
opt.fuzzy = _opt.fuzzy || false
|
|||
|
opt.limit = _opt.limit || 10
|
|||
|
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
|
|||
|
opt.sort = _opt.sort || NoSort
|
|||
|
opt.exclude = _opt.exclude || []
|
|||
|
}
|
|||
|
|
|||
|
function findMatches (data, crit, strategy, opt) {
|
|||
|
const matches = []
|
|||
|
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
|
|||
|
const match = findMatchesInObject(data[i], crit, strategy, opt)
|
|||
|
if (match) {
|
|||
|
matches.push(match)
|
|||
|
}
|
|||
|
}
|
|||
|
return matches
|
|||
|
}
|
|||
|
|
|||
|
function findMatchesInObject (obj, crit, strategy, opt) {
|
|||
|
for (const key in obj) {
|
|||
|
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
|
|||
|
return obj
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isExcluded (term, excludedTerms) {
|
|||
|
for (let i = 0, len = excludedTerms.length; i < len; i++) {
|
|||
|
const excludedTerm = excludedTerms[i]
|
|||
|
if (new RegExp(excludedTerm).test(term)) {
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
/* globals ActiveXObject:false */
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$JSONLoader_2 = {
|
|||
|
load: load
|
|||
|
}
|
|||
|
|
|||
|
function load (location, callback) {
|
|||
|
const xhr = getXHR()
|
|||
|
xhr.open('GET', location, true)
|
|||
|
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
|
|||
|
xhr.send()
|
|||
|
}
|
|||
|
|
|||
|
function createStateChangeListener (xhr, callback) {
|
|||
|
return function () {
|
|||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|||
|
try {
|
|||
|
callback(null, JSON.parse(xhr.responseText))
|
|||
|
} catch (err) {
|
|||
|
callback(err, null)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function getXHR () {
|
|||
|
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$OptionsValidator_3 = function OptionsValidator (params) {
|
|||
|
if (!validateParams(params)) {
|
|||
|
throw new Error('-- OptionsValidator: required options missing')
|
|||
|
}
|
|||
|
|
|||
|
if (!(this instanceof OptionsValidator)) {
|
|||
|
return new OptionsValidator(params)
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = params.required
|
|||
|
|
|||
|
this.getRequiredOptions = function () {
|
|||
|
return requiredOptions
|
|||
|
}
|
|||
|
|
|||
|
this.validate = function (parameters) {
|
|||
|
const errors = []
|
|||
|
requiredOptions.forEach(function (requiredOptionName) {
|
|||
|
if (typeof parameters[requiredOptionName] === 'undefined') {
|
|||
|
errors.push(requiredOptionName)
|
|||
|
}
|
|||
|
})
|
|||
|
return errors
|
|||
|
}
|
|||
|
|
|||
|
function validateParams (params) {
|
|||
|
if (!params) {
|
|||
|
return false
|
|||
|
}
|
|||
|
return typeof params.required !== 'undefined' && params.required instanceof Array
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
'use strict'
|
|||
|
|
|||
|
var _$utils_9 = {
|
|||
|
merge: merge,
|
|||
|
isJSON: isJSON
|
|||
|
}
|
|||
|
|
|||
|
function merge (defaultParams, mergeParams) {
|
|||
|
const mergedOptions = {}
|
|||
|
for (const option in defaultParams) {
|
|||
|
mergedOptions[option] = defaultParams[option]
|
|||
|
if (typeof mergeParams[option] !== 'undefined') {
|
|||
|
mergedOptions[option] = mergeParams[option]
|
|||
|
}
|
|||
|
}
|
|||
|
return mergedOptions
|
|||
|
}
|
|||
|
|
|||
|
function isJSON (json) {
|
|||
|
try {
|
|||
|
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
|
|||
|
return true
|
|||
|
}
|
|||
|
return false
|
|||
|
} catch (err) {
|
|||
|
return false
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var _$src_8 = {};
|
|||
|
(function (window) {
|
|||
|
'use strict'
|
|||
|
|
|||
|
let options = {
|
|||
|
searchInput: null,
|
|||
|
resultsContainer: null,
|
|||
|
json: [],
|
|||
|
success: Function.prototype,
|
|||
|
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
|
|||
|
templateMiddleware: Function.prototype,
|
|||
|
sortMiddleware: function () {
|
|||
|
return 0
|
|||
|
},
|
|||
|
noResultsText: 'No results found',
|
|||
|
limit: 10,
|
|||
|
fuzzy: false,
|
|||
|
debounceTime: null,
|
|||
|
exclude: []
|
|||
|
}
|
|||
|
|
|||
|
let debounceTimerHandle
|
|||
|
const debounce = function (func, delayMillis) {
|
|||
|
if (delayMillis) {
|
|||
|
clearTimeout(debounceTimerHandle)
|
|||
|
debounceTimerHandle = setTimeout(func, delayMillis)
|
|||
|
} else {
|
|||
|
func.call()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
|
|||
|
|
|||
|
/* removed: const _$Templater_7 = require('./Templater') */;
|
|||
|
/* removed: const _$Repository_4 = require('./Repository') */;
|
|||
|
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
|
|||
|
const optionsValidator = _$OptionsValidator_3({
|
|||
|
required: requiredOptions
|
|||
|
})
|
|||
|
/* removed: const _$utils_9 = require('./utils') */;
|
|||
|
|
|||
|
window.SimpleJekyllSearch = function (_options) {
|
|||
|
const errors = optionsValidator.validate(_options)
|
|||
|
if (errors.length > 0) {
|
|||
|
throwError('You must specify the following required options: ' + requiredOptions)
|
|||
|
}
|
|||
|
|
|||
|
options = _$utils_9.merge(options, _options)
|
|||
|
|
|||
|
_$Templater_7.setOptions({
|
|||
|
template: options.searchResultTemplate,
|
|||
|
middleware: options.templateMiddleware
|
|||
|
})
|
|||
|
|
|||
|
_$Repository_4.setOptions({
|
|||
|
fuzzy: options.fuzzy,
|
|||
|
limit: options.limit,
|
|||
|
sort: options.sortMiddleware,
|
|||
|
exclude: options.exclude
|
|||
|
})
|
|||
|
|
|||
|
if (_$utils_9.isJSON(options.json)) {
|
|||
|
initWithJSON(options.json)
|
|||
|
} else {
|
|||
|
initWithURL(options.json)
|
|||
|
}
|
|||
|
|
|||
|
const rv = {
|
|||
|
search: search
|
|||
|
}
|
|||
|
|
|||
|
typeof options.success === 'function' && options.success.call(rv)
|
|||
|
return rv
|
|||
|
}
|
|||
|
|
|||
|
function initWithJSON (json) {
|
|||
|
_$Repository_4.put(json)
|
|||
|
registerInput()
|
|||
|
}
|
|||
|
|
|||
|
function initWithURL (url) {
|
|||
|
_$JSONLoader_2.load(url, function (err, json) {
|
|||
|
if (err) {
|
|||
|
throwError('failed to get JSON (' + url + ')')
|
|||
|
}
|
|||
|
initWithJSON(json)
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function emptyResultsContainer () {
|
|||
|
options.resultsContainer.innerHTML = ''
|
|||
|
}
|
|||
|
|
|||
|
function appendToResultsContainer (text) {
|
|||
|
options.resultsContainer.innerHTML += text
|
|||
|
}
|
|||
|
|
|||
|
function registerInput () {
|
|||
|
options.searchInput.addEventListener('input', function (e) {
|
|||
|
if (isWhitelistedKey(e.which)) {
|
|||
|
emptyResultsContainer()
|
|||
|
debounce(function () { search(e.target.value) }, options.debounceTime)
|
|||
|
}
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
function search (query) {
|
|||
|
if (isValidQuery(query)) {
|
|||
|
emptyResultsContainer()
|
|||
|
render(_$Repository_4.search(query), query)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function render (results, query) {
|
|||
|
const len = results.length
|
|||
|
if (len === 0) {
|
|||
|
return appendToResultsContainer(options.noResultsText)
|
|||
|
}
|
|||
|
for (let i = 0; i < len; i++) {
|
|||
|
results[i].query = query
|
|||
|
appendToResultsContainer(_$Templater_7.compile(results[i]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function isValidQuery (query) {
|
|||
|
return query && query.length > 0
|
|||
|
}
|
|||
|
|
|||
|
function isWhitelistedKey (key) {
|
|||
|
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
|
|||
|
}
|
|||
|
|
|||
|
function throwError (message) {
|
|||
|
throw new Error('SimpleJekyllSearch --- ' + message)
|
|||
|
}
|
|||
|
})(window)
|
|||
|
|
|||
|
}());
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Configuration -->
|
|||
|
<script>
|
|||
|
SimpleJekyllSearch({
|
|||
|
searchInput: document.getElementById('search-input'),
|
|||
|
resultsContainer: document.getElementById('results-container'),
|
|||
|
noResultsText: '<p>Aucun résultat!</p>',
|
|||
|
json: '/search.json',
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {title}</a> (Création {create})</li>'
|
|||
|
})
|
|||
|
</script>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<script>(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function scrollToAnchor(anchor, duration, callback) {
|
|||
|
var $root = this;
|
|||
|
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
|
|||
|
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
|
|||
|
callback && callback();
|
|||
|
});
|
|||
|
}
|
|||
|
$.fn.scrollToAnchor = scrollToAnchor;
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function affix(options) {
|
|||
|
var $root = this, $window = $(window), $scrollTarget, $scroll,
|
|||
|
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
|
|||
|
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
|
|||
|
hasInit = false, curState;
|
|||
|
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
|
|||
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
|
|||
|
_options.scroll && (scroll = _options.scroll);
|
|||
|
_options.disabled !== undefined && (disabled = _options.disabled);
|
|||
|
$scrollTarget = $(scrollTarget);
|
|||
|
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
|
|||
|
$scroll = $(scroll);
|
|||
|
}
|
|||
|
function preCalc() {
|
|||
|
top();
|
|||
|
rootHeight = $root.outerHeight();
|
|||
|
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
|
|||
|
rootLeft = $root.offset().left;
|
|||
|
}
|
|||
|
function calc(needPreCalc) {
|
|||
|
needPreCalc && preCalc();
|
|||
|
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
|
|||
|
rootBottomTop = scrollBottom - rootTop;
|
|||
|
}
|
|||
|
function top() {
|
|||
|
if (curState !== 'top') {
|
|||
|
$root.removeClass('fixed').css({
|
|||
|
left: 0,
|
|||
|
top: 0
|
|||
|
});
|
|||
|
curState = 'top';
|
|||
|
}
|
|||
|
}
|
|||
|
function fixed() {
|
|||
|
if (curState !== 'fixed') {
|
|||
|
$root.addClass('fixed').css({
|
|||
|
left: rootLeft + 'px',
|
|||
|
top: 0
|
|||
|
});
|
|||
|
curState = 'fixed';
|
|||
|
}
|
|||
|
}
|
|||
|
function bottom() {
|
|||
|
if (curState !== 'bottom') {
|
|||
|
$root.removeClass('fixed').css({
|
|||
|
left: 0,
|
|||
|
top: rootBottomTop + 'px'
|
|||
|
});
|
|||
|
curState = 'bottom';
|
|||
|
}
|
|||
|
}
|
|||
|
function setState() {
|
|||
|
var scrollTop = $scrollTarget.scrollTop();
|
|||
|
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
|
|||
|
fixed();
|
|||
|
} else if (scrollTop < rootTop) {
|
|||
|
top();
|
|||
|
} else {
|
|||
|
bottom();
|
|||
|
}
|
|||
|
}
|
|||
|
function init() {
|
|||
|
if(!hasInit) {
|
|||
|
var interval, timeout;
|
|||
|
calc(true); setState();
|
|||
|
// run calc every 100 millisecond
|
|||
|
interval = setInterval(function() {
|
|||
|
calc();
|
|||
|
}, 100);
|
|||
|
timeout = setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
}, 45000);
|
|||
|
window.pageLoad.then(function() {
|
|||
|
setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
clearTimeout(timeout);
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
$scrollTarget.on('scroll', function() {
|
|||
|
disabled || setState();
|
|||
|
});
|
|||
|
$window.on('resize', function() {
|
|||
|
disabled || (calc(true), setState());
|
|||
|
});
|
|||
|
hasInit = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
setOptions(options);
|
|||
|
if (!disabled) {
|
|||
|
init();
|
|||
|
}
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
init();
|
|||
|
}, 200));
|
|||
|
return {
|
|||
|
setOptions: setOptions,
|
|||
|
refresh: function() {
|
|||
|
calc(true, { animation: false }); setState();
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.affix = affix;
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
function toc(options) {
|
|||
|
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
|
|||
|
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
|
|||
|
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
|
|||
|
|
|||
|
function setOptions(options) {
|
|||
|
var _options = options || {};
|
|||
|
_options.selectors && (selectors = _options.selectors);
|
|||
|
_options.container && (container = _options.container);
|
|||
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
|
|||
|
_options.scroller && (scroller = _options.scroller);
|
|||
|
_options.disabled !== undefined && (disabled = _options.disabled);
|
|||
|
$headings = $(container).find(selectors).filter('[id]');
|
|||
|
$scrollTarget = $(scrollTarget);
|
|||
|
$scroller = $(scroller);
|
|||
|
}
|
|||
|
function calc() {
|
|||
|
headingsPos = [];
|
|||
|
$headings.each(function() {
|
|||
|
headingsPos.push(Math.floor($(this).position().top));
|
|||
|
});
|
|||
|
}
|
|||
|
function setState(element, disabled) {
|
|||
|
var scrollTop = $scrollTarget.scrollTop(), i;
|
|||
|
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
|
|||
|
if (element) {
|
|||
|
$activeCur = element;
|
|||
|
} else {
|
|||
|
for (i = 0; i < headingsPos.length; i++) {
|
|||
|
if (scrollTop >= headingsPos[i]) {
|
|||
|
$activeCur = $tocLi.eq(i);
|
|||
|
} else {
|
|||
|
$activeCur || ($activeCur = $tocLi.eq(i));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$activeLast && $activeLast.removeClass('active');
|
|||
|
($activeLast = $activeCur).addClass('active');
|
|||
|
}
|
|||
|
function render() {
|
|||
|
if(!hasRendered) {
|
|||
|
$root.append($tocUl);
|
|||
|
$headings.each(function() {
|
|||
|
var $this = $(this);
|
|||
|
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
|
|||
|
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
|
|||
|
});
|
|||
|
$tocLi = $tocUl.children('li');
|
|||
|
$tocUl.on('click', 'a', function(e) {
|
|||
|
e.preventDefault();
|
|||
|
var $this = $(this);
|
|||
|
scrolling = true;
|
|||
|
setState($this.parent());
|
|||
|
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
|
|||
|
scrolling = false;
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
hasRendered = true;
|
|||
|
}
|
|||
|
function init() {
|
|||
|
var interval, timeout;
|
|||
|
if(!hasInit) {
|
|||
|
render(); calc(); setState(null, scrolling);
|
|||
|
// run calc every 100 millisecond
|
|||
|
interval = setInterval(function() {
|
|||
|
calc();
|
|||
|
}, 100);
|
|||
|
timeout = setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
}, 45000);
|
|||
|
window.pageLoad.then(function() {
|
|||
|
setTimeout(function() {
|
|||
|
clearInterval(interval);
|
|||
|
clearTimeout(timeout);
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
$scrollTarget.on('scroll', function() {
|
|||
|
disabled || setState(null, scrolling);
|
|||
|
});
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
if (!disabled) {
|
|||
|
render(); calc(); setState(null, scrolling);
|
|||
|
}
|
|||
|
}, 100));
|
|||
|
}
|
|||
|
hasInit = true;
|
|||
|
}
|
|||
|
|
|||
|
setOptions(options);
|
|||
|
if (!disabled) {
|
|||
|
init();
|
|||
|
}
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
init();
|
|||
|
}, 200));
|
|||
|
return {
|
|||
|
setOptions: setOptions
|
|||
|
};
|
|||
|
}
|
|||
|
$.fn.toc = toc;
|
|||
|
});
|
|||
|
})();
|
|||
|
/*(function () {
|
|||
|
|
|||
|
})();*/
|
|||
|
</script><script>
|
|||
|
/* toc must before affix, since affix need to konw toc' height. */(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $window = $(window);
|
|||
|
var $articleContent = $('.js-article-content');
|
|||
|
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
|
|||
|
var toc;
|
|||
|
var tocDisabled = false;
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
|
|||
|
|
|||
|
function disabled() {
|
|||
|
return $col2.css('display') === 'none' || !hasToc;
|
|||
|
}
|
|||
|
|
|||
|
tocDisabled = disabled();
|
|||
|
|
|||
|
toc = $tocRoot.toc({
|
|||
|
selectors: TOC_SELECTOR,
|
|||
|
container: $articleContent,
|
|||
|
scrollTarget: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroller: hasSidebar ? '.js-page-main' : null,
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
tocDisabled = disabled();
|
|||
|
toc && toc.setOptions({
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
}, 100));
|
|||
|
|
|||
|
});
|
|||
|
})();
|
|||
|
(function() {
|
|||
|
var SOURCES = window.TEXT_VARIABLES.sources;
|
|||
|
window.Lazyload.js(SOURCES.jquery, function() {
|
|||
|
var $window = $(window), $pageFooter = $('.js-page-footer');
|
|||
|
var $pageAside = $('.js-page-aside');
|
|||
|
var affix;
|
|||
|
var tocDisabled = false;
|
|||
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
|
|||
|
|
|||
|
affix = $pageAside.affix({
|
|||
|
offsetBottom: $pageFooter.outerHeight(),
|
|||
|
scrollTarget: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroller: hasSidebar ? '.js-page-main' : null,
|
|||
|
scroll: hasSidebar ? $('.js-page-main').children() : null,
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
|
|||
|
$window.on('resize', window.throttle(function() {
|
|||
|
affix && affix.setOptions({
|
|||
|
disabled: tocDisabled
|
|||
|
});
|
|||
|
}, 100));
|
|||
|
|
|||
|
window.pageAsideAffix = affix;
|
|||
|
});
|
|||
|
})();
|
|||
|
</script><!---->
|
|||
|
|
|||
|
</div>
|
|||
|
<script>(function () {
|
|||
|
var $root = document.getElementsByClassName('root')[0];
|
|||
|
if (window.hasEvent('touchstart')) {
|
|||
|
$root.dataset.isTouch = true;
|
|||
|
document.addEventListener('touchstart', function(){}, false);
|
|||
|
}
|
|||
|
})();
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
|