yannstatic/static/2019/10/08/SSO-module-Nginx-'auth_request'.html

2492 lines
239 KiB
HTML
Raw Permalink Normal View History

2024-10-31 20:18:37 +01:00
<!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>Guide et SSO avec le module Nginx 'auth_request' - YannStatic</title>
<meta name="description" content="Le guide “auth_request_module” de nginx">
<link rel="canonical" href="https://static.rnmkcy.eu/2019/10/08/SSO-module-Nginx-'auth_request'.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.5
" 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}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{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;">Guide et SSO avec le module Nginx 'auth_request'</h1></header></div><meta itemprop="headline" content="Guide et SSO avec le module Nginx 'auth_request'"><div class="article__info clearfix"><ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=nginx">nginx</a>
2024-10-31 20:18:37 +01:00
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">&nbsp;8&nbsp;oct.&nbsp;&nbsp;2019</span></li></ul></div><meta itemprop="datePublished" content="2019-10-08T00:00:00+02:00">
<meta itemprop="keywords" content="nginx"><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">&#8679;</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><h1 id="le-guide-auth_request_module-de-nginx">Le guide “auth_request_module” de nginx</h1>
<p><a href="https://www.0ink.net/2019/05/10/nginx_mod_authrequest.html">nginxs auth_request_module howto</a></p>
<p>Cet article tente de compléter les documentations nginx concernant le module auth_request et comment le configurer . À mon avis, cette documentation est un peu incomplète.</p>
<h2 id="quel-est-le-module-auth_request-de-nginx">Quel est le module auth_request de nginx</h2>
<p>La documentation de ce module indique quelle implémente lautorisation du client en fonction du résultat dune sous-demande.</p>
<p>Cela signifie que lorsque vous envoyez une requête HTTP à une URL protégée, nginx effectue une sous-demande interne sur une URL dautorisation définie.</p>
<ul>
<li>Si le résultat de la sous-demande est HTTP 2xx, nginx envoie par proxy la requête HTTP dorigine au serveur principal.</li>
<li>Si le résultat de la sous-demande est HTTP 401 ou 403, laccès au serveur principal est refusé.</li>
</ul>
<p>En configurant nginx , vous pouvez rediriger ces 401 ou 403 vers une page de connexion où lutilisateur est authentifié puis redirigé vers la destination dorigine.</p>
<p>Lensemble du processus de sous-demande dautorisation est ensuite répété, mais comme lutilisateur est maintenant authentifié, la sous-demande renvoie HTTP 200 et la requête HTTP dorigine est envoyée par proxy au serveur principal.</p>
<h2 id="configuration-de-nginx">Configuration de nginx</h2>
<p>Dans votre configuration nginx …</p>
<p>Ce bloc configure la zone du serveur Web qui sera protégée:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location /hello {
error_page 401 = @error401; # Specific login page to use
auth_request /auth; # The sub-request to use
auth_request_set $username $upstream_http_x_username; # Make the sub request data available
auth_request_set $sid $upstream_http_x_session; # send what is needed
proxy_pass http://sample.com:8080/hello; # actual location of protected data
proxy_set_header X-Forwarded-Host $host; # Custom headers with authentication related data
proxy_set_header X-Remote-User $username;
proxy_set_header X-Remote-SID $sid;
}
</code></pre></div></div>
<ul>
<li><code class="language-plaintext highlighter-rouge">error_page 401</code> définit la page de connexion personnalisée à utiliser (le cas échéant). En théorie, pour une API REST, il serait possible de sauthentifier à laide dun en-tête de <strong>token</strong> fourni, ce qui rend la page de connexion inutile.</li>
<li><code class="language-plaintext highlighter-rouge">auth_request /auth</code> définit que cet emplacement nécessite une authentification et définit lemplacement de la sous-requête à utiliser.</li>
<li><code class="language-plaintext highlighter-rouge">auth_request_set</code> peut être utilisé pour extraire des données des en-têtes de sous-demande et les rendre disponibles ultérieurement (par exemple, à un serveur de contenu principal utilisant des en-têtes personnalisés.</li>
<li><code class="language-plaintext highlighter-rouge">proxy_pass</code> définit le serveur de contenu principal.</li>
<li><code class="language-plaintext highlighter-rouge">proxy_set_header</code> définit un en-tête personnalisé utilisé pour transmettre des informations au serveur backend de contenu.</li>
</ul>
<p>Ce bloc configure le serveur de sous-requête dauthentification:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location = /auth {
proxy_pass http://auth-server.sample.com:8080/auth; # authentication server
proxy_pass_request_body off; # no data is being transferred...
proxy_set_header Content-Length '0';
proxy_set_header Host $host; # Custom headers with authentication related data
proxy_set_header X-Origin-URI $request_uri;
proxy_set_header X-Forwarded-Host $host;
}
</code></pre></div></div>
<ul>
<li><code class="language-plaintext highlighter-rouge">proxy_pass</code> où la sous-requête doit être traitée.</li>
<li><code class="language-plaintext highlighter-rouge">proxy_pass_request_body</code> off et <code class="language-plaintext highlighter-rouge">proxy_set_header Content-Length 0</code> permettent de supprimer le corps du contenu et denvoyer uniquement les en-têtes au serveur dauthentification.</li>
<li><code class="language-plaintext highlighter-rouge">proxy_set_header</code> détails supplémentaires envoyés à la sous-requête. Par exemple, l <code class="language-plaintext highlighter-rouge">X-Origin-URI</code></li>
</ul>
<p>Ceci implémente lURL du pager de connexion</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> # If the user is not logged in, redirect them to login URL
location @error401 {
return 302 https://$host/login/?url=https://$http_host$request_uri;
}
</code></pre></div></div>
<p>Dans cet exemple, la page de connexion se trouve sur le même proxy inverse, mais ce nest pas nécessairement le cas.</p>
<p>La page de connexion actuelle:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location /login/ {
proxy_pass http://auth-server.sample.com:8080/login/; # Where the login happens
proxy_set_header X-My-Real-IP $remote_addr; # Additional parameters to send to login page
proxy_set_header X-My-Real-Port $remote_port;
proxy_set_header X-My-Server-Port $server_port;
}
</code></pre></div></div>
<ul>
<li><code class="language-plaintext highlighter-rouge">proxy_pass</code> pointe sur lemplacement du script de connexion</li>
<li><code class="language-plaintext highlighter-rouge">proxy_set_header</code> peut être utilisé pour transmettre des champs supplémentaires pouvant être requis par le script de connexion. Pour implémenter, par exemple, des règles daccès basées sur <code class="language-plaintext highlighter-rouge">$remote_addr</code></li>
</ul>
<p>Donc, dans cet exemple particulier, nous faisons référence à un serveur avec DEUX emplacements:</p>
<ol>
<li>http://auth-server.sample.com:8080/auth - LURI de sous-requête qui nest pas visible à lextérieur mais qui gère la sous-requête.</li>
<li>http://auth-server.sample.com:8080/login/ - LURI de connexion qui gère la conversation de connexion.</li>
</ol>
<h2 id="le-serveur-dauthentification">Le serveur dauthentification</h2>
<p>Cest là que la documentation de nginx manque un peu. Il nya pas dexemple de serveur dauthentification à utiliser.</p>
<p>Dans mon exemple, nous avons un workflow dauthentification simple. Lorsquun utilisateur non authentifié rencontre le serveur, la sous-requête est appelée et recherche (et échoue) la présence dun cookie de session.</p>
<p>Lutilisateur est ensuite redirigé vers la page de connexion, où la connexion réelle a lieu. En cas de succès, un cookie de session est défini et lutilisateur est redirigé vers lURL dorigine.</p>
<p>Ceci est implémenté en utilisant le script suivant:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#/usr/bin/env python
</span><span class="kn">from</span> <span class="n">bottle</span> <span class="kn">import</span> <span class="n">route</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">abort</span><span class="p">,</span> <span class="n">redirect</span>
<span class="kn">import</span> <span class="n">sys</span>
<span class="kn">import</span> <span class="n">uuid</span>
<span class="n">SIGNATURE</span> <span class="o">=</span> <span class="n">uuid</span><span class="p">.</span><span class="nf">uuid4</span><span class="p">()</span>
<span class="n">COOKIE</span> <span class="o">=</span> <span class="sh">'</span><span class="s">demo-cookie</span><span class="sh">'</span>
<span class="n">sessions</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">def</span> <span class="nf">check_login</span><span class="p">(</span><span class="n">username</span><span class="p">,</span><span class="n">password</span><span class="p">):</span>
<span class="k">if</span> <span class="n">username</span> <span class="o">==</span> <span class="n">password</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="k">def</span> <span class="nf">is_active_session</span><span class="p">():</span>
<span class="n">sid</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="nf">get_cookie</span><span class="p">(</span><span class="n">COOKIE</span><span class="p">,</span> <span class="n">secret</span><span class="o">=</span><span class="n">SIGNATURE</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">sid</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="k">if</span> <span class="n">sid</span> <span class="ow">in</span> <span class="n">sessions</span><span class="p">:</span>
<span class="k">return</span> <span class="n">sid</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/hello</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">show_headers</span><span class="p">():</span>
<span class="n">hdrs</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="kn">import</span> <span class="n">pprint</span>
<span class="k">return</span> <span class="sh">"</span><span class="s">Got it</span><span class="se">\n</span><span class="s">&lt;pre&gt;</span><span class="se">\n</span><span class="sh">"</span><span class="o">+</span><span class="n">pprint</span><span class="p">.</span><span class="nf">pformat</span><span class="p">(</span><span class="n">hdrs</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="o">+</span><span class="sh">"</span><span class="se">\n</span><span class="s">&lt;/pre&gt;</span><span class="se">\n</span><span class="sh">"</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/login/</span><span class="sh">'</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">GET</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">user_login</span><span class="p">():</span>
<span class="n">sid</span> <span class="o">=</span> <span class="nf">is_active_session</span><span class="p">()</span>
<span class="k">if</span> <span class="n">sid</span><span class="p">:</span>
<span class="k">return</span> <span class="sh">'</span><span class="s">Logged in as %s</span><span class="sh">'</span> <span class="o">%</span> <span class="n">sessions</span><span class="p">[</span><span class="n">sid</span><span class="p">]</span>
<span class="k">return</span> <span class="sh">'''</span><span class="s">
&lt;form method=</span><span class="sh">"</span><span class="s">post</span><span class="sh">"</span><span class="s">&gt;
Username: &lt;input name=</span><span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="s"> type=</span><span class="sh">"</span><span class="s">text</span><span class="sh">"</span><span class="s"> /&gt;&lt;br/&gt;
Password: &lt;input name=</span><span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="s"> type=</span><span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="s"> /&gt;&lt;br/&gt;
&lt;input value=</span><span class="sh">"</span><span class="s">Login</span><span class="sh">"</span><span class="s"> type=</span><span class="sh">"</span><span class="s">submit</span><span class="sh">"</span><span class="s"> /&gt;
&lt;/form&gt;</span><span class="sh">'''</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/login/</span><span class="sh">'</span><span class="p">,</span><span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">POST</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">do_login</span><span class="p">():</span>
<span class="n">username</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">forms</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">username</span><span class="sh">'</span><span class="p">)</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">forms</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">password</span><span class="sh">'</span><span class="p">)</span>
<span class="k">if</span> <span class="sh">'</span><span class="s">url</span><span class="sh">'</span> <span class="ow">in</span> <span class="n">request</span><span class="p">.</span><span class="n">query</span><span class="p">:</span>
<span class="n">url</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">query</span><span class="p">.</span><span class="n">url</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">url</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">if</span> <span class="nf">check_login</span><span class="p">(</span><span class="n">username</span><span class="p">,</span><span class="n">password</span><span class="p">):</span>
<span class="n">sid</span> <span class="o">=</span> <span class="n">uuid</span><span class="p">.</span><span class="n">uuid4</span>
<span class="n">sessions</span><span class="p">[</span><span class="n">sid</span><span class="p">]</span> <span class="o">=</span> <span class="n">username</span>
<span class="n">response</span><span class="p">.</span><span class="nf">set_cookie</span><span class="p">(</span><span class="n">COOKIE</span><span class="p">,</span> <span class="n">sid</span><span class="p">,</span> <span class="n">secret</span><span class="o">=</span><span class="n">SIGNATURE</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="sh">"</span><span class="s">/</span><span class="sh">"</span><span class="p">,</span> <span class="n">httponly</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">if</span> <span class="n">url</span><span class="p">:</span>
<span class="nf">redirect</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="sh">"</span><span class="s">Welcome %s</span><span class="sh">"</span> <span class="o">%</span> <span class="n">username</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="sh">"</span><span class="s">Login Failed</span><span class="sh">"</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/auth</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">auth</span><span class="p">():</span>
<span class="n">sid</span> <span class="o">=</span> <span class="nf">is_active_session</span><span class="p">()</span>
<span class="k">if</span> <span class="n">sid</span><span class="p">:</span>
<span class="n">response</span><span class="p">.</span><span class="nf">set_header</span><span class="p">(</span><span class="sh">'</span><span class="s">X-Username</span><span class="sh">'</span><span class="p">,</span> <span class="n">sessions</span><span class="p">[</span><span class="n">sid</span><span class="p">])</span>
<span class="n">response</span><span class="p">.</span><span class="nf">set_header</span><span class="p">(</span><span class="sh">'</span><span class="s">X-Session</span><span class="sh">'</span><span class="p">,</span> <span class="nf">str</span><span class="p">(</span><span class="n">sid</span><span class="p">))</span>
<span class="k">return</span> <span class="sh">'</span><span class="s">OK </span><span class="sh">'</span> <span class="o">+</span> <span class="nf">str</span><span class="p">(</span><span class="n">sid</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="nf">abort</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span><span class="sh">"</span><span class="s">Unathenticated</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="sh">'</span><span class="s">0.0.0.0</span><span class="sh">'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
</code></pre></div></div>
<p><a href="https://github.com/alejandroliu/0ink.net/blob/master/snippets/nginx_mod_authrequest/auth1.py">snippets/nginx_mod_authrequest/auth1.py</a> <a href="https://github.com/alejandroliu/0ink.net/raw/master/snippets/nginx_mod_authrequest/auth1.py">view raw </a></p>
<p>Ceci fait des utilisations du micro cadre de la <a href="https://bottlepy.org/">bottle</a></p>
<p>Il implémente quatre itinéraires:</p>
<ol>
<li>GET /hello
Ceci est juste une URL de démonstration utilisée pour les tests. Affiche uniquement les en-têtes de demande.</li>
<li>GET /login/
Cest le point dentrée de la page de connexion.</li>
<li>POST /login/
Ceci est le gestionnaire de la page de connexion.</li>
<li>GET /auth
Cest le gestionnaire de sous-requête.</li>
</ol>
<p>Pour la démo, nous ne faisons pas vraiment de traitement de connexion. Il vous suffit de faire en sorte que le nom dutilisateur soit identique au mot de passe pour vous connecter. Tout le reste est un échec de connexion.</p>
<p>Lorsque lutilisateur se connecte avec succès, nous définissons un cookie. LURL de connexion et la ressource protégée ( /hello URL) se /hello dans la même étendue de cookie, nous pouvons utiliser le cookie défini par la page de connexion comme jeton de vérification dans la sous-demande.</p>
<p>Notez que la page de connexion peut être aussi simple ou aussi complexe que nécessaire. Par exemple, il est possible dimplémenter un flux de travail SAML , OpenID Connect ou tout autre flux de travail Single-Sign-On disponible.</p>
<p>En variante, une authentification à deux facteurs pourrait être implémentée ici. Les possibilités sont infinies.</p>
<p>Une utilisation intéressante du module <a href="https://nginx.org/en/docs/http/ngx_http_auth_request_module.html">auth_request</a> serait de déléguer l <a href="https://en.wikipedia.org/wiki/Basic_access_authentication">authentification de base</a> à un autre serveur ou même dimplémenter des authentifications non prises en charge par nginx, comme par exemple un simple en-tête Token-Bearer ou une <a href="https://en.wikipedia.org/wiki/Digest_access_authentication">authentification Digest</a>.</p>
<h2 id="authentification-de-base">authentification de base</h2>
<p>Cela peut sembler idiot puisque <a href="https://nginx.org/en/">nginx</a> prend en charge l<a href="https://en.wikipedia.org/wiki/Basic_access_authentication">authentification de base</a> tout de suite. Le cas dutilisation de cela est lorsque vous avez un cluster de serveurs frontaux nginx et que vous souhaitez quils sauthentifient tous auprès dun serveur didentité central. De plus, étant donné que lURI peut être transmis, un contrôle daccès plus sophistiqué peut être mis en œuvre. Enfin, des valeurs supplémentaires peuvent être passées dans les en-têtes, telles que les noms de groupe, les jetons, etc.</p>
<h3 id="configuration-nginx">configuration nginx</h3>
<p>Ressource protégée:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location /hello {
auth_request /auth; # The sub-request to use
auth_request_set $username $upstream_http_x_username; # Make the sub request data available
proxy_pass http://sample.com:8080/hello; # actual location of protected data
proxy_set_header X-Forwarded-Host $host; # Custom headers with authentication related data
proxy_set_header X-Remote-User $username;
}
</code></pre></div></div>
<blockquote>
<p>REMARQUE: contrairement à lexemple précédent, il nest pas nécessaire de fournir une page @error401.</p>
</blockquote>
<p>Configuration de sous-requête:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location = /auth {
proxy_pass http://auth-server.sample.com:8080/auth; # authentication server
proxy_pass_request_body off; # no data is being transferred...
proxy_set_header Content-Length '0';
proxy_set_header Host $host; # Custom headers with authentication related data
proxy_set_header X-Origin-URI $request_uri;
proxy_set_header X-Forwarded-Host $host;
}
</code></pre></div></div>
<h3 id="serveur-dauthentification">serveur dauthentification</h3>
<p>Limplémentation en python (encore une fois, en utilisant <a href="https://bottlepy.org/">bottle</a> ):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#/usr/bin/env python
</span><span class="kn">from</span> <span class="n">bottle</span> <span class="kn">import</span> <span class="n">route</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">abort</span><span class="p">,</span> <span class="n">redirect</span>
<span class="kn">import</span> <span class="n">base64</span>
<span class="k">def</span> <span class="nf">check_login</span><span class="p">(</span><span class="n">username</span><span class="p">,</span><span class="n">password</span><span class="p">):</span>
<span class="k">if</span> <span class="n">username</span> <span class="o">==</span> <span class="n">password</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="k">def</span> <span class="nf">authorize</span><span class="p">(</span><span class="n">basic_str</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">basic_str</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="n">auth</span> <span class="o">=</span> <span class="n">basic_str</span><span class="p">.</span><span class="nf">split</span><span class="p">()</span>
<span class="k">if</span> <span class="n">auth</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">Basic</span><span class="sh">'</span><span class="p">:</span>
<span class="c1"># We only support basic authentication
</span> <span class="k">return</span> <span class="bp">False</span>
<span class="n">auth</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="nf">b64decode</span><span class="p">(</span><span class="n">auth</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">auth</span> <span class="o">=</span> <span class="n">auth</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">:</span><span class="sh">'</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">auth</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">:</span>
<span class="c1"># Unable to decode username password...
</span> <span class="k">return</span> <span class="bp">False</span>
<span class="k">if</span> <span class="nf">check_login</span><span class="p">(</span><span class="n">auth</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span><span class="n">auth</span><span class="p">[</span><span class="mi">1</span><span class="p">]):</span>
<span class="n">response</span><span class="p">.</span><span class="nf">set_header</span><span class="p">(</span><span class="sh">'</span><span class="s">X-Username</span><span class="sh">'</span><span class="p">,</span> <span class="n">auth</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/hello</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">show_headers</span><span class="p">():</span>
<span class="n">hdrs</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="kn">import</span> <span class="n">pprint</span>
<span class="k">return</span> <span class="sh">"</span><span class="s">Got it</span><span class="se">\n</span><span class="s">&lt;pre&gt;</span><span class="se">\n</span><span class="sh">"</span><span class="o">+</span><span class="n">pprint</span><span class="p">.</span><span class="nf">pformat</span><span class="p">(</span><span class="n">hdrs</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="o">+</span><span class="sh">"</span><span class="se">\n</span><span class="s">&lt;/pre&gt;</span><span class="se">\n</span><span class="sh">"</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/auth</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">auth</span><span class="p">():</span>
<span class="k">if</span> <span class="nf">authorize</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">Authorization</span><span class="sh">'</span><span class="p">)):</span>
<span class="k">return</span> <span class="sh">'</span><span class="s">Yes, go in</span><span class="sh">'</span>
<span class="n">response</span><span class="p">.</span><span class="nf">set_header</span><span class="p">(</span><span class="sh">'</span><span class="s">WWW-Authenticate</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Basic realm=</span><span class="sh">"</span><span class="s">DEMO REALM</span><span class="sh">"'</span><span class="p">)</span>
<span class="n">response</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="mi">401</span>
<span class="k">return</span> <span class="sh">'</span><span class="s">Unauthenticated</span><span class="sh">'</span>
<span class="nf">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="sh">'</span><span class="s">0.0.0.0</span><span class="sh">'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
</code></pre></div></div>
<p><a href="https://github.com/alejandroliu/0ink.net/blob/master/snippets/nginx_mod_authrequest/auth2.py">snippets/nginx_mod_authrequest/auth2.py</a> <a href="https://github.com/alejandroliu/0ink.net/raw/master/snippets/nginx_mod_authrequest/auth2.py">view raw </a></p>
<p>Comme dans lexemple précédent, nous ne procédons à aucune vérification dutilisateur / mot de passe. Nous vérifions seulement si le nom dutilisateur et le mot de passe correspondent.</p>
<p>Contrairement à lexemple précédent, toute lauthentification est gérée par une seule route ( <code class="language-plaintext highlighter-rouge">/auth</code> ). Il renvoie WWW-Authenticate pour demander à lutilisateur un mot de passe. Et sil voit un en-tête d <code class="language-plaintext highlighter-rouge">Authorization</code> , il le validera.</p>
<h2 id="authentification-digest">authentification digest</h2>
<p>Cela implémente lauthentification <a href="https://en.wikipedia.org/wiki/Digest_access_authentication">Digest</a> pour nginx à laide du module de demande dauthentification (<a href="https://nginx.org/en/docs/http/ngx_http_auth_request_module.html">auth request module</a>).<br />
La configuration de nginx est la même que dans l<a href="https://en.wikipedia.org/wiki/Basic_access_authentication">authentification de base</a> .</p>
<p>Limplémentation en python (en utilisant la structure du framework <a href="https://bottlepy.org/">bootle</a> ):</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#/usr/bin/env python
</span><span class="kn">from</span> <span class="n">bottle</span> <span class="kn">import</span> <span class="n">route</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">response</span><span class="p">,</span> <span class="n">abort</span><span class="p">,</span> <span class="n">redirect</span>
<span class="kn">import</span> <span class="n">base64</span>
<span class="kn">import</span> <span class="n">hashlib</span>
<span class="kn">import</span> <span class="n">random</span>
<span class="kn">import</span> <span class="n">shlex</span>
<span class="n">REALM</span> <span class="o">=</span> <span class="sh">'</span><span class="s">Demo Realm</span><span class="sh">'</span>
<span class="k">def</span> <span class="nf">md5sum</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">hashlib</span><span class="p">.</span><span class="nf">md5</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="nf">hexdigest</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">gen_noise</span><span class="p">(</span><span class="nb">len</span> <span class="o">=</span> <span class="mi">16</span><span class="p">):</span>
<span class="n">letters</span> <span class="o">=</span> <span class="sh">'</span><span class="s">0123456789abcdef</span><span class="sh">'</span>
<span class="n">salt</span> <span class="o">=</span> <span class="sh">''</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="nb">len</span><span class="p">):</span>
<span class="n">salt</span> <span class="o">=</span> <span class="n">salt</span> <span class="o">+</span> <span class="n">random</span><span class="p">.</span><span class="nf">choice</span><span class="p">(</span><span class="n">letters</span><span class="p">)</span>
<span class="k">return</span> <span class="n">salt</span>
<span class="k">def</span> <span class="nf">authorize</span><span class="p">(</span><span class="n">auth</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">auth</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="n">auth</span> <span class="o">=</span> <span class="n">shlex</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="n">auth</span><span class="p">)</span>
<span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">auth</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">auth</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="sh">'</span><span class="s">Digest</span><span class="sh">'</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="n">req</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">auth</span><span class="p">:</span>
<span class="k">if</span> <span class="n">x</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">:]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">,</span><span class="sh">'</span><span class="p">:</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sh">'</span><span class="s">=</span><span class="sh">'</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span>
<span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">continue</span>
<span class="k">elif</span> <span class="nf">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">req</span><span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">lower</span><span class="p">()]</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">req</span><span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">lower</span><span class="p">()]</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="c1"># make sure that all fields are there...
</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="p">[</span><span class="sh">'</span><span class="s">username</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">realm</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">nonce</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">uri</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">response</span><span class="sh">'</span><span class="p">]:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">req</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="sh">'</span><span class="s">Missing </span><span class="sh">"</span><span class="s">%s</span><span class="sh">"</span><span class="s"> in response</span><span class="sh">'</span> <span class="o">%</span> <span class="n">x</span><span class="p">)</span>
<span class="n">method</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">X-Origin-Method</span><span class="sh">'</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">method</span><span class="p">:</span>
<span class="n">method</span> <span class="o">=</span> <span class="sh">'</span><span class="s">GET</span><span class="sh">'</span>
<span class="n">ha1</span> <span class="o">=</span> <span class="nf">md5sum</span><span class="p">(</span><span class="sh">'</span><span class="s">%s:%s:%s</span><span class="sh">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">username</span><span class="sh">'</span><span class="p">],</span><span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">realm</span><span class="sh">'</span><span class="p">],</span><span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">username</span><span class="sh">'</span><span class="p">]))</span>
<span class="n">ha2</span> <span class="o">=</span> <span class="nf">md5sum</span><span class="p">(</span><span class="sh">'</span><span class="s">%s:%s</span><span class="sh">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">uri</span><span class="sh">'</span><span class="p">]))</span>
<span class="n">resp</span> <span class="o">=</span> <span class="nf">md5sum</span><span class="p">(</span><span class="sh">'</span><span class="s">%s:%s:%s</span><span class="sh">'</span> <span class="o">%</span> <span class="p">(</span> <span class="n">ha1</span><span class="p">,</span> <span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">nonce</span><span class="sh">'</span><span class="p">],</span> <span class="n">ha2</span><span class="p">))</span>
<span class="c1">#print(req)
</span> <span class="c1">#print("calc: %s\nrecv: %s\n" % (resp, req['response']))
</span>
<span class="k">if</span> <span class="n">resp</span> <span class="o">==</span> <span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">response</span><span class="sh">'</span><span class="p">]:</span>
<span class="n">response</span><span class="p">.</span><span class="nf">set_header</span><span class="p">(</span><span class="sh">'</span><span class="s">X-Username</span><span class="sh">'</span><span class="p">,</span> <span class="n">req</span><span class="p">[</span><span class="sh">'</span><span class="s">username</span><span class="sh">'</span><span class="p">])</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/hello</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">show_headers</span><span class="p">():</span>
<span class="n">hdrs</span> <span class="o">=</span> <span class="nf">dict</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">)</span>
<span class="kn">import</span> <span class="n">pprint</span>
<span class="k">return</span> <span class="sh">"</span><span class="s">Got it</span><span class="se">\n</span><span class="s">&lt;pre&gt;</span><span class="se">\n</span><span class="sh">"</span><span class="o">+</span><span class="n">pprint</span><span class="p">.</span><span class="nf">pformat</span><span class="p">(</span><span class="n">hdrs</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="o">+</span><span class="sh">"</span><span class="se">\n</span><span class="s">&lt;/pre&gt;</span><span class="se">\n</span><span class="sh">"</span>
<span class="nd">@route</span><span class="p">(</span><span class="sh">'</span><span class="s">/auth</span><span class="sh">'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">auth</span><span class="p">():</span>
<span class="k">if</span> <span class="nf">authorize</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">'</span><span class="s">Authorization</span><span class="sh">'</span><span class="p">)):</span>
<span class="k">return</span> <span class="sh">'</span><span class="s">Yes, go in</span><span class="sh">'</span>
<span class="n">nonce</span> <span class="o">=</span> <span class="nf">gen_noise</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
<span class="n">opaque</span> <span class="o">=</span> <span class="nf">gen_noise</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
<span class="c1">#print("nonce=%s\nopaque=%s\n" % (nonce, opaque))
</span> <span class="n">response</span><span class="p">.</span><span class="nf">set_header</span><span class="p">(</span><span class="sh">'</span><span class="s">WWW-Authenticate</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">Digest realm=</span><span class="sh">"</span><span class="s">%s</span><span class="sh">"</span><span class="s">, nonce=</span><span class="sh">"</span><span class="s">%s</span><span class="sh">"</span><span class="s">, opaque=</span><span class="sh">"</span><span class="s">%s</span><span class="sh">"'</span> <span class="o">%</span> <span class="p">(</span><span class="n">REALM</span><span class="p">,</span> <span class="n">nonce</span><span class="p">,</span> <span class="n">opaque</span><span class="p">))</span>
<span class="n">response</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="mi">401</span>
<span class="k">return</span> <span class="sh">'</span><span class="s">Unauthenticated</span><span class="sh">'</span>
<span class="nf">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="sh">'</span><span class="s">0.0.0.0</span><span class="sh">'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">8080</span><span class="p">)</span>
</code></pre></div></div>
<p><a href="https://github.com/alejandroliu/0ink.net/blob/master/snippets/nginx_mod_authrequest/auth3.py"> snippets/nginx_mod_authrequest/auth3.py</a> <a href="https://github.com/alejandroliu/0ink.net/raw/master/snippets/nginx_mod_authrequest/auth3.py">view raw</a></p>
<h1 id="exemple-nginx-module-http_auth_request_module">Exemple nginx module http_auth_request_module</h1>
<p>Assurez-vous que votre NGINX Open Source est compilé avec loption de configuration module <code class="language-plaintext highlighter-rouge">--with-http_auth_request_module</code></p>
<p>Dans lemplacement qui nécessite une authentification de demande, spécifiez la directive auth_request dans laquelle spécifier un emplacement interne où une sous-demande dautorisation sera transmise :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location /private/ {
auth_request /auth;
#...
}
</code></pre></div></div>
<p>Ici, pour chaque demande à /private, une sous-demande à lemplacement interne /auth sera faite.<br />
Spécifiez un emplacement interne et la directive proxy_pass à lintérieur de cet emplacement qui transmettra les sous-demandes dauthentification par proxy à un serveur ou service dauthentification :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location = /auth {
internal;
proxy_pass http://auth-server;
#...
}
</code></pre></div></div>
<p>Comme le corps de la requête est rejeté pour les sous-requêtes dauthentification, vous devrez désactiver la directive proxy_pass_request_body et également définir len-tête Content-Length sur une chaîne nulle :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location = /auth {
internal;
proxy_pass http://auth-server;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
#...
}
</code></pre></div></div>
<p>Passez lURI de la requête originale complète avec les arguments de la directive proxy_set_set_header :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location = /auth {
internal;
proxy_pass http://auth-server;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
</code></pre></div></div>
<p>En option, vous pouvez définir une valeur variable basée sur le résultat de la sous-requête avec la directive auth_request_set :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location /private/ {
auth_request /auth;
auth_request_set $auth_status $upstream_status;
}
</code></pre></div></div>
<p>Exemple complet<br />
Cet exemple résume les étapes précédentes en une seule configuration :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http {
#...
server {
#...
location /private/ {
auth_request /auth;
auth_request_set $auth_status $upstream_status;
}
location = /auth {
internal;
proxy_pass http://auth-server;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}
}
</code></pre></div></div>
<h2 id="sso-avec-le-module-nginx-auth_request">SSO avec le module Nginx auth_request</h2>
<p>Récemment, nous avons eu le défi de connecter un site Web statique à notre infrastructure SSO (Single Sign-On) existante.</p>
<p>Les composants suivants sont impliqués</p>
<ul>
<li>api.example.com : le point de terminaison de lAPI SSO</li>
<li>login.example.com : interface utilisateur orientée utilisateur pour lAPI SSO; Fournit les formulaires dinscription et de connexion, etc.</li>
<li>staticpage.example.com : contenu statique du site Web devant être sécurisé / connecté au SSO.</li>
</ul>
<p>Lauthentification sur lAPI SSO est effectuée avec un jeton qui peut être fourni via lentête <code class="language-plaintext highlighter-rouge">X-SHOPWARE-SSO-Token</code> ou via le cookie <code class="language-plaintext highlighter-rouge">shopware_sso_token</code> .</p>
<h3 id="challenge">challenge</h3>
<p>Notre tâche était de nous assurer que toutes les demandes à <em>staticpage.example.com</em> sont autorisées par <em>api.example.com</em> <br />
Les demandes non authentifiées doivent être redirigées vers <em>login.example.com</em></p>
<h3 id="nginx-à-la-rescousse">Nginx à la rescousse</h3>
<p>Heureusement, <a href="http://nginx.org/">nginx</a> est également capable de résoudre ce problème pour nous.</p>
<p>Tout ce dont nous avons besoin, cest du module <a href="http://nginx.org/en/docs/http/ngx_http_auth_request_module.html">auth_request</a></p>
<blockquote>
<p>Le module <code class="language-plaintext highlighter-rouge">ngx_http_auth_request_module</code> implémente lautorisation du client en fonction du résultat dune sous-demande.<br />
-&gt; Si la sous-demande renvoie un code de réponse 2xx, laccès est autorisé.<br />
-&gt; Sil renvoie 401 ou 403, laccès est refusé avec le code derreur correspondant.</p>
</blockquote>
<h3 id="configuration">Configuration</h3>
<p>Dans le vhost de <em>staticpage.example.com</em> nous devons ajouter la directive <code class="language-plaintext highlighter-rouge">auth_request</code> :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
server_name staticpage.example.com;
auth_request /auth;
...
}
</code></pre></div></div>
<p>Pour chaque demande adressée à http://staticpage.example.com/ , une sous-demande interne à http://staticpage.example.com/auth est effectuée.</p>
<p>Ajoutons ceci en tant quemplacement:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
...
location = /auth {
internal;
proxy_pass https://api.example.com;
}
}
</code></pre></div></div>
<p>La demande est maintenant transmise à notre noeud final SSO ( proxy_pass ). Veuillez noter que le chemin de lemplacement est inclus dans cette demande. LURL de la demande devient donc <strong>https://api.example.com/auth</strong> .</p>
<p>À ce stade, <em>api.example.com</em> est responsable de lautorisation.<br />
Si la demande renvoie un code de réponse 2xx, la demande est autorisée. Sil renvoie 401 ou 403, laccès est refusé.</p>
<p>Gérons la redirection au cas où lAPI SSO renvoie le code HTTP 401. Avec la directive <a href="http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page">error_page</a> :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
...
error_page 401 = @error401;
location @error401 {
return 302 https://login.example.com;
}
}
</code></pre></div></div>
<p>Si la demande nest pas autorisée, nous redirigerons lutilisateur vers <strong>https://login.example.com</strong> aide du code détat 302.<br />
Ici, lutilisateur recevra un message derreur approprié et la possibilité de lautoriser.</p>
<p>Nous devons maintenant transporter le jeton dautorisation du client dun système à un autre. Après avoir été autorisé à <strong>login.example.com</strong> , lutilisateur reçoit un cookie contenant le jeton dauthentification. Le cookie est défini sur .example.com afin que <strong>staticpage.example.com</strong> puisse également accéder au jeton. Tout ce que nous avons à faire maintenant, cest de passer le jeton du cookie au serveur dauthentification.</p>
<p>Nous utilisons <code class="language-plaintext highlighter-rouge">$http_cookie ~* "shopware_sso_token=([^;]+)(?:;|$)"</code> pour faire correspondre le jeton du cookie de lutilisateur, suivi dun proxy_set_header pour le transmettre au backend.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>location = /auth {
...
if ($http_cookie ~* "shopware_sso_token=([^;]+)(?:;|$)") {
set $token "$1";
}
proxy_set_header X-SHOPWARE-SSO-Token $token;
}
</code></pre></div></div>
<h3 id="mission-accomplie">Mission accomplie</h3>
<p>Maintenant, <strong>api.example.com</strong> est capable de décider si la demande nécessite une authentification (jeton manquant ou expiré) et de répondre avec le code détat 401.<br />
Pour les utilisateurs authentifiés mais non autorisés , il répond avec un code 403.<br />
Si lutilisateur est authentifié et autorisé, il répond avec un code 200.</p>
<h3 id="appendice">appendice</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
server_name staticpage.example.com;
root /var/www/staticpage.example.com/;
error_page 401 = @error401;
location @error401 {
return 302 https://login.example.com;
}
auth_request /auth;
location = /auth {
internal;
proxy_pass https://api.example.com;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
if ($http_cookie ~* "shopware_sso_token=([^;]+)(?:;|$)") {
set $token "$1";
}
proxy_set_header X-SHOPWARE-SSO-Token $token;
}
}
</code></pre></div></div>
<h2 id="nginx-sso---sso-hors-ligne-simple-pour-nginx">nginx-sso - SSO hors ligne simple pour nginx</h2>
<p>nginx-sso est un système SSO (Single Sign-On) léger, hors ligne, qui fonctionne avec les cookies et ECDSA. Il peut facilement être utilisé avec vanilla nginx et nimporte quelle application dorsale. Limplémentation de référence est écrite en golang et possède quelques fonctionnalités supplémentaires intéressantes telles que lautorisation.</p>
<h3 id="sso-simple-sur-le-web">SSO simple sur le Web</h3>
<p>Remote-User Remote-Groups</p>
<p>Vous pouvez choisir entre Google, Facebook, LinkedIn, GitHub et plus de sites, couvrant parfaitement votre base dutilisateurs. Lexternalisation de lauthentification auprès de ces personnes est une meilleure idée que de (mal) gérer vous-même les informations didentification des utilisateurs!</p>
<p>Bien que ces options soient définitivement la voie à suivre pour la plupart des applications, il existe des scénarios où elles ne fonctionnent pas:</p>
<ul>
<li>Vous pourriez ne pas faire confiance à ces fournisseurs.</li>
<li>Vos applications peuvent vivre «hors ligne», par exemple dans un réseau dentreprise.</li>
<li>Vous voudrez peut-être protéger les ressources statiques.</li>
<li>Vous pourriez être choqué par la complexité de systèmes tels que OpenID Connect.</li>
</ul>
<p>Une autre réalité de nos jours est que votre application risque de sexécuter derrière un autre processus HTTP. De nombreuses personnes (moi-même inclus) utilisent aujourdhui nginx pour mettre fin à TLS, aux demandes déquilibrage de charge, etc. Cest pourquoi jai décidé de créer ma propre SSO légère, qui fonctionne avec nginx et des applications arbitraires.</p>
<h3 id="fonctions">Fonctions</h3>
<p>Lors de la conception de nginx-sso, jai proposé une liste de fonctionnalités nécessaires et utiles:</p>
<ul>
<li>Travailler hors connexion: ni lutilisateur ni le service ne doivent parler à Internet.</li>
<li>Travail déconnecté: il nest pas nécessaire que le service communique avec le fournisseur didentité.</li>
<li>Sécurisé: la compromission dun service ne doit affecter aucun autre service.</li>
<li>Fournir une authentification pour les applications dorsales.</li>
<li>Fournissez une autorisation pour accéder aux URI et aux applications / sites dorsaux.</li>
<li>Soyez simple à comprendre et à configurer.</li>
<li>Travailler avec nginx sans nécessiter de correction / construction manuelle de nginx ni de maintenance de modules en dehors de larbre.</li>
</ul>
<h3 id="solutions-sso-basées-sur-les-cookies">Solutions SSO basées sur les cookies</h3>
<p>En ce qui concerne lauthentification unique déconnectée / hors ligne, il est évident que lutilisateur fournira ses propres informations didentification au serveur dapplications, qui doit décider sil est légitime ou non. Cela signifie quil faut vérifier lintégrité et lauthenticité de la revendication des utilisateurs, ces deux tâches étant généralement accomplies à laide de MAC ou de signatures .</p>
<p>Pour moi, les MAC nétaient pas une option, car un attaquant pourrait émettre ses propres tickets en compromettant un seul serveur dapplications. Cela laisse des signatures à clé publique, basées sur DSA ou ECC. Les signatures ECC sont le meilleur choix car elles sont plus efficaces et prennent moins de place dans un cookie.</p>
<p>nginx-sso utilise un cookie simple avec une signature supplémentaire ECSDA. La signature est faite sur la charge du cookie (nom dutilisateur, groupes), ainsi que sur lhorodatage dexpiration et ladresse IP de lutilisateur.</p>
<h3 id="authentification">Authentification</h3>
<p>Lauthentification à laide de vanilla nginx est possible principalement grâce à un impressionnant nginx-plugin appelé <a href="http://nginx.org/en/docs/http/ngx_http_auth_request_module.html">auth_request</a> . Sur le site:</p>
<blockquote>
<p>Le module ngx_http_auth_request_module (1.5.4+) implémente lautorisation du client en fonction du résultat dune sous-demande. Si la sous-demande renvoie un code de réponse 2xx, laccès est autorisé. Sil renvoie 401 ou 403, laccès est refusé avec le code derreur correspondant. Tout autre code de réponse renvoyé par la sous-demande est considéré comme une erreur.</p>
</blockquote>
<p>Avec ce module, chaque accès aux ressources configurées sur votre serveur nginx déclenchera une requête HTTP vers un serveur dauthentification.<br />
Cette demande contiendra les en-têtes de la demande originale que votre serveur dauthentification utilise pour accorder ou refuser laccès.<br />
Le module auth_request nest pas compilé par défaut ou dans toutes les distributions, mais il fait partie de la baseline nginx codebase et les distributions majeures ont des packages compilés avec ce module.</p>
<p>Ici, nous pouvons voir lextrait de configuration nginx qui protège la ressource /secret avec une sous-requête à la ressource interne <strong>/auth</strong> qui envoie un proxy au serveur ssoauth qui sexécute sur localhost.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> location = /auth {
internal;
proxy_pass http://127.0.0.1:8082;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
location /secret {
auth_request /auth;
}
</code></pre></div></div>
<h3 id="passer-des-variables-à-lapplication">Passer des variables à lapplication</h3>
<p>Bien que je sois capable deffectuer lauthentification et lautorisation dans le backend auth, cela na toujours pas aidé mon application backend à identifier lutilisateur. Heureusement, jai découvert comment transmettre les variables renvoyées par le point final auth aux applications dorsales , voir lexemple nginx.conf . Maintenant, le service backend peut simplement assumer la présence et lexactitude de ces en-têtes HTTP et ne pas avoir à gérer le cookie sso .</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>location /secret {
auth_request /auth;
auth_request_set $user $upstream_http_remote_user;
proxy_set_header Remote-User $user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-Groups $groups;
auth_request_set $expiry $upstream_http_remote_expiry;
proxy_set_header Remote-Expiry $expiry;
[...]
proxy_information_for_backend_application;
[...]
}
</code></pre></div></div>
<h3 id="autorisation">Autorisation</h3>
<p>Comme un effet secondaire intéressant, étant donné que nous effectuons déjà une sous-demande pour chaque requête HTTP, nous pouvons également utiliser le point de terminaison <strong>auth</strong> pour effectuer une autorisation. <br />
Pour ce faire, jai mis en place une ACL très rudimentaire dans ssoauth. Il contient une liste de vhosts et de préfixes et pour chacun deux une liste dutilisateurs et de groupes autorisés.<br />
De cette façon, même les ressources statiques peuvent être facilement protégées.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"acl": {
"auth.domain.dev:8080": {
"Users": ["jg123456"],
"Groups": ["x:"],
"UrlPrefixes": {
"/secret/": {
"Users": ["ba514378", "jb759123"],
"Groups": ["y:engineering:cloud"]
}
}
}
}
</code></pre></div></div>
<h3 id="protéger-les-applications-avec-nginx-sso">Protéger les applications avec nginx-sso</h3>
<p>Si vous écrivez une application personnalisée et souhaitez utiliser ce système (ou un système similaire), rien de plus simple.<br />
Vous pouvez simplement utiliser les entêtes <code class="language-plaintext highlighter-rouge">Remote-User</code> <code class="language-plaintext highlighter-rouge">Remote-Groups</code> pour effectuer une autorisation, par exemple en disant que <em>Tout le monde du groupe xyz</em> est un <em>administrateur</em><br />
Vous pouvez également disposer de votre propre base de données dutilisateurs et utiliser uniquement len <code class="language-plaintext highlighter-rouge">Remote-User</code> pour créer et rechercher ultérieurement le bon utilisateur.<br />
De cette façon, vous pouvez avoir des attributs (et des autorisations) supplémentaires pour chaque utilisateur.</p>
<p>Si vous utilisez un logiciel de stock, vous pourrez peut-être aussi utiliser ce schéma. La plupart des logiciels prennent en charge la connexion via l Remote-User , même si le logiciel implémente ensuite sa propre base de données dutilisateurs. Pour les logiciels à source fermée, vous pouvez parfois trouver des plug-ins qui activent cette fonctionnalité.</p>
<h2 id="liens">Liens</h2>
<ul>
<li><a href="https://jolicode.com/blog/comment-nous-avons-remplace-openresty-par-nginx">Comment nous avons remplacé OpenResty par NGINX?</a></li>
<li><a href="https://github.com/perusio/nginx-auth-request-module">Nginx Auth Request Module</a></li>
<li><a href="https://developers.shopware.com/blog/2015/03/02/sso-with-nginx-authrequest-module/">SSO with Nginx auth_request module</a></li>
<li><a href="https://developers.shopware.com/blog/2015/03/02/sso-with-nginx-authrequest-module/">Décrit une configuration de base de lutilisation de auth_request avec des cookies</a>.</li>
<li><a href="https://news.ycombinator.com/item?id=7641148">Discussion sur Hackernews traitant de auth_request et de différentes approches de lauthentification unique</a>.</li>
<li><a href="https://github.com/heipei/nginx-sso/">Le code sur GitHub</a></li>
<li><a href="https://www.mon-code.net/article/103/Securiser-les-cookies-de-son-application-web">Sécuriser les cookies de son application web</a></li>
</ul>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2019-10-08T00:00:00+02: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>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2019/10/08/Configuration_nginx_ssl_(tls1.2-tls1.3)_Diffie-Hellman_Headers_OCSP.html">Configuration nginx , ssl (tls1.2 tls1.3) , Diffie Hellman ,Headers et OCSP</a></div><div class="next"><span>SUIVANT</span><a href="/2019/10/17/Linux-gestion-des-utilisateurs-et-permissions-serveur-web.html">Linux, gestion des utilisateurs ,permissions des fichiers et répertoires dun serveur web</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}&nbsp;{title}</a>&nbsp;(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>