Compare commits

...
17 Commits
Author SHA1 Message Date
FreddleSpl0it 2a2ffca169 [Web] API: update Swagger UI to 5.34.1, plain inline code 2026-11-10 20:28:43 +01:00
FreddleSpl0it 1a5095c0f4 [Web] API: shorter descriptions 2026-11-10 20:25:51 +01:00
FreddleSpl0it 2418da031f [Web] API: complete the OpenAPI docs, add route tests for json_api.php 2026-11-10 20:04:05 +01:00
FreddleSpl0it 9825c8d290 Merge pull request #7641 from mailcow/fix/7436
[Update.sh] Keep ENABLE_IPV6 as set, do not disable IPv6 on hosts with a public IPv6 address
2026-11-10 17:09:56 +01:00
FreddleSpl0it dc880d861c [Update] Keep ENABLE_IPV6 as set, do not disable IPv6 on hosts with a public IPv6 address 2026-11-10 17:04:50 +01:00
FreddleSpl0it a03fd8aaad Merge pull request #7500 from philippderdiedas/fix/update-sh-masquerade-bridge
[Update] Fix update.sh deleting mailcow's own MASQUERADE rule
2026-11-10 15:39:48 +01:00
FreddleSpl0it e3a90718c2 Merge pull request #7516 from wbilalz400/fix/webauthn-forced-enrollment-empty-credential
[Web] Fix forced WebAuthn TFA enrollment storing an empty credential
2026-11-10 15:21:30 +01:00
FreddleSpl0it 54a85ba439 Merge pull request #7540 from pReya/fix/table-ui-polish
[Web] Table UI polish (icon centering, tag heights, sort headers)
2026-11-10 14:07:39 +01:00
FreddleSpl0it 4ced82d554 Merge pull request #7548 from junkerderprovinz/fix/idp-ignore-ssl-error-key
[Web] Fix ignore_ssl_error key in identity provider settings
2026-11-10 13:55:15 +01:00
FreddleSpl0it f83e02b740 Merge pull request #7640 from mailcow/fix/7562
[Rspamd] Remove the URLhaus and MalwareBazaar maps retired by abuse.ch
2026-11-10 13:51:45 +01:00
Junker der Provinzanddiko 5705a48724 [Web] Fix ignore_ssl_error key in identity provider settings
The edit action stores the setting as ignore_ssl_error, but the get
action cast and defaulted ignore_ssl_errors. The stored value was
returned as a string and LDAP setups got an extra ignore_ssl_errors
field that was always false.

Co-authored-by: diko <108140792+admins-little-helper@users.noreply.github.com>
2026-10-24 20:05:10 +02:00
Moritz Stückler 498434ee3d [Web] Even out spacing of the expanded table row
DataTables removes the top padding of the first entry in the expanded
row, but mailcow's 0.5em padding left 7px under the last entry, so the
list had 7px of space above and 15px below and the last entry (often
Tags) looked taller than the others. Drop the last entry's bottom
padding as well.

Signed-off-by: Moritz Stückler <moritz@bitbetter.de>
2026-10-22 13:10:49 +02:00
Moritz Stückler f662af4c50 [Web] Even out badge and tag sizes in tables
- Use the normal cursor on non-orderable headers. The expand column
  header kept a pointer cursor from sorting_asc although clicking it
  does nothing.
- Let icons in badges inherit the badge font size, like .btn .bi, and
  keep their glyph from adding line height. The global 12pt icon size
  made e.g. the "SSO @" badge in Last mail login taller than the IMAP
  and SMTP badges next to it. Tag badges keep their 12px icon.
- Drop the vertical margin of tag badges inside tables. It is meant for
  the tag editor in edit forms and made the Tags entry of the expanded
  row taller than the others.
- Center tag badge captions: the 12px tag icon stretched the 10.5px
  badge line and pushed the caption below center.

Signed-off-by: Moritz Stückler <moritz@bitbetter.de>
2026-10-22 13:06:00 +02:00
Moritz Stückler 8d4895a54d [Web] Align the table expand control and row checkbox
- Center the +/- glyph in the expand control: its line-height (1.25em)
  was larger than the 1em box, pushing the glyph down. Derive the box's
  margin-top from its size so it is centered in the row (desktop and
  the larger mobile variant).
- Hide sort arrows on non-orderable headers. The default initial order
  targets column 0, the expand column, which got a sorting_asc arrow
  and arrow padding although it cannot be sorted.
- Center row checkboxes level with the expand control by using
  Bootstrap's 0.25em margin-top instead of a fixed 7px.

Signed-off-by: Moritz Stückler <moritz@bitbetter.de>
2026-10-22 12:43:18 +02:00
Moritz Stückler 81d6b1f547 [Web] Clearer captions for domain table columns
Caption the "RL" column "Rate limit" using the existing translated
edit.ratelimit string, and shorten "Max. size of a mailbox" to
"Max. mailbox size" (en-gb only; the key is only used for this column).

Signed-off-by: Moritz Stückler <moritz@bitbetter.de>
2026-10-22 12:43:18 +02:00
Bilal Zahid d93f32072e fix(web): post WebAuthn TFA enrollment through the JSON API
The forced TFA enrollment form submitted the raw credential JSON in the
'token' field, which set_tfa()'s webauthn case never reads. It expects the
parsed \['registration'] object that only the JSON API builds, so the
form POST inserted a row with an empty keyHandle and a NULL publicKey while
still reporting success. Every WebAuthn 2FA login then failed, because
webauthn-tfa-get-args sent an empty credential id in allowCredentials.

- base.twig: POST the attestation to /api/v1/add/webauthn-tfa-registration
  like the regular WebAuthn modal does
- functions.inc.php: refuse incomplete WebAuthn registrations
- lang.en-gb.json: message for the new guard
2026-10-13 19:25:17 +05:00
Philipp Harms 7b658fa65c [Update] Fix update.sh deleting mailcow's own MASQUERADE rule
The "Checking for conflicting bridges" step extracts the bridge name with
`cut -d':' -f2`, which yields " br-mailcow" (leading space). Since the
variable got quoted in `grep -v "${MAILCOW_BRIDGE}"`, the leading space is
significant and never matches "!br-mailcow" in the `iptables -L` output, so
the rule `-s <IPV4_NETWORK>.0/24 ! -o br-mailcow -j MASQUERADE` itself gets
deleted. The mailcow network loses outbound NAT, unbound turns unhealthy and
dependent containers stay in "Created" until Docker is restarted.

Strip whitespace/quotes from the bridge name, match the interface as a whole
token, and skip the cleanup if the bridge name could not be determined.
2026-10-02 18:53:12 +02:00
75 changed files with 18606 additions and 7561 deletions

No files matched your search

+46 -21
View File
@@ -3,6 +3,11 @@
# THIS SCRIPT IS DESIGNED TO BE RUNNING BY MAILCOW SCRIPTS ONLY! # THIS SCRIPT IS DESIGNED TO BE RUNNING BY MAILCOW SCRIPTS ONLY!
# DO NOT, AGAIN, NOT TRY TO RUN THIS SCRIPT STANDALONE!!!!!! # DO NOT, AGAIN, NOT TRY TO RUN THIS SCRIPT STANDALONE!!!!!!
# Global IPv6 address that is no unique local address (fc00::/7, e.g. Docker bridges)
_has_public_ipv6() {
ip -6 -o addr show scope global 2>/dev/null | awk '{print $4}' | grep -qviE '^f[cd]'
}
# 1) Check if the host supports IPv6 # 1) Check if the host supports IPv6
get_ipv6_support() { get_ipv6_support() {
# ---- helper: probe external IPv6 connectivity without DNS ---- # ---- helper: probe external IPv6 connectivity without DNS ----
@@ -37,6 +42,9 @@ get_ipv6_support() {
if _probe_ipv6_connectivity; then if _probe_ipv6_connectivity; then
DETECTED_IPV6=true DETECTED_IPV6=true
echo -e "IPv6 detected on host – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}" echo -e "IPv6 detected on host – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
elif _has_public_ipv6; then
DETECTED_IPV6=true
echo -e "${YELLOW}External IPv6 connectivity test failed, but the host has a public IPv6 address – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}"
else else
DETECTED_IPV6=false DETECTED_IPV6=false
echo -e "${YELLOW}Default IPv6 route present but external IPv6 connectivity failed – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" echo -e "${YELLOW}Default IPv6 route present but external IPv6 connectivity failed – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}"
@@ -192,9 +200,31 @@ EOF
fi fi
} }
_set_enable_ipv6() {
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
sed -i "s/^ENABLE_IPV6=.*/ENABLE_IPV6=$1/" "$MAILCOW_CONF"
else
echo "ENABLE_IPV6=$1" >> "$MAILCOW_CONF"
fi
else
export IPV6_BOOL=$1
fi
}
_warn_ipv6_disabled() {
_has_public_ipv6 || return 0
ip -6 route show default 2>/dev/null | grep -qE '^default' || return 0
grep -Eq '"userland-proxy"[[:space:]]*:[[:space:]]*false' /etc/docker/daemon.json 2>/dev/null && return 0
echo -e "${LIGHT_RED}WARNING: ENABLE_IPV6=false, but this host has a public IPv6 address.${NC}"
echo -e "${YELLOW}Docker forwards IPv6 connections to mailcow with an internal source address, which Postfix trusts. This can make your server an open relay over IPv6.${NC}"
echo -e "${YELLOW}Set ENABLE_IPV6=true in mailcow.conf, or set \"userland-proxy\": false in /etc/docker/daemon.json and restart Docker.${NC}"
echo -e "${YELLOW}See https://docs.mailcow.email/post_installation/firststeps-disable_ipv6/${NC}"
sleep 5
}
# 3) Main wrapper for generate_config.sh and update.sh # 3) Main wrapper for generate_config.sh and update.sh
configure_ipv6() { configure_ipv6() {
# detect manual override if mailcow.conf is present
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]] && grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]] && grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
MANUAL_SETTING=$(grep '^ENABLE_IPV6=' "$MAILCOW_CONF" | cut -d= -f2) MANUAL_SETTING=$(grep '^ENABLE_IPV6=' "$MAILCOW_CONF" | cut -d= -f2)
elif [[ -z "$MAILCOW_CONF" ]] && [[ -n "${ENABLE_IPV6:-}" ]]; then elif [[ -z "$MAILCOW_CONF" ]] && [[ -n "${ENABLE_IPV6:-}" ]]; then
@@ -203,19 +233,23 @@ configure_ipv6() {
MANUAL_SETTING="" MANUAL_SETTING=""
fi fi
if [[ "${MANUAL_SETTING,,}" == "false" ]]; then
_set_enable_ipv6 false
echo "ENABLE_IPV6=false is set, skipping Docker IPv6 configuration."
_warn_ipv6_disabled
echo "IPv6 configuration complete: ENABLE_IPV6=false"
return
fi
get_ipv6_support get_ipv6_support
# if user manually set it, check for mismatch if [[ "$DETECTED_IPV6" != "true" && "${MANUAL_SETTING,,}" == "true" ]] && _has_public_ipv6; then
echo -e "${YELLOW}Keeping ENABLE_IPV6=true, the host has a public IPv6 address.${NC}"
DETECTED_IPV6=true
fi
if [[ "$DETECTED_IPV6" != "true" ]]; then if [[ "$DETECTED_IPV6" != "true" ]]; then
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then _set_enable_ipv6 false
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=false/' "$MAILCOW_CONF"
else
echo "ENABLE_IPV6=false" >> "$MAILCOW_CONF"
fi
else
export IPV6_BOOL=false
fi
echo "Skipping Docker IPv6 configuration because host does not support IPv6." echo "Skipping Docker IPv6 configuration because host does not support IPv6."
echo "Make sure to check if your docker daemon.json does not include \"enable_ipv6\": true if you do not want IPv6." echo "Make sure to check if your docker daemon.json does not include \"enable_ipv6\": true if you do not want IPv6."
echo "IPv6 configuration complete: ENABLE_IPV6=false" echo "IPv6 configuration complete: ENABLE_IPV6=false"
@@ -224,16 +258,7 @@ configure_ipv6() {
fi fi
docker_daemon_edit docker_daemon_edit
_set_enable_ipv6 true
if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then
if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then
sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=true/' "$MAILCOW_CONF"
else
echo "ENABLE_IPV6=true" >> "$MAILCOW_CONF"
fi
else
export IPV6_BOOL=true
fi
echo "IPv6 configuration complete: ENABLE_IPV6=true" echo "IPv6 configuration complete: ENABLE_IPV6=true"
} }
+16
View File
@@ -14,3 +14,19 @@ body {
margin: 0; margin: 0;
background: #fafafa; background: #fafafa;
} }
/* mailcow: inline code in descriptions in the text colour instead of bold purple */
.swagger-ui .markdown code,
.swagger-ui .renderedMarkdown code {
color: inherit;
font-weight: normal;
font-size: 0.9em;
background: rgba(0, 0, 0, 0.04);
padding: 0 3px;
}
html.dark-mode .swagger-ui .markdown code,
html.dark-mode .swagger-ui .renderedMarkdown code {
color: inherit;
background: rgba(255, 255, 255, 0.08);
}
+1 -74
View File
@@ -1,79 +1,6 @@
<!doctype html> <!doctype html>
<html lang="en-US"> <html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body> <body>
<script> <script src="oauth2-redirect.js"></script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}
arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};
isValid = qp.state === sentState;
if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body> </body>
</html> </html>
+1
View File
@@ -0,0 +1 @@
"use strict";function run(){var e,r,t,a=window.opener.swaggerUIRedirectOauth2,o=a.state,n=a.redirectUrl;if((t=(r=/code|token|error/.test(window.location.hash)?window.location.hash.substring(1).replace("?","&"):location.search.substring(1)).split("&")).forEach((function(e,r,t){t[r]='"'+e.replace("=",'":"')+'"'})),e=(r=r?JSON.parse("{"+t.join()+"}",(function(e,r){return""===e?r:decodeURIComponent(r)})):{}).state===o,"accessCode"!==a.auth.schema.get("flow")&&"authorizationCode"!==a.auth.schema.get("flow")&&"authorization_code"!==a.auth.schema.get("flow")||a.auth.code)a.callback({auth:a.auth,token:r,isValid:e,redirectUrl:n});else if(e||a.errCb({authId:a.auth.name,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),r.code)delete a.state,a.auth.code=r.code,a.callback({auth:a.auth,redirectUrl:n});else{let e;r.error&&(e="["+r.error+"]: "+(r.error_description?r.error_description+". ":"no accessCode received from the server. ")+(r.error_uri?"More info: "+r.error_uri:"")),a.errCb({authId:a.auth.name,source:"auth",level:"error",message:e||"[Authorization failed]: no accessCode received from the server"})}window.close()}"loading"!==document.readyState?run():document.addEventListener("DOMContentLoaded",(function(){run()}));
+13895 -7429
View File
File diff suppressed because it is too large. Load diff
+1 -2
View File
@@ -1,3 +1,2 @@
/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */ /*! For license information please see swagger-ui-bundle.js.LICENSE.txt */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,(()=>(()=>{var e={17967:(e,t)=>{"use strict";t.N=void 0;var n=/^([^\w]*)(javascript|data|vbscript)/im,r=/&#(\w+)(^\w|;)?/g,o=/&(newline|tab);/gi,s=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,i=/^.+(:|&colon;)/gim,a=[".","/"];t.N=function(e){var t,l=(t=e||"",t.replace(r,(function(e,t){return String.fromCharCode(t)}))).replace(o,"").replace(s,"").trim();if(!l)return"about:blank";if(function(e){return a.indexOf(e[0])>-1}(l))return l;var c=l.match(i);if(!c)return l;var u=c[0];return n.test(u)?"about:blank":l}},53795:(e,t,n)=>{"use strict";n.d(t,{Z:()=>P});var r=n(23101),o=n.n(r),s=n(61125),i=n.n(s),a=n(11882),l=n.n(a),c=n(97606),u=n.n(c),p=n(67294),h=n(43393);function f(e){return f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f(e)}function d(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function y(e){return y=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},y(e)}function v(e,t){return v=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},v(e,t)}function b(e,t){return!t||"object"!=typeof t&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}var w={};function E(e,t,n){return function(e){return null==e}(e)?n:function(e){return null!==e&&"object"===f(e)&&"function"==typeof e.get&&"function"==typeof e.has}(e)?e.has(t)?e.get(t):n:hasOwnProperty.call(e,t)?e[t]:n}function x(e,t,n){for(var r=0;r!==t.length;)if((e=E(e,t[r++],w))===w)return n;return e}function S(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=function(e,t){return function(n){if("string"==typeof n)return(0,h.is)(t[n],e[n]);if(Array.isArray(n))return(0,h.is)(x(t,n),x(e,n));throw new TypeError("Invalid key: expected Array or string: "+n)}}(t,n),o=e||Object.keys(function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?g(n,!0).forEach((function(t){m(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):g(n).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({},n,{},t));return o.every(r)}const _=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),b(this,y(t).apply(this,arguments))}var n,r,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&v(e,t)}(t,e),n=t,r=[{key:"shouldComponentUpdate",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return!S(this.updateOnProps,this.props,e,"updateOnProps")||!S(this.updateOnStates,this.state,t,"updateOnStates")}}],r&&d(n.prototype,r),o&&d(n,o),t}(p.Component);var j=n(23930),O=n.n(j),k=n(45697),A=n.n(k);const C=e=>{const t=e.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(t)}catch{return t}};class P extends _{constructor(){super(...arguments),i()(this,"getModelName",(e=>-1!==l()(e).call(e,"#/definitions/")?C(e.replace(/^.*#\/definitions\//,"")):-1!==l()(e).call(e,"#/components/schemas/")?C(e.replace(/^.*#\/components\/schemas\//,"")):void 0)),i()(this,"getRefSchema",(e=>{let{specSelectors:t}=this.props;return t.findDefinition(e)}))}render(){let{getComponent:e,getConfigs:t,specSelectors:r,schema:s,required:i,name:a,isRef:l,specPath:c,displayName:u,includeReadOnly:h,includeWriteOnly:f}=this.props;const d=e("ObjectModel"),m=e("ArrayModel"),g=e("PrimitiveModel");let y="object",v=s&&s.get("$$ref");if(!a&&v&&(a=this.getModelName(v)),!s&&v&&(s=this.getRefSchema(a)),!s)return p.createElement("span",{className:"model model-title"},p.createElement("span",{className:"model-title__text"},u||a),p.createElement("img",{src:n(2517),height:"20px",width:"20px"}));const b=r.isOAS3()&&s.get("deprecated");switch(l=void 0!==l?l:!!v,y=s&&s.get("type")||y,y){case"object":return p.createElement(d,o()({className:"object"},this.props,{specPath:c,getConfigs:t,scLine truncated !function webpackUniversalModuleDefinition(s,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.SwaggerUIBundle=o():s.SwaggerUIBundle=o()}(this,(()=>(()=>{var s={67526(s,o){"use strict";o.byteLength=function byteLength(s){var o=getLens(s),i=o[0],a=o[1];return 3*(i+a)/4-a},o.toByteArray=function toByteArray(s){var o,i,_=getLens(s),w=_[0],x=_[1],C=new u(function _byteLength(s,o,i){return 3*(o+i)/4-i}(0,w,x)),j=0,L=x>0?w-4:w;for(i=0;i<L;i+=4)o=a[s.charCodeAt(i)]<<18|a[s.charCodeAt(i+1)]<<12|a[s.charCodeAt(i+2)]<<6|a[s.charCodeAt(i+3)],C[j++]=o>>16&255,C[j++]=o>>8&255,C[j++]=255&o;2===x&&(o=a[s.charCodeAt(i)]<<2|a[s.charCodeAt(i+1)]>>4,C[j++]=255&o);1===x&&(o=a[s.charCodeAt(i)]<<10|a[s.charCodeAt(i+1)]<<4|a[s.charCodeAt(i+2)]>>2,C[j++]=o>>8&255,C[j++]=255&o);return C},o.fromByteArray=function fromByteArray(s){for(var o,a=s.length,u=a%3,_=[],w=16383,x=0,C=a-u;x<C;x+=w)_.push(encodeChunk(s,x,x+w>C?C:x+w));1===u?(o=s[a-1],_.push(i[o>>2]+i[o<<4&63]+"==")):2===u&&(o=(s[a-2]<<8)+s[a-1],_.push(i[o>>10]+i[o>>4&63]+i[o<<2&63]+"="));return _.join("")};for(var i=[],a=[],u="undefined"!=typeof Uint8Array?Uint8Array:Array,_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",w=0;w<64;++w)i[w]=_[w],a[_.charCodeAt(w)]=w;function getLens(s){var o=s.length;if(o%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var i=s.indexOf("=");return-1===i&&(i=o),[i,i===o?0:4-i%4]}function encodeChunk(s,o,a){for(var u,_,w=[],x=o;x<a;x+=3)u=(s[x]<<16&16711680)+(s[x+1]<<8&65280)+(255&s[x+2]),w.push(i[(_=u)>>18&63]+i[_>>12&63]+i[_>>6&63]+i[63&_]);return w.join("")}a["-".charCodeAt(0)]=62,a["_".charCodeAt(0)]=63},48287(s,o,i){"use strict";const a=i(67526),u=i(251),_="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;o.Buffer=Buffer,o.SlowBuffer=function SlowBuffer(s){+s!=s&&(s=0);return Buffer.alloc(+s)},o.INSPECT_MAX_BYTES=50;const w=2147483647;function createBuffer(s){if(s>w)throw new RangeError('The value "'+s+'" is invalid for option "size"');const o=new Uint8Array(s);return Object.setPrototypeOf(o,Buffer.prototype),o}function Buffer(s,o,i){if("number"==typeof s){if("string"==typeof o)throw new TypeError('The "string" argument must be of type string. Received type number');return allocUnsafe(s)}return from(s,o,i)}function from(s,o,i){if("string"==typeof s)return function fromString(s,o){"string"==typeof o&&""!==o||(o="utf8");if(!Buffer.isEncoding(o))throw new TypeError("Unknown encoding: "+o);const i=0|byteLength(s,o);let a=createBuffer(i);const u=a.write(s,o);u!==i&&(a=a.slice(0,u));return a}(s,o);if(ArrayBuffer.isView(s))return function fromArrayView(s){if(isInstance(s,Uint8Array)){const o=new Uint8Array(s);return fromArrayBuffer(o.buffer,o.byteOffset,o.byteLength)}return fromArrayLike(s)}(s);if(null==s)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s);if(isInstance(s,ArrayBuffer)||s&&isInstance(s.buffer,ArrayBuffer))return fromArrayBuffer(s,o,i);if("undefined"!=typeof SharedArrayBuffer&&(isInstance(s,SharedArrayBuffer)||s&&isInstance(s.buffer,SharedArrayBuffer)))return fromArrayBuffer(s,o,i);if("number"==typeof s)throw new TypeError('The "value" argument must not be of type number. Received type number');const a=s.valueOf&&s.valueOf();if(null!=a&&a!==s)return Buffer.from(a,o,i);const u=function fromObject(s){if(Buffer.isBuffer(s)){const o=0|checked(s.length),i=createBuffer(o);return 0===i.length||s.copy(i,0,0,o),i}if(void 0!==s.length)return"number"!=typeof s.length||numberIsNaN(s.length)?createBuffer(0):fromArrayLike(s);if("Buffer"===s.type&&Array.isArray(s.data))return fromArrayLike(s.data)}(s);if(u)return u;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof s[Symbol.toPrimitive])return Buffer.from(s[Symbol.toPrimitive]("string"),o,i);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s)}function assertSize(s){if("number"!=typeof s)throw new TypeError('"size" argument must be of type number');if(s<0)throw new RangeError('The value "'+s+'" is invalid for option "size"')}function allocUnsafe(s){return assertSize(s),createBuffer(s<0?0:0|checked(s))}function fromArrayLike(s){const o=s.length<0?0:0|checked(s.length),i=createBuffer(o);for(let a=0;a<o;a+=1)i[a]=255&s[a];return i}function fromArrayBuffer(s,o,i){if(o<0||s.byteLength<o)throw new RangeError('"offset" is outside of buffer bounds');if(s.byteLength<o+(i||0))throw new RangeError('"length" is outside of buffer bounds');let a;return a=void 0===o&&void 0===i?new Uint8Array(s):void 0===i?new Uint8Array(s,o):new Uint8Array(s,o,i),Object.setPrototypeOf(a,Buffer.prototype),a}function checked(s){if(s>=w)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+w.toString(16)+" Line truncated
//# sourceMappingURL=swagger-ui-bundle.js.map
@@ -0,0 +1,131 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*!
* https://github.com/Starcounter-Jack/JSON-Patch
* (c) 2017-2021 Joachim Wester
* MIT license
*/
/*!
* https://github.com/Starcounter-Jack/JSON-Patch
* (c) 2017-2022 Joachim Wester
* MIT licensed
*/
/*!
* repeat-string <https://github.com/jonschlinkert/repeat-string>
*
* Copyright (c) 2014-2015, Jon Schlinkert.
* Licensed under the MIT License.
*/
/*! @license DOMPurify 3.4.17 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.17/LICENSE */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* @license
* MIT License
*
* Copyright (c) 2014-present, Lee Byron and other contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-with-selector.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","mappings":"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 truncated {"version":3,"file":"swagger-ui-bundle.js","mappings":"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 truncated
+1 -1
View File
@@ -1,3 +1,3 @@
/*! For license information please see swagger-ui-es-bundle-core.js.LICENSE.txt */ /*! For license information please see swagger-ui-es-bundle-core.js.LICENSE.txt */
import*as e from"react-immutable-pure-component";import*as t from"remarkable/linkify";import*as a from"dompurify";import*as n from"zenscroll";import*as r from"lodash/reduce";import*as s from"@babel/runtime-corejs3/core-js-stable/string/raw";import*as o from"@babel/runtime-corejs3/core-js-stable/number/epsilon";import*as l from"@babel/runtime-corejs3/core-js-stable/instance/repeat";import*as i from"@babel/runtime-corejs3/core-js-stable/instance/fill";import*as c from"lodash/zipObject";import*as u from"@babel/runtime-corejs3/core-js-stable/promise";import*as d from"@babel/runtime-corejs3/core-js-stable/date/now";import*as p from"lodash/isString";import*as m from"lodash/debounce";import*as f from"lodash/set";import*as h from"lodash/fp/assocPath";import*as g from"swagger-client/es/resolver/strategies/generic";import*as y from"swagger-client/es/resolver/strategies/openapi-2";import*as v from"swagger-client/es/resolver/strategies/openapi-3-0";import*as E from"swagger-client/es/resolver/strategies/openapi-3-1-apidom";import*as S from"swagger-client/es/resolver";import*as w from"swagger-client/es/execute";import*as b from"swagger-client/es/http";import*as x from"swagger-client/es/subtree-resolver";import*as _ from"react-dom";import*as C from"react-redux";import*as j from"lodash/omit";import*as O from"react-syntax-highlighter/dist/esm/light";import*as N from"react-syntax-highlighter/dist/esm/languages/hljs/javascript";import*as k from"react-syntax-highlighter/dist/esm/languages/hljs/json";import*as A from"react-syntax-highlighter/dist/esm/languages/hljs/xml";import*as I from"react-syntax-highlighter/dist/esm/languages/hljs/bash";import*as R from"react-syntax-highlighter/dist/esm/languages/hljs/yaml";import*as P from"react-syntax-highlighter/dist/esm/languages/hljs/http";import*as T from"react-syntax-highlighter/dist/esm/languages/hljs/powershell";import*as M from"react-syntax-highlighter/dist/esm/styles/hljs/agate";import*as D from"react-syntax-highlighter/dist/esm/styles/hljs/arta";import*as L from"react-syntax-highlighter/dist/esm/styles/hljs/monokai";import*as q from"react-syntax-highlighter/dist/esm/styles/hljs/nord";import*as B from"react-syntax-highlighter/dist/esm/styles/hljs/obsidian";import*as U from"react-syntax-highlighter/dist/esm/styles/hljs/tomorrow-night";import*as J from"@babel/runtime-corejs3/core-js-stable/instance/starts-with";import*as $ from"@braintree/sanitize-url";import*as K from"lodash/camelCase";import*as V from"lodash/upperFirst";import*as F from"lodash/find";import*as z from"lodash/eq";import*as W from"css.escape";import*as H from"@babel/runtime-corejs3/core-js-stable/instance/find-index";import*as G from"@babel/runtime-corejs3/core-js-stable/array/from";import*as Z from"@babel/runtime-corejs3/core-js-stable/array/is-array";import*as Y from"@babel/runtime-corejs3/core-js-stable/instance/bind";import*as X from"@babel/runtime-corejs3/core-js-stable/instance/concat";import*as Q from"@babel/runtime-corejs3/core-js-stable/instance/entries";import*as ee from"@babel/runtime-corejs3/core-js-stable/instance/every";import*as te from"@babel/runtime-corejs3/core-js-stable/instance/filter";import*as ae from"@babel/runtime-corejs3/core-js-stable/instance/find";import*as ne from"@babel/runtime-corejs3/core-js-stable/instance/for-each";import*as re from"@babel/runtime-corejs3/core-js-stable/instance/includes";import*as se from"@babel/runtime-corejs3/core-js-stable/instance/index-of";import*as oe from"@babel/runtime-corejs3/core-js-stable/instance/keys";import*as le from"@babel/runtime-corejs3/core-js-stable/instance/map";import*as ie from"@babel/runtime-corejs3/core-js-stable/instance/reduce";import*as ce from"@babel/runtime-corejs3/core-js-stable/instance/slice";import*as ue from"@babel/runtime-corejs3/core-js-stable/instance/some";import*as de from"@babel/runtime-corejs3/core-js-stable/instance/sort";import*as pe from"@babel/runtime-corejs3/core-js-stable/instance/trim";import*as me from"@babel/runtime-corejs3/core-js-stable/json/stringify";import*as fe from"@babel/runtime-corejs3/core-js-stable/map";import*as he from"@babel/runtime-corejs3/core-js-stable/number/is-integer";import*as ge from"@babel/runtime-corejs3/core-js-stable/object/assign";import*as ye from"@babel/runtime-corejs3/core-js-stable/object/entries";import*as ve from"@babel/runtime-corejs3/core-js-stable/object/from-entries";import*as Ee from"@babel/runtime-corejs3/core-js-stable/object/keys";import*as Se from"@babel/runtime-corejs3/core-js-stable/object/values";import*as we from"@babel/runtime-corejs3/core-js-stable/set";import*as be from"@babel/runtime-corejs3/core-js-stable/set-timeout";import*as xe from"@babel/runtime-corejs3/core-js-stable/url";import*as _e from"@babel/runtime-corejs3/core-js-stable/weak-map";import*as Ce from"@babel/runtime-corejs3/core-js-stable/weak-set";import*as je from"@babel/runtime-corejs3/helpers/classPrivateFieldGet";import*as Oe from"@babel/runtime-corejs3/helpers/defineProperty";import*as Ne from"@babelLine truncated import*as e from"base64-js";import*as t from"ieee754";import*as r from"to-buffer";import{Component as n,PureComponent as a,createContext as s,createElement as o,default as i,forwardRef as l,useCallback as c,useContext as u,useEffect as p,useLayoutEffect as m,useMemo as d,useRef as h,useState as f}from"react";import{applyMiddleware as g,bindActionCreators as y,compose as S,createStore as v}from"redux";import*as b from"immutable";import{serializeError as w}from"serialize-error";import{default as _}from"lodash/merge";import"lodash/camelCase";import"lodash/upperFirst";import{default as C}from"lodash/memoize";import{default as x}from"lodash/find";import{default as O}from"lodash/some";import{default as N}from"lodash/eq";import{default as A}from"lodash/isFunction";import{default as k}from"css.escape";import{default as I}from"url-parse";import{createSelector as j}from"reselect";import{default as T}from"prop-types";import{default as R}from"lodash/omit";import{JSON_SCHEMA as M,default as P}from"js-yaml";import{default as B}from"zenscroll";import{default as L}from"react-immutable-proptypes";import{default as q}from"lodash/reduce";import{default as U}from"lodash/get";import{default as D}from"@babel/runtime-corejs3/helpers/extends";import{default as J}from"classnames";import{CopyToClipboard as $}from"react-copy-to-clipboard";import{default as V}from"react-immutable-pure-component";import{useVirtualizer as F,useWindowVirtualizer as z}from"@tanstack/react-virtual";import{default as K}from"react-debounce-input";import{default as W}from"lodash/isPlainObject";import{default as H}from"randexp";import{default as G}from"lodash/isEmpty";import{default as X}from"lodash/constant";import{default as Y}from"lodash/isString";import{default as Q}from"lodash/debounce";import{default as Z}from"lodash/set";import{default as ee}from"lodash/fp/assocPath";import{default as te}from"swagger-client/es/resolver/strategies/generic";import{default as re}from"swagger-client/es/resolver/strategies/openapi-2";import{default as ne}from"swagger-client/es/resolver/strategies/openapi-3-0";import{default as ae}from"swagger-client/es/resolver/strategies/openapi-3-1-apidom";import{default as se}from"swagger-client/es/resolver/strategies/openapi-3-2-apidom";import{makeResolve as oe}from"swagger-client/es/resolver";import{buildRequest as ie,execute as le}from"swagger-client/es/execute";import{default as ce,makeHttp as ue,serializeRes as pe}from"swagger-client/es/http";import{makeResolveSubtree as me}from"swagger-client/es/subtree-resolver";import{opId as de}from"swagger-client/es/helpers";import{default as he}from"react-dom";import{Provider as fe,connect as ge}from"react-redux";import{default as ye}from"lodash/identity";import{default as Se}from"react-syntax-highlighter/dist/esm/light";import{default as Ee}from"react-syntax-highlighter/dist/esm/languages/hljs/javascript";import{default as ve}from"react-syntax-highlighter/dist/esm/languages/hljs/json";import{default as be}from"react-syntax-highlighter/dist/esm/languages/hljs/xml";import{default as we}from"react-syntax-highlighter/dist/esm/languages/hljs/bash";import{default as _e}from"react-syntax-highlighter/dist/esm/languages/hljs/yaml";import{default as Ce}from"react-syntax-highlighter/dist/esm/languages/hljs/http";import{default as xe}from"react-syntax-highlighter/dist/esm/languages/hljs/powershell";import{default as Oe}from"react-syntax-highlighter/dist/esm/styles/hljs/agate";import{default as Ne}from"react-syntax-highlighter/dist/esm/styles/hljs/arta";import{default as Ae}from"react-syntax-highlighter/dist/esm/styles/hljs/monokai";import{default as ke}from"react-syntax-highlighter/dist/esm/styles/hljs/nord";import{default as Ie}from"react-syntax-highlighter/dist/esm/styles/hljs/obsidian";import{default as je}from"react-syntax-highlighter/dist/esm/styles/hljs/tomorrow-night";import{default as Te}from"react-syntax-highlighter/dist/esm/styles/hljs/idea";import{default as Re}from"js-file-download";import{default as Me}from"lodash/zipObject";import{default as Pe}from"lodash/toString";import{default as Be}from"xml-but-prettier";import{default as Le}from"lodash/toLower";import{Remarkable as qe}from"remarkable";import{linkify as Ue}from"remarkable/linkify";import{default as De}from"dompurify";import{default as Je}from"lodash/escapeRegExp";import{default as $e}from"lodash/has";import{default as Ve}from"lodash/fp/set";var Fe={287:function(e,t,r){const n=r(987),a=r(362),s="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=Buffer,t.SlowBuffer=function SlowBuffer(e){+e!=e&&(e=0);return Buffer.alloc(+e)},t.INSPECT_MAX_BYTES=50;const o=2147483647;function createBuffer(e){if(e>o)throw new RangeError('The value "'+e+'" is invalid for option "size"');const t=new Uint8Array(e);return Object.setPrototypeOf(t,Buffer.prototype),t}function Buffer(e,t,r){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. ReLine truncated
//# sourceMappingURL=swagger-ui-es-bundle-core.js.map //# sourceMappingURL=swagger-ui-es-bundle-core.js.map
@@ -0,0 +1,35 @@
/*!
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-es-bundle-core.js","mappings":"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 truncated {"version":3,"file":"swagger-ui-es-bundle-core.js","mappings":"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 truncated
+1 -2
View File
@@ -1,3 +1,2 @@
/*! For license information please see swagger-ui-es-bundle.js.LICENSE.txt */ /*! For license information please see swagger-ui-es-bundle.js.LICENSE.txt */
(()=>{var e={17967:(e,t)=>{"use strict";t.N=void 0;var n=/^([^\w]*)(javascript|data|vbscript)/im,r=/&#(\w+)(^\w|;)?/g,o=/&(newline|tab);/gi,s=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,i=/^.+(:|&colon;)/gim,a=[".","/"];t.N=function(e){var t,l=(t=e||"",t.replace(r,(function(e,t){return String.fromCharCode(t)}))).replace(o,"").replace(s,"").trim();if(!l)return"about:blank";if(function(e){return a.indexOf(e[0])>-1}(l))return l;var c=l.match(i);if(!c)return l;var u=c[0];return n.test(u)?"about:blank":l}},53795:(e,t,n)=>{"use strict";n.d(t,{Z:()=>P});var r=n(23101),o=n.n(r),s=n(61125),i=n.n(s),a=n(11882),l=n.n(a),c=n(97606),u=n.n(c),p=n(67294),h=n(43393);function f(e){return f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f(e)}function d(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function y(e){return y=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},y(e)}function v(e,t){return v=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},v(e,t)}function b(e,t){return!t||"object"!=typeof t&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}var w={};function E(e,t,n){return function(e){return null==e}(e)?n:function(e){return null!==e&&"object"===f(e)&&"function"==typeof e.get&&"function"==typeof e.has}(e)?e.has(t)?e.get(t):n:hasOwnProperty.call(e,t)?e[t]:n}function x(e,t,n){for(var r=0;r!==t.length;)if((e=E(e,t[r++],w))===w)return n;return e}function S(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=function(e,t){return function(n){if("string"==typeof n)return(0,h.is)(t[n],e[n]);if(Array.isArray(n))return(0,h.is)(x(t,n),x(e,n));throw new TypeError("Invalid key: expected Array or string: "+n)}}(t,n),o=e||Object.keys(function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?g(n,!0).forEach((function(t){m(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):g(n).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({},n,{},t));return o.every(r)}const _=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),b(this,y(t).apply(this,arguments))}var n,r,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&v(e,t)}(t,e),n=t,r=[{key:"shouldComponentUpdate",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return!S(this.updateOnProps,this.props,e,"updateOnProps")||!S(this.updateOnStates,this.state,t,"updateOnStates")}}],r&&d(n.prototype,r),o&&d(n,o),t}(p.Component);var j=n(23930),O=n.n(j),k=n(45697),A=n.n(k);const C=e=>{const t=e.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(t)}catch{return t}};class P extends _{constructor(){super(...arguments),i()(this,"getModelName",(e=>-1!==l()(e).call(e,"#/definitions/")?C(e.replace(/^.*#\/definitions\//,"")):-1!==l()(e).call(e,"#/components/schemas/")?C(e.replace(/^.*#\/components\/schemas\//,"")):void 0)),i()(this,"getRefSchema",(e=>{let{specSelectors:t}=this.props;return t.findDefinition(e)}))}render(){let{getComponent:e,getConfigs:t,specSelectors:r,schema:s,required:i,name:a,isRef:l,specPath:c,displayName:u,includeReadOnly:h,includeWriteOnly:f}=this.props;const d=e("ObjectModel"),m=e("ArrayModel"),g=e("PrimitiveModel");let y="object",v=s&&s.get("$$ref");if(!a&&v&&(a=this.getModelName(v)),!s&&v&&(s=this.getRefSchema(a)),!s)return p.createElement("span",{className:"model model-title"},p.createElement("span",{className:"model-title__text"},u||a),p.createElement("img",{src:n(2517),height:"20px",width:"20px"}));const b=r.isOAS3()&&s.get("deprecated");switch(l=void 0!==l?l:!!v,y=s&&s.get("type")||y,y){case"object":return p.createElement(d,o()({className:"object"},this.props,{specPath:c,getConfigs:t,schema:s,name:a,deprecated:b,isRef:l,includeReadOnly:h,includeWriteOnly:f}));case"array":return p.createElement(m,o()({className:"array"},this.props,{getConfigs:t,schema:s,name:a,deprecated:b,required:i,includeReadOnly:h,inLine truncated (()=>{var s={67526(s,o){"use strict";o.byteLength=function byteLength(s){var o=getLens(s),i=o[0],a=o[1];return 3*(i+a)/4-a},o.toByteArray=function toByteArray(s){var o,i,_=getLens(s),w=_[0],x=_[1],C=new u(function _byteLength(s,o,i){return 3*(o+i)/4-i}(0,w,x)),j=0,L=x>0?w-4:w;for(i=0;i<L;i+=4)o=a[s.charCodeAt(i)]<<18|a[s.charCodeAt(i+1)]<<12|a[s.charCodeAt(i+2)]<<6|a[s.charCodeAt(i+3)],C[j++]=o>>16&255,C[j++]=o>>8&255,C[j++]=255&o;2===x&&(o=a[s.charCodeAt(i)]<<2|a[s.charCodeAt(i+1)]>>4,C[j++]=255&o);1===x&&(o=a[s.charCodeAt(i)]<<10|a[s.charCodeAt(i+1)]<<4|a[s.charCodeAt(i+2)]>>2,C[j++]=o>>8&255,C[j++]=255&o);return C},o.fromByteArray=function fromByteArray(s){for(var o,a=s.length,u=a%3,_=[],w=16383,x=0,C=a-u;x<C;x+=w)_.push(encodeChunk(s,x,x+w>C?C:x+w));1===u?(o=s[a-1],_.push(i[o>>2]+i[o<<4&63]+"==")):2===u&&(o=(s[a-2]<<8)+s[a-1],_.push(i[o>>10]+i[o>>4&63]+i[o<<2&63]+"="));return _.join("")};for(var i=[],a=[],u="undefined"!=typeof Uint8Array?Uint8Array:Array,_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",w=0;w<64;++w)i[w]=_[w],a[_.charCodeAt(w)]=w;function getLens(s){var o=s.length;if(o%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var i=s.indexOf("=");return-1===i&&(i=o),[i,i===o?0:4-i%4]}function encodeChunk(s,o,a){for(var u,_,w=[],x=o;x<a;x+=3)u=(s[x]<<16&16711680)+(s[x+1]<<8&65280)+(255&s[x+2]),w.push(i[(_=u)>>18&63]+i[_>>12&63]+i[_>>6&63]+i[63&_]);return w.join("")}a["-".charCodeAt(0)]=62,a["_".charCodeAt(0)]=63},48287(s,o,i){"use strict";const a=i(67526),u=i(251),_="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;o.Buffer=Buffer,o.SlowBuffer=function SlowBuffer(s){+s!=s&&(s=0);return Buffer.alloc(+s)},o.INSPECT_MAX_BYTES=50;const w=2147483647;function createBuffer(s){if(s>w)throw new RangeError('The value "'+s+'" is invalid for option "size"');const o=new Uint8Array(s);return Object.setPrototypeOf(o,Buffer.prototype),o}function Buffer(s,o,i){if("number"==typeof s){if("string"==typeof o)throw new TypeError('The "string" argument must be of type string. Received type number');return allocUnsafe(s)}return from(s,o,i)}function from(s,o,i){if("string"==typeof s)return function fromString(s,o){"string"==typeof o&&""!==o||(o="utf8");if(!Buffer.isEncoding(o))throw new TypeError("Unknown encoding: "+o);const i=0|byteLength(s,o);let a=createBuffer(i);const u=a.write(s,o);u!==i&&(a=a.slice(0,u));return a}(s,o);if(ArrayBuffer.isView(s))return function fromArrayView(s){if(isInstance(s,Uint8Array)){const o=new Uint8Array(s);return fromArrayBuffer(o.buffer,o.byteOffset,o.byteLength)}return fromArrayLike(s)}(s);if(null==s)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s);if(isInstance(s,ArrayBuffer)||s&&isInstance(s.buffer,ArrayBuffer))return fromArrayBuffer(s,o,i);if("undefined"!=typeof SharedArrayBuffer&&(isInstance(s,SharedArrayBuffer)||s&&isInstance(s.buffer,SharedArrayBuffer)))return fromArrayBuffer(s,o,i);if("number"==typeof s)throw new TypeError('The "value" argument must not be of type number. Received type number');const a=s.valueOf&&s.valueOf();if(null!=a&&a!==s)return Buffer.from(a,o,i);const u=function fromObject(s){if(Buffer.isBuffer(s)){const o=0|checked(s.length),i=createBuffer(o);return 0===i.length||s.copy(i,0,0,o),i}if(void 0!==s.length)return"number"!=typeof s.length||numberIsNaN(s.length)?createBuffer(0):fromArrayLike(s);if("Buffer"===s.type&&Array.isArray(s.data))return fromArrayLike(s.data)}(s);if(u)return u;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof s[Symbol.toPrimitive])return Buffer.from(s[Symbol.toPrimitive]("string"),o,i);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s)}function assertSize(s){if("number"!=typeof s)throw new TypeError('"size" argument must be of type number');if(s<0)throw new RangeError('The value "'+s+'" is invalid for option "size"')}function allocUnsafe(s){return assertSize(s),createBuffer(s<0?0:0|checked(s))}function fromArrayLike(s){const o=s.length<0?0:0|checked(s.length),i=createBuffer(o);for(let a=0;a<o;a+=1)i[a]=255&s[a];return i}function fromArrayBuffer(s,o,i){if(o<0||s.byteLength<o)throw new RangeError('"offset" is outside of buffer bounds');if(s.byteLength<o+(i||0))throw new RangeError('"length" is outside of buffer bounds');let a;return a=void 0===o&&void 0===i?new Uint8Array(s):void 0===i?new Uint8Array(s,o):new Uint8Array(s,o,i),Object.setPrototypeOf(a,Buffer.prototype),a}function checked(s){if(s>=w)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+w.toString(16)+" bytes");return 0|s}function byteLength(s,o){if(Buffer.isBuffer(s))return s.length;if(ArrayBuffer.isView(s)||isInstance(s,ArrayBuffer))return s.byteLength;if("string"!=typeof s)throw new TypeError('The "string" argument must be one of type string, Buffer,Line truncated
//# sourceMappingURL=swagger-ui-es-bundle.js.map
@@ -0,0 +1,131 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*!
* https://github.com/Starcounter-Jack/JSON-Patch
* (c) 2017-2021 Joachim Wester
* MIT license
*/
/*!
* https://github.com/Starcounter-Jack/JSON-Patch
* (c) 2017-2022 Joachim Wester
* MIT licensed
*/
/*!
* repeat-string <https://github.com/jonschlinkert/repeat-string>
*
* Copyright (c) 2014-2015, Jon Schlinkert.
* Licensed under the MIT License.
*/
/*! @license DOMPurify 3.4.17 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.17/LICENSE */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* @license
* MIT License
*
* Copyright (c) 2014-present, Lee Byron and other contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-with-selector.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-es-bundle.js","mappings":"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 truncated {"version":3,"file":"swagger-ui-es-bundle.js","mappings":"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 truncated
+1 -2
View File
@@ -1,3 +1,2 @@
/*! For license information please see swagger-ui-standalone-preset.js.LICENSE.txt */ /*! For license information please see swagger-ui-standalone-preset.js.LICENSE.txt */
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.SwaggerUIStandalonePreset=e():t.SwaggerUIStandalonePreset=e()}(this,(()=>(()=>{var t={7967:(t,e)=>{"use strict";var r=/^([^\w]*)(javascript|data|vbscript)/im,n=/&#(\w+)(^\w|;)?/g,i=/&(newline|tab);/gi,o=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,u=/^.+(:|&colon;)/gim,s=[".","/"]},9742:(t,e)=>{"use strict";e.byteLength=function(t){var e=s(t),r=e[0],n=e[1];return 3*(r+n)/4-n},e.toByteArray=function(t){var e,r,o=s(t),u=o[0],a=o[1],c=new i(function(t,e,r){return 3*(e+r)/4-r}(0,u,a)),f=0,l=a>0?u-4:u;for(r=0;r<l;r+=4)e=n[t.charCodeAt(r)]<<18|n[t.charCodeAt(r+1)]<<12|n[t.charCodeAt(r+2)]<<6|n[t.charCodeAt(r+3)],c[f++]=e>>16&255,c[f++]=e>>8&255,c[f++]=255&e;2===a&&(e=n[t.charCodeAt(r)]<<2|n[t.charCodeAt(r+1)]>>4,c[f++]=255&e);1===a&&(e=n[t.charCodeAt(r)]<<10|n[t.charCodeAt(r+1)]<<4|n[t.charCodeAt(r+2)]>>2,c[f++]=e>>8&255,c[f++]=255&e);return c},e.fromByteArray=function(t){for(var e,n=t.length,i=n%3,o=[],u=16383,s=0,c=n-i;s<c;s+=u)o.push(a(t,s,s+u>c?c:s+u));1===i?(e=t[n-1],o.push(r[e>>2]+r[e<<4&63]+"==")):2===i&&(e=(t[n-2]<<8)+t[n-1],o.push(r[e>>10]+r[e>>4&63]+r[e<<2&63]+"="));return o.join("")};for(var r=[],n=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",u=0;u<64;++u)r[u]=o[u],n[o.charCodeAt(u)]=u;function s(t){var e=t.length;if(e%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var r=t.indexOf("=");return-1===r&&(r=e),[r,r===e?0:4-r%4]}function a(t,e,n){for(var i,o,u=[],s=e;s<n;s+=3)i=(t[s]<<16&16711680)+(t[s+1]<<8&65280)+(255&t[s+2]),u.push(r[(o=i)>>18&63]+r[o>>12&63]+r[o>>6&63]+r[63&o]);return u.join("")}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63},8764:(t,e,r)=>{"use strict";const n=r(9742),i=r(645),o="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;e.Buffer=a,e.SlowBuffer=function(t){+t!=t&&(t=0);return a.alloc(+t)},e.INSPECT_MAX_BYTES=50;const u=2147483647;function s(t){if(t>u)throw new RangeError('The value "'+t+'" is invalid for option "size"');const e=new Uint8Array(t);return Object.setPrototypeOf(e,a.prototype),e}function a(t,e,r){if("number"==typeof t){if("string"==typeof e)throw new TypeError('The "string" argument must be of type string. Received type number');return l(t)}return c(t,e,r)}function c(t,e,r){if("string"==typeof t)return function(t,e){"string"==typeof e&&""!==e||(e="utf8");if(!a.isEncoding(e))throw new TypeError("Unknown encoding: "+e);const r=0|M(t,e);let n=s(r);const i=n.write(t,e);i!==r&&(n=n.slice(0,i));return n}(t,e);if(ArrayBuffer.isView(t))return function(t){if($(t,Uint8Array)){const e=new Uint8Array(t);return p(e.buffer,e.byteOffset,e.byteLength)}return h(t)}(t);if(null==t)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if($(t,ArrayBuffer)||t&&$(t.buffer,ArrayBuffer))return p(t,e,r);if("undefined"!=typeof SharedArrayBuffer&&($(t,SharedArrayBuffer)||t&&$(t.buffer,SharedArrayBuffer)))return p(t,e,r);if("number"==typeof t)throw new TypeError('The "value" argument must not be of type number. Received type number');const n=t.valueOf&&t.valueOf();if(null!=n&&n!==t)return a.from(n,e,r);const i=function(t){if(a.isBuffer(t)){const e=0|y(t.length),r=s(e);return 0===r.length||t.copy(r,0,0,e),r}if(void 0!==t.length)return"number"!=typeof t.length||X(t.length)?s(0):h(t);if("Buffer"===t.type&&Array.isArray(t.data))return h(t.data)}(t);if(i)return i;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof t[Symbol.toPrimitive])return a.from(t[Symbol.toPrimitive]("string"),e,r);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t)}function f(t){if("number"!=typeof t)throw new TypeError('"size" argument must be of type number');if(t<0)throw new RangeError('The value "'+t+'" is invalid for option "size"')}function l(t){return f(t),s(t<0?0:0|y(t))}function h(t){const e=t.length<0?0:0|y(t.length),r=s(e);for(let n=0;n<e;n+=1)r[n]=255&t[n];return r}function p(t,e,r){if(e<0||t.byteLength<e)throw new RangeError('"offset" is outside of buffer bounds');if(t.byteLength<e+(r||0))throw new RangeError('"length" is outside of buffer bounds');let n;return n=void 0===e&&void 0===r?new Uint8Array(t):void 0===r?new Uint8Array(t,e):new Uint8Array(t,e,r),Object.setPrototypeOf(n,a.prototype),n}function y(t){if(t>=u)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+u.toString(16)+" bytes");return 0|t}function M(t,e){if(a.isBuffer(t))return t.length;if(ArrayBuffer.isView(t)||$(t,ArrayBuffer))return t.byteLength;if("string"!=typeof t)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof t);const r=t.leLine truncated !function webpackUniversalModuleDefinition(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.SwaggerUIStandalonePreset=e():t.SwaggerUIStandalonePreset=e()}(this,(()=>(()=>{var t={7526(t,e){"use strict";e.byteLength=function byteLength(t){var e=getLens(t),r=e[0],n=e[1];return 3*(r+n)/4-n},e.toByteArray=function toByteArray(t){var e,r,i=getLens(t),a=i[0],s=i[1],u=new o(function _byteLength(t,e,r){return 3*(e+r)/4-r}(0,a,s)),c=0,l=s>0?a-4:a;for(r=0;r<l;r+=4)e=n[t.charCodeAt(r)]<<18|n[t.charCodeAt(r+1)]<<12|n[t.charCodeAt(r+2)]<<6|n[t.charCodeAt(r+3)],u[c++]=e>>16&255,u[c++]=e>>8&255,u[c++]=255&e;2===s&&(e=n[t.charCodeAt(r)]<<2|n[t.charCodeAt(r+1)]>>4,u[c++]=255&e);1===s&&(e=n[t.charCodeAt(r)]<<10|n[t.charCodeAt(r+1)]<<4|n[t.charCodeAt(r+2)]>>2,u[c++]=e>>8&255,u[c++]=255&e);return u},e.fromByteArray=function fromByteArray(t){for(var e,n=t.length,o=n%3,i=[],a=16383,s=0,u=n-o;s<u;s+=a)i.push(encodeChunk(t,s,s+a>u?u:s+a));1===o?(e=t[n-1],i.push(r[e>>2]+r[e<<4&63]+"==")):2===o&&(e=(t[n-2]<<8)+t[n-1],i.push(r[e>>10]+r[e>>4&63]+r[e<<2&63]+"="));return i.join("")};for(var r=[],n=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0;a<64;++a)r[a]=i[a],n[i.charCodeAt(a)]=a;function getLens(t){var e=t.length;if(e%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var r=t.indexOf("=");return-1===r&&(r=e),[r,r===e?0:4-r%4]}function encodeChunk(t,e,n){for(var o,i,a=[],s=e;s<n;s+=3)o=(t[s]<<16&16711680)+(t[s+1]<<8&65280)+(255&t[s+2]),a.push(r[(i=o)>>18&63]+r[i>>12&63]+r[i>>6&63]+r[63&i]);return a.join("")}n["-".charCodeAt(0)]=62,n["_".charCodeAt(0)]=63},8287(t,e,r){"use strict";const n=r(7526),o=r(251),i="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;e.Buffer=Buffer,e.SlowBuffer=function SlowBuffer(t){+t!=t&&(t=0);return Buffer.alloc(+t)},e.INSPECT_MAX_BYTES=50;const a=2147483647;function createBuffer(t){if(t>a)throw new RangeError('The value "'+t+'" is invalid for option "size"');const e=new Uint8Array(t);return Object.setPrototypeOf(e,Buffer.prototype),e}function Buffer(t,e,r){if("number"==typeof t){if("string"==typeof e)throw new TypeError('The "string" argument must be of type string. Received type number');return allocUnsafe(t)}return from(t,e,r)}function from(t,e,r){if("string"==typeof t)return function fromString(t,e){"string"==typeof e&&""!==e||(e="utf8");if(!Buffer.isEncoding(e))throw new TypeError("Unknown encoding: "+e);const r=0|byteLength(t,e);let n=createBuffer(r);const o=n.write(t,e);o!==r&&(n=n.slice(0,o));return n}(t,e);if(ArrayBuffer.isView(t))return function fromArrayView(t){if(isInstance(t,Uint8Array)){const e=new Uint8Array(t);return fromArrayBuffer(e.buffer,e.byteOffset,e.byteLength)}return fromArrayLike(t)}(t);if(null==t)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(isInstance(t,ArrayBuffer)||t&&isInstance(t.buffer,ArrayBuffer))return fromArrayBuffer(t,e,r);if("undefined"!=typeof SharedArrayBuffer&&(isInstance(t,SharedArrayBuffer)||t&&isInstance(t.buffer,SharedArrayBuffer)))return fromArrayBuffer(t,e,r);if("number"==typeof t)throw new TypeError('The "value" argument must not be of type number. Received type number');const n=t.valueOf&&t.valueOf();if(null!=n&&n!==t)return Buffer.from(n,e,r);const o=function fromObject(t){if(Buffer.isBuffer(t)){const e=0|checked(t.length),r=createBuffer(e);return 0===r.length||t.copy(r,0,0,e),r}if(void 0!==t.length)return"number"!=typeof t.length||numberIsNaN(t.length)?createBuffer(0):fromArrayLike(t);if("Buffer"===t.type&&Array.isArray(t.data))return fromArrayLike(t.data)}(t);if(o)return o;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof t[Symbol.toPrimitive])return Buffer.from(t[Symbol.toPrimitive]("string"),e,r);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t)}function assertSize(t){if("number"!=typeof t)throw new TypeError('"size" argument must be of type number');if(t<0)throw new RangeError('The value "'+t+'" is invalid for option "size"')}function allocUnsafe(t){return assertSize(t),createBuffer(t<0?0:0|checked(t))}function fromArrayLike(t){const e=t.length<0?0:0|checked(t.length),r=createBuffer(e);for(let n=0;n<e;n+=1)r[n]=255&t[n];return r}function fromArrayBuffer(t,e,r){if(e<0||t.byteLength<e)throw new RangeError('"offset" is outside of buffer bounds');if(t.byteLength<e+(r||0))throw new RangeError('"length" is outside of buffer bounds');let n;return n=void 0===e&&void 0===r?new Uint8Array(t):void 0===r?new Uint8Array(t,e):new Uint8Array(t,e,r),Object.setPrototypeOf(n,Buffer.prototype),n}function checked(t){if(t>=a)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+Line truncated
//# sourceMappingURL=swagger-ui-standalone-preset.js.map
@@ -0,0 +1,45 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* @license
* MIT License
*
* Copyright (c) 2014-present, Lee Byron and other contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@@ -1 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","mappings":"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 truncated {"version":3,"file":"swagger-ui-standalone-preset.js","mappings":"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 truncated
+1 -1
View File
@@ -1,3 +1,3 @@
.swagger-ui{color:#3b4151;font-family:sans-serif/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */}.swagger-ui html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.15}.swagger-ui body{margin:0}.swagger-ui article,.swagger-ui aside,.swagger-ui footer,.swagger-ui header,.swagger-ui nav,.swagger-ui section{display:block}.swagger-ui h1{font-size:2em;margin:.67em 0}.swagger-ui figcaption,.swagger-ui figure,.swagger-ui main{display:block}.swagger-ui figure{margin:1em 40px}.swagger-ui hr{box-sizing:content-box;height:0;overflow:visible}.swagger-ui pre{font-family:monospace,monospace;font-size:1em}.swagger-ui a{-webkit-text-decoration-skip:objects;background-color:transparent}.swagger-ui abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.swagger-ui b,.swagger-ui strong{font-weight:inherit;font-weight:bolder}.swagger-ui code,.swagger-ui kbd,.swagger-ui samp{font-family:monospace,monospace;font-size:1em}.swagger-ui dfn{font-style:italic}.swagger-ui mark{background-color:#ff0;color:#000}.swagger-ui small{font-size:80%}.swagger-ui sub,.swagger-ui sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.swagger-ui sub{bottom:-.25em}.swagger-ui sup{top:-.5em}.swagger-ui audio,.swagger-ui video{display:inline-block}.swagger-ui audio:not([controls]){display:none;height:0}.swagger-ui img{border-style:none}.swagger-ui svg:not(:root){overflow:hidden}.swagger-ui button,.swagger-ui input,.swagger-ui optgroup,.swagger-ui select,.swagger-ui textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}.swagger-ui button,.swagger-ui input{overflow:visible}.swagger-ui button,.swagger-ui select{text-transform:none}.swagger-ui [type=reset],.swagger-ui [type=submit],.swagger-ui button,.swagger-ui html [type=button]{-webkit-appearance:button}.swagger-ui [type=button]::-moz-focus-inner,.swagger-ui [type=reset]::-moz-focus-inner,.swagger-ui [type=submit]::-moz-focus-inner,.swagger-ui button::-moz-focus-inner{border-style:none;padding:0}.swagger-ui [type=button]:-moz-focusring,.swagger-ui [type=reset]:-moz-focusring,.swagger-ui [type=submit]:-moz-focusring,.swagger-ui button:-moz-focusring{outline:1px dotted ButtonText}.swagger-ui fieldset{padding:.35em .75em .625em}.swagger-ui legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}.swagger-ui progress{display:inline-block;vertical-align:baseline}.swagger-ui textarea{overflow:auto}.swagger-ui [type=checkbox],.swagger-ui [type=radio]{box-sizing:border-box;padding:0}.swagger-ui [type=number]::-webkit-inner-spin-button,.swagger-ui [type=number]::-webkit-outer-spin-button{height:auto}.swagger-ui [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.swagger-ui [type=search]::-webkit-search-cancel-button,.swagger-ui [type=search]::-webkit-search-decoration{-webkit-appearance:none}.swagger-ui ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.swagger-ui details,.swagger-ui menu{display:block}.swagger-ui summary{display:list-item}.swagger-ui canvas{display:inline-block}.swagger-ui [hidden],.swagger-ui template{display:none}.swagger-ui .debug *{outline:1px solid gold}.swagger-ui .debug-white *{outline:1px solid #fff}.swagger-ui .debug-black *{outline:1px solid #000}.swagger-ui .debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) repeat 0 0}.swagger-ui .debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+ILine truncated .swagger-ui{color:#3b4151;container-name:swagger-ui;container-type:inline-size;/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */font-family:sans-serif}.swagger-ui html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.swagger-ui body{margin:0}.swagger-ui article,.swagger-ui aside,.swagger-ui footer,.swagger-ui header,.swagger-ui nav,.swagger-ui section{display:block}.swagger-ui h1{font-size:2em;margin:.67em 0}.swagger-ui figcaption,.swagger-ui figure,.swagger-ui main{display:block}.swagger-ui figure{margin:1em 40px}.swagger-ui hr{box-sizing:content-box;height:0;overflow:visible}.swagger-ui pre{font-family:monospace,monospace;font-size:1em}.swagger-ui a{background-color:transparent;-webkit-text-decoration-skip:objects}.swagger-ui abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.swagger-ui b,.swagger-ui strong{font-weight:inherit;font-weight:bolder}.swagger-ui code,.swagger-ui kbd,.swagger-ui samp{font-family:monospace,monospace;font-size:1em}.swagger-ui dfn{font-style:italic}.swagger-ui mark{background-color:#ff0;color:#000}.swagger-ui small{font-size:80%}.swagger-ui sub,.swagger-ui sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.swagger-ui sub{bottom:-.25em}.swagger-ui sup{top:-.5em}.swagger-ui audio,.swagger-ui video{display:inline-block}.swagger-ui audio:not([controls]){display:none;height:0}.swagger-ui img{border-style:none}.swagger-ui svg:not(:root){overflow:hidden}.swagger-ui button,.swagger-ui input,.swagger-ui optgroup,.swagger-ui select,.swagger-ui textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}.swagger-ui button,.swagger-ui input{overflow:visible}.swagger-ui button,.swagger-ui select{text-transform:none}.swagger-ui [type=reset],.swagger-ui [type=submit],.swagger-ui button,.swagger-ui html [type=button]{-webkit-appearance:button}.swagger-ui [type=button]::-moz-focus-inner,.swagger-ui [type=reset]::-moz-focus-inner,.swagger-ui [type=submit]::-moz-focus-inner,.swagger-ui button::-moz-focus-inner{border-style:none;padding:0}.swagger-ui [type=button]:-moz-focusring,.swagger-ui [type=reset]:-moz-focusring,.swagger-ui [type=submit]:-moz-focusring,.swagger-ui button:-moz-focusring{outline:1px dotted ButtonText}.swagger-ui fieldset{padding:.35em .75em .625em}.swagger-ui legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}.swagger-ui progress{display:inline-block;vertical-align:baseline}.swagger-ui textarea{overflow:auto}.swagger-ui [type=checkbox],.swagger-ui [type=radio]{box-sizing:border-box;padding:0}.swagger-ui [type=number]::-webkit-inner-spin-button,.swagger-ui [type=number]::-webkit-outer-spin-button{height:auto}.swagger-ui [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.swagger-ui [type=search]::-webkit-search-cancel-button,.swagger-ui [type=search]::-webkit-search-decoration{-webkit-appearance:none}.swagger-ui ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.swagger-ui details,.swagger-ui menu{display:block}.swagger-ui summary{display:list-item}.swagger-ui canvas{display:inline-block}.swagger-ui [hidden],.swagger-ui template{display:none}.swagger-ui .debug *{outline:1px solid gold}.swagger-ui .debug-white *{outline:1px solid #fff}.swagger-ui .debug-black *{outline:1px solid #000}.swagger-ui .debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) repeat 0 0}.swagger-ui .debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpLine truncated
/*# sourceMappingURL=swagger-ui.css.map*/ /*# sourceMappingURL=swagger-ui.css.map*/
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui.css","mappings":"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,CDrDb,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,oDC8BW,CD7BX,qDC8BW,CD7BX,uDC8BY,CD7BZ,wDC8Ba,CD5Bb,6CCyCW,CDxCX,wCCyCM,CDxCN,8CCyCY,CDxCZ,2CCyCS,CDxCT,yCCyCO,CDxCP,wCCyCS,CDxCT,iDCyCe,CDxCf,2CCyCS,CDxCT,iDCyCe,CDxCf,8CCyCY,CDxCZ,6CCyCW,CDxCX,yCCyCO,CDxCP,+CCyCa,CDxCb,+CCyCa,CDxCb,0CCyCQ,CDxCR,gDCyCc,CDxCd,yCCyCO,CDxCP,8CCyCY,CDxCZ,yCCyCO,CDxCP,+CCyCa,CDxCb,kDCyCgB,CDxChB,gDCyCc,CDxCd,iDCyCe,CDxCf,kDCyCgB,CDxChB,+CCyCa,CDvCb,oDCVc,CDWd,6CE3DE,gCDmBmB,CClBnB,sCDmBgB,CClBhB,qCDmBgB,CClBhB,oCDmBgB,CClBhB,mCDmBgB,CClBhB,sCDmBqB,CClBrB,yCDmBmB,CClBnB,wBACI,yBACA,0BAEJ,qBACI,4BACA,6BAEJ,uBAEI,4BADA,wBACA,CAEJ,sBAEI,6BADA,yBACA,CAGN,mCACE,mCDNmB,CCOnB,yCDNgB,CCOhB,wCDNgB,CCOhB,uCDNgB,CCOhB,sCDNgB,CCOhB,yCDNqB,CCOrB,4CDNmB,CCOnB,2BACI,yBACA,0BAEJ,wBACI,4BACA,6BAEJ,0BAEI,4BADA,wBACA,CAEJ,yBAEI,6BADA,yBACA,EAIN,uDACE,kCDhCmB,CCiCnB,wCDhCgB,CCiChB,uCDhCgB,CCiChB,sCDhCgB,CCiChB,qCDhCgB,CCiChB,wCDhCqB,CCiCrB,2CDhCmB,CCiCnB,0BACI,yBACA,0BAEJ,uBACI,4BACA,6BAEJ,yBAEI,4BADA,wBACA,CAEJ,wBAEI,6BADA,yBACA,EAIN,mCACE,kCD1DmB,CC2DnB,wCD1DgB,CC2DhB,uCD1DgB,CC2DhB,sCD1DgB,CC2DhB,qCD1DgB,CC2DhB,wCD1DqB,CC2DrB,2CD1DmB,CC2DnB,0BACI,yBACA,0BAEJ,uBACI,4BACA,6BAEJ,yBAEI,4BADA,wBACA,CAEJ,wBAEI,6BADA,yBACA,ECrGN,2CACA,2CACA,yCACA,uCAEA,mCACE,8CACA,8CACA,4CACA,2CAGF,uDACE,6CACA,6CACA,2CACA,0CAGF,mCACE,6CACA,6CACA,2CACA,0CCvBF,+BH6BoB,CG5BpB,qCH6BiB,CG5BjB,oCH6BiB,CG5BjB,mCH6BiB,CG5BjB,kCH6BiB,CG5BjB,kCH6BiB,CG1BjB,oCHqBoB,CGpBpB,sCHoBoB,CGnBpB,uCHmBoB,CGlBpB,qCHkBoB,CGhBpB,mCACE,kCHekB,CGdlB,wCHee,CGdf,uCHee,CGdf,sCHee,CGdf,qCHee,CGdf,qCHee,CGdf,uCHSkB,CGRlB,yCHQkB,CGPlB,0CHOkB,CGNlB,wCHMkB,EGHpB,uDACE,iCHEkB,CGDlB,uCHEe,CGDf,sCHEe,CGDf,qCHEe,CGDf,oCHEe,CGDf,oCHEe,CGDf,sCHJkB,CGKlB,wCHLkB,CGMlB,yCHNkB,CGOlB,uCHPkB,EGUpB,mCACE,iCHXkB,CGYlB,uCHXe,CGYf,sCHXe,CGYf,qCHXe,CGYf,oCHXe,CGYf,oCHXe,CGYf,sCHjBkB,CGkBlB,wCHlBkB,CGmBlB,yCHnBkB,CGoBlB,uCHpBkB,EIxCpB,2DJ8Ce,CI7Cf,2DJ8Ce,CI7Cf,+DJ8Ce,CI7Cf,6DJ8Ce,CI7Cf,6DJ8Ce,CI5Cf,mCACE,8DJuCa,CItCb,8DJuCa,CItCb,kEJuCa,CItCb,gEJuCa,CItCb,gEJuCa,EIpCf,uDACE,6DJ+Ba,CI9Bb,6DJ+Ba,CI9Bb,iEJ+Ba,CI9Bb,+DJ+Ba,CI9Bb,+DJ+Ba,EI5Bf,mCACE,6DJuBa,CItBb,6DJuBa,CItBb,iEJuBa,CItBb,+DJuBa,CItBb,+DJuBa,EKxDf,iBACE,gBACA,kBACA,gBCkBF,yBACA,6BACA,+BACA,2BAEA,4BACA,gCACA,kCACA,8BAEA,4BACA,gCACA,kCACA,8BAEA,8BACA,kCACA,oCACA,gCAEA,8BACA,kCACA,oCACA,gCAGA,4BAGE,SACA,OAFA,QADA,KAGA,CAGF,mCACE,4BACA,8BACA,gCACA,kCACA,+BACA,iCACA,mCACA,qCACA,+BACA,iCACA,mCACA,qCACA,iCACA,qCACA,uCACA,mCACA,iCACA,qCACA,uCACA,mCACA,+BAGE,SACA,OAFA,QADA,KAGA,EAIJ,uDACE,2BACA,6BACA,+BACA,iLine truncated {"version":3,"file":"swagger-ui.css","mappings":"AAAA,YCII,aCcU,CFfd,yBACE,2BACA,4EGEF,CFJE,sBEIF,CDWc,iCCCZ,0BACA,8BACA,kBAUF,QACE,iHAOF,aAME,gBAQF,aACE,eACA,4DAWF,aAGE,oBAOF,eACE,gBAQF,sBACE,SACA,iBACA,iBAQF,+BACE,cACA,eAWF,4BACE,qCACA,yBAQF,kBACE,CACA,yBACA,CADA,wCACA,CADA,gCACA,kCAOF,mBAEE,CAOF,kBAPE,CASA,kDAQF,+BAGE,cACA,iBAOF,iBACE,kBAOF,qBACE,WACA,mBAOF,aACE,iCAQF,aAEE,cACA,kBACA,wBACA,iBAGF,aACE,iBAGF,SACE,qCAUF,oBAEE,mCAOF,YACE,SACA,iBAOF,iBACE,4BAOF,eACE,mGAWF,sBAKE,eACA,iBACA,SACA,sCAQF,gBAEE,uCAQF,mBAEE,sGASF,yBAIE,yKAOF,iBAIE,UACA,6JAOF,6BAIE,sBAOF,0BACE,oBAUF,qBACE,cACA,cACA,eACA,UACA,mBACA,sBAQF,oBACE,wBACA,sBAOF,aACE,sDAQF,qBAEE,UACA,2GAOF,WAEE,2BAQF,4BACE,oBACA,8GAOF,uBAEE,0CAQF,yBACE,aACA,sCAWF,aAEE,qBAOF,iBACE,oBAUF,oBACE,CAQA,0CAUF,YACE,sBCnbF,kDACA,kDACA,+CCDA,yEACE,4BAGF,yEACE,iCAGF,kEACE,kCAGF,kEACE,2tBClBF,qBAkCE,2BCrBF,QACE,kBACA,iCAGF,sDACA,sDAEA,kDACA,sDAEA,oDACA,mDAEA,oDACA,mDAEA,qDACA,mDAEA,sDAKI,QACA,CAEA,WACA,CAHA,MACA,CALJ,iBACI,CACA,OACA,CAFA,KACA,CAGA,UACA,CACA,WACA,oCAGJ,6BACI,QACE,kBACA,oCAEF,yDACA,yDACA,qDACA,yDACA,uDACA,sDACA,uDACA,sDACA,wDACA,sDACA,yDAII,QACA,CAEA,WACA,CAHA,MACA,CALJ,iBACI,CACA,OACA,CAFA,KACA,CAGA,UACA,CACA,WACA,yDAIR,4BACI,QACE,kBACA,mCAEF,wDACA,wDACA,oDACA,wDACA,sDACA,qDACA,sDACA,qDACA,uDACA,qDACA,wDAII,QACA,CAEA,WACA,CAHA,MACA,CALJ,iBACI,CACA,OACA,CAFA,KACA,CAGA,UACA,CACA,WACA,qCAIR,4BACI,QACE,kBACA,mCAEF,wDACA,wDACA,oDACA,wDACA,sDACA,qDACA,sDACA,qDACA,uDACA,qDACA,wDAII,QACA,CAEA,WACA,CAHA,MACA,CALJ,iBACI,CACA,OACA,CAFA,KACA,CAGA,UACA,CACA,WACA,kBC3HR,kCCQE,qDACA,qEAEF,sBACE,wDACA,0FAGF,qBACE,uDACA,sEAGF,qBACE,uDACA,0DCVA,uBACA,CAFF,2BAEE,qBAIA,uBACA,CAFF,2BAEE,uBAIA,wBACA,CAFF,2BAEE,wBAIA,0BACA,CAFF,2BAEE,sBAIA,qBACA,CAFF,2BAEE,oCAGF,0BAEI,uBACA,CAFF,2BAEE,wBAIA,uBACA,CAFF,2BAEE,0BAIA,wBACA,CAFF,2BAEE,2BAIA,0BACA,CAFF,2BAEE,yBAIA,qBACA,CAFF,2BAEE,yDAIJ,yBAEI,uBACA,CAFF,2BAEE,uBAIA,uBACA,CAFF,2BAEE,yBAIA,wBACA,CAFF,2BAEE,0BAIA,0BACA,CAFF,2BAEE,wBAIA,qBACA,CAFF,2BAEE,qCAIJ,yBAEI,uBACA,CAFF,2BAEE,uBAIA,uBACA,CAFF,2BAEE,yBAIA,wBACA,CAFF,2BAEE,0BAIA,0BACA,CAFF,2BAEE,wBAIA,qBACA,CAFF,2BAEE,uBChHJ,mDACA,qDACA,6CAEA,wBACE,sDACA,wDACA,kEAGF,uBACE,qDACA,uDACA,8CAGF,uBACE,qDACA,uDACA,2BCPA,oDACA,4DACA,gEACA,kEACA,8DACA,oEAGF,mBACE,uDACA,+DACA,mEACA,qEACA,iEACA,yFAGF,kBACE,sDACA,8DACA,kEACA,oEACA,gEACA,qEAGF,kBACE,sDACA,8DACA,kEACA,oEACA,gEACA,wDCnCF,iBC2CQ,4BD1CR,iBC2Ca,2BD1Cb,iBC2CY,0BD1CZ,iBC2CW,sBD1CX,iBC2CO,wBD1CP,iBC2CS,8BD1CT,iBC2Ce,2BD1Cf,iBC2CY,4BD1CZ,iBC2Ca,4BD1Cb,oBC2Ca,uBD1Cb,iBC2CQ,0BDzCR,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,+BCuDW,0BDtDX,gCCuDW,2BDtDX,iCCuDY,4BDtDZ,iCCuDa,0BDrDb,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,2BC8BW,0BD7BX,4BC8BW,2BD7BX,6BC8BY,4BD7BZ,6BC8Ba,0BD5Bb,oBCyCW,qBDxCX,oBCyCM,2BDxCN,oBCyCY,wBDxCZ,oBCyCS,sBDxCT,oBCyCO,wBDxCP,iBCyCS,8BDxCT,oBCyCe,wBDxCf,oBCyCS,8BDxCT,oBCyCe,2BDxCf,oBCyCY,0BDxCZ,oBCyCW,sBDxCX,oBCyCO,4BDxCP,oBCyCa,4BDxCb,oBCyCa,uBDxCb,oBCyCQ,6BDxCR,oBCyCc,sBDxCd,oBCyCO,2BDxCP,oBCyCY,sBDxCZ,oBCyCO,4BDxCP,oBCyCa,+BDxCb,oBCyCgB,6BDxChB,oBCyCc,8BDxCd,oBCyCe,+BDxCf,oBCyCgB,4BDxChB,oBCyCa,6BDvCb,wBCVc,yBDWd,sCE3DE,eDmBmB,kBClBnB,qBDmBgB,kBClBhB,oBDmBgB,kBClBhB,mBDmBgB,kBClBhB,kBDmBgB,qBClBhB,kBDmBqB,sBClBrB,oBDmBmB,yBClBnB,wBACI,0BACA,sBAEJ,2BACI,6BACA,wBAGA,2BACA,CAFJ,wBAEI,uBAGA,4BACA,CAFJ,yBAEI,oCAGN,oBACE,eDNmB,qBCOnB,qBDNgB,qBCOhB,oBDNgB,qBCOhB,mBDNgB,qBCOhB,kBDNgB,wBCOhB,kBDNqB,yBCOrB,oBDNmB,4BCOnB,wBACI,0BACA,yBAEJ,2BACI,6BACA,2BAGA,2BACA,CAFJ,wBAEI,0BAGA,4BACA,CAFJ,yBAEI,yDAIN,mBACE,eDhCmB,oBCiCnB,qBDhCgB,oBCiChB,oBDhCgB,oBCiChB,mBDhCgB,oBCiChB,kBDhCgB,uBCiChB,kBDhCqB,wBCiCrB,oBDhCmB,2BCiCnB,wBACI,0BACA,wBAEJ,2BACI,6BACA,0BAGA,2BACA,CAFJ,wBAEI,yBAGA,4BACA,CAFJ,yBAEI,qCAIN,mBACE,eD1DmB,oBC2DnB,qBD1DgB,oBC2DhB,oBD1DgB,oBC2DhB,mBD1DgB,oBC2DhB,kBD1DgB,uBC2DhB,kBD1DqB,wBC2DrB,oBD1DmB,2BC2DnB,wBACI,0BACA,wBAEJ,2BACI,6BACA,0BAGA,2BACA,CAFJ,wBAEI,yBAGA,4BACA,CAFJ,yBAEI,yBCrGN,2CACA,0CACA,wCACA,qDAEA,0BACE,8CACA,6CACA,2CACA,0EAGF,yBACE,6CACA,4CACA,0CACA,sDAGF,yBACE,6CACA,4CACA,0CACA,oCCvBF,cH6BoB,kBG5BpB,oBH6BiB,kBG5BjB,mBH6BiB,kBG5BjB,kBH6BiB,kBG5BjB,iBH6BiB,kBG5BjB,iBH6BiB,mBG1BjB,kBHqBoB,mBGpBpB,oBHoBoB,mBGnBpB,qBHmBoB,mBGlBpB,mBHkBoB,oCGhBpB,oBACE,cHekB,qBGdlB,oBHee,qBGdf,mBHee,qBGdf,kBHee,qBGdf,iBHee,qBGdf,iBHee,sBGdf,kBHSkB,sBGRlB,oBHQkB,sBGPlB,qBHOkB,sBGNlB,mBHMkB,yDGHpB,mBACE,cHEkB,oBGDlB,oBHEe,oBGDf,mBHEe,oBGDf,kBHEe,oBGDf,iBHEe,oBGDf,iBHEe,qBGDf,kBHJkB,qBGKlB,oBHLkB,qBGMlB,qBHNkB,qBGOlB,mBHPkB,qCGUpB,mBACE,cHXkB,oBGYlB,oBHXe,oBGYf,mBHXe,oBGYf,kBHXe,oBGYf,iBHXe,oBGYf,iBHXe,qBGYf,kBHjBkB,qBGkBlB,oBHlBkB,qBGmBlB,qBHnBkB,qBGoBlB,mBHpBkB,wBIxCpB,qCJ8Ce,uBI7Cf,qCJ8Ce,uBI7Cf,yCJ8Ce,uBI7Cf,uCJ8Ce,uBI7Cf,uCJ8Ce,oCI5Cf,yBACE,qCJuCa,0BItCb,qCJuCa,0BItCb,yCJuCa,0BItCb,uCJuCa,0BItCb,uCJuCa,yDIpCf,wBACE,qCJ+Ba,yBI9Bb,qCJ+Ba,yBI9Bb,yCJ+Ba,yBI9Bb,uCJ+Ba,yBI9Bb,uCJ+Ba,qCI5Bf,wBACE,qCJuBa,yBItBb,qCJuBa,yBItBb,yCJuBa,yBItBb,uCJuBa,yBItBb,uCJuBa,mBKxDf,eACE,kBACA,gBACA,oBCkBF,2BACA,8BACALine truncated
+1 -1
View File
@@ -1,2 +1,2 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUICore=t():e.SwaggerUICore=t()}(this,(()=>(()=>{var e={6024:(e,t,n)=>{"use strict";n.d(t,{Z:()=>E});var s=n(4250),r=n.n(s),a=n(1093),o=n.n(a),l=n(8493),c=n.n(l),i=n(3942),u=n.n(i),p=n(6689),m=n.n(p);const d=require("react-immutable-pure-component");var h=n.n(d),g=n(8082),f=n.n(g),y=n(580),S=n.n(y);const v=e=>{const t=e.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(t)}catch{return t}};class E extends(h()){constructor(){super(...arguments),o()(this,"getModelName",(e=>-1!==c()(e).call(e,"#/definitions/")?v(e.replace(/^.*#\/definitions\//,"")):-1!==c()(e).call(e,"#/components/schemas/")?v(e.replace(/^.*#\/components\/schemas\//,"")):void 0)),o()(this,"getRefSchema",(e=>{let{specSelectors:t}=this.props;return t.findDefinition(e)}))}render(){let{getComponent:e,getConfigs:t,specSelectors:s,schema:a,required:o,name:l,isRef:c,specPath:i,displayName:u,includeReadOnly:p,includeWriteOnly:d}=this.props;const h=e("ObjectModel"),g=e("ArrayModel"),f=e("PrimitiveModel");let y="object",S=a&&a.get("$$ref");if(!l&&S&&(l=this.getModelName(S)),!a&&S&&(a=this.getRefSchema(l)),!a)return m().createElement("span",{className:"model model-title"},m().createElement("span",{className:"model-title__text"},u||l),m().createElement("img",{src:n(2517),height:"20px",width:"20px"}));const v=s.isOAS3()&&a.get("deprecated");switch(c=void 0!==c?c:!!S,y=a&&a.get("type")||y,y){case"object":return m().createElement(h,r()({className:"object"},this.props,{specPath:i,getConfigs:t,schema:a,name:l,deprecated:v,isRef:c,includeReadOnly:p,includeWriteOnly:d}));case"array":return m().createElement(g,r()({className:"array"},this.props,{getConfigs:t,schema:a,name:l,deprecated:v,required:o,includeReadOnly:p,includeWriteOnly:d}));default:return m().createElement(f,r()({},this.props,{getComponent:e,getConfigs:t,schema:a,name:l,deprecated:v,required:o}))}}}o()(E,"propTypes",{schema:u()(f()).isRequired,getComponent:S().func.isRequired,getConfigs:S().func.isRequired,specSelectors:S().object.isRequired,name:S().string,displayName:S().string,isRef:S().bool,required:S().bool,expandDepth:S().number,depth:S().number,specPath:f().list.isRequired,includeReadOnly:S().bool,includeWriteOnly:S().bool})},5623:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var s=n(1093),r=n.n(s),a=n(7252),o=n.n(a),l=n(6689),c=n.n(l),i=n(3883),u=n.n(i),p=(n(580),n(1669)),m=n(7504);class d extends c().Component{constructor(e,t){super(e,t),r()(this,"getDefinitionUrl",(()=>{let{specSelectors:e}=this.props;return new(u())(e.url(),m.Z.location).toString()}));let{getConfigs:n}=e,{validatorUrl:s}=n();this.state={url:this.getDefinitionUrl(),validatorUrl:void 0===s?"https://validator.swagger.io/validator":s}}UNSAFE_componentWillReceiveProps(e){let{getConfigs:t}=e,{validatorUrl:n}=t();this.setState({url:this.getDefinitionUrl(),validatorUrl:void 0===n?"https://validator.swagger.io/validator":n})}render(){let{getConfigs:e}=this.props,{spec:t}=e(),n=(0,p.Nm)(this.state.validatorUrl);return"object"==typeof t&&o()(t).length?null:this.state.url&&(0,p.hW)(this.state.validatorUrl)&&(0,p.hW)(this.state.url)?c().createElement("span",{className:"float-right"},c().createElement("a",{target:"_blank",rel:"noopener noreferrer",href:`${n}/debug?url=${encodeURIComponent(this.state.url)}`},c().createElement(h,{src:`${n}?url=${encodeURIComponent(this.state.url)}`,alt:"Online validator badge"}))):null}}class h extends c().Component{constructor(e){super(e),this.state={loaded:!1,error:!1}}componentDidMount(){const e=new Image;e.onload=()=>{this.setState({loaded:!0})},e.onerror=()=>{this.setState({error:!0})},e.src=this.props.src}UNSAFE_componentWillReceiveProps(e){if(e.src!==this.props.src){const t=new Image;t.onload=()=>{this.setState({loaded:!0})},t.onerror=()=>{this.setState({error:!0})},t.src=e.src}}render(){return this.state.error?c().createElement("img",{alt:"Error"}):this.state.loaded?c().createElement("img",{src:this.props.src,alt:this.props.alt}):null}}},2552:(e,t,n)=>{"use strict";n.d(t,{Z:()=>m,s:()=>d});var s=n(6689),r=n.n(s),a=(n(580),n(963));const o=require("remarkable/linkify"),l=require("dompurify");var c=n.n(l),i=n(9003),u=n.n(i);function p(e){let{source:t,className:n="",getConfigs:s}=e;if("string"!=typeof t)return null;const l=new a.Remarkable({html:!0,typographer:!0,breaks:!0,linkTarget:"_blank"}).use(o.linkify);l.core.ruler.disable(["replacements","smartquotes"]);const{useUnsafeMarkdown:c}=s(),i=l.render(t),p=d(i,{useUnsafeMarkdown:c});return t&&i&&p?r().createElement("div",{className:u()(n,"markdown"),dangerouslySetInnerHTML:{__html:p}}):null}c().addHook&&c().addHook("beforeSanitizeElements",(function(e){return e.href&&e.setAttribute("rel","noopener noreferrer"),e})),p.defaultProps={getConfigs:()=>({useUnsafeMarkdown:!1})};const m=p;function d(e){let{useUnsafeMarkdown:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguLine truncated !function webpackUniversalModuleDefinition(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUICore=t():e.SwaggerUICore=t()}(this,(()=>(()=>{"use strict";var e={158(e){e.exports=require("buffer")}},t={};function __webpack_require__(a){var r=t[a];if(void 0!==r)return r.exports;var n=t[a]={exports:{}};return e[a](n,n.exports,__webpack_require__),n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},__webpack_require__.d=(e,t)=>{for(var a in t)__webpack_require__.o(t,a)&&!__webpack_require__.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};__webpack_require__.d(a,{default:()=>Cl});var r={};__webpack_require__.r(r),__webpack_require__.d(r,{CLEAR:()=>$,CLEAR_BY:()=>L,NEW_AUTH_ERR:()=>V,NEW_SPEC_ERR:()=>R,NEW_SPEC_ERR_BATCH:()=>J,NEW_THROWN_ERR:()=>P,NEW_THROWN_ERR_BATCH:()=>T,clear:()=>clear,clearBy:()=>clearBy,newAuthErr:()=>newAuthErr,newSpecErr:()=>newSpecErr,newSpecErrBatch:()=>newSpecErrBatch,newThrownErr:()=>newThrownErr,newThrownErrBatch:()=>newThrownErrBatch});var n={};__webpack_require__.r(n),__webpack_require__.d(n,{AUTHORIZE:()=>ue,AUTHORIZE_OAUTH2:()=>he,CONFIGURE_AUTH:()=>ge,LOGOUT:()=>de,RESTORE_AUTHORIZATION:()=>ye,SHOW_AUTH_POPUP:()=>me,authPopup:()=>authPopup,authorize:()=>authorize,authorizeAccessCodeWithBasicAuthentication:()=>authorizeAccessCodeWithBasicAuthentication,authorizeAccessCodeWithFormParams:()=>authorizeAccessCodeWithFormParams,authorizeApplication:()=>authorizeApplication,authorizeOauth2:()=>authorizeOauth2,authorizeOauth2WithPersistOption:()=>authorizeOauth2WithPersistOption,authorizePassword:()=>authorizePassword,authorizeRequest:()=>authorizeRequest,authorizeWithPersistOption:()=>authorizeWithPersistOption,configureAuth:()=>configureAuth,logout:()=>logout,logoutWithPersistOption:()=>logoutWithPersistOption,persistAuthorizationIfNeeded:()=>persistAuthorizationIfNeeded,preAuthorizeImplicit:()=>preAuthorizeImplicit,restoreAuthorization:()=>restoreAuthorization,showDefinitions:()=>showDefinitions});var s={};__webpack_require__.r(s),__webpack_require__.d(s,{authorized:()=>ve,definitionsForRequirements:()=>definitionsForRequirements,definitionsToAuthorize:()=>_e,getConfigs:()=>we,getDefinitionsByNames:()=>getDefinitionsByNames,isAuthorized:()=>isAuthorized,selectAuthPath:()=>selectAuthPath,shownDefinitions:()=>Ee});var o={};__webpack_require__.r(o),__webpack_require__.d(o,{TOGGLE_CONFIGS:()=>qe,UPDATE_CONFIGS:()=>Ie,downloadConfig:()=>downloadConfig,getConfigByUrl:()=>getConfigByUrl,loaded:()=>actions_loaded,toggle:()=>toggle,update:()=>update});var l={};__webpack_require__.r(l),__webpack_require__.d(l,{get:()=>get});var c={};__webpack_require__.r(c),__webpack_require__.d(c,{transform:()=>transform});var i={};__webpack_require__.r(i),__webpack_require__.d(i,{transform:()=>parameter_oneof_transform});var p={};__webpack_require__.r(p),__webpack_require__.d(p,{allErrors:()=>Ye,lastError:()=>Qe});var m={};__webpack_require__.r(m),__webpack_require__.d(m,{SHOW:()=>nt,UPDATE_FILTER:()=>at,UPDATE_LAYOUT:()=>tt,UPDATE_MODE:()=>rt,changeMode:()=>changeMode,show:()=>actions_show,updateFilter:()=>updateFilter,updateLayout:()=>updateLayout});var u={};__webpack_require__.r(u),__webpack_require__.d(u,{current:()=>current,currentFilter:()=>currentFilter,isShown:()=>isShown,showSummary:()=>ot,whatMode:()=>whatMode});var d={};__webpack_require__.r(d),__webpack_require__.d(d,{taggedOperations:()=>taggedOperations});var h={};__webpack_require__.r(h),__webpack_require__.d(h,{getActiveLanguage:()=>it,getDefaultExpanded:()=>pt,getGenerators:()=>ct,getSnippetGenerators:()=>getSnippetGenerators});var g={};__webpack_require__.r(g),__webpack_require__.d(g,{JsonSchemaArrayItemFile:()=>JsonSchemaArrayItemFile,JsonSchemaArrayItemText:()=>JsonSchemaArrayItemText,JsonSchemaForm:()=>JsonSchemaForm,JsonSchema_array:()=>JsonSchema_array,JsonSchema_boolean:()=>JsonSchema_boolean,JsonSchema_object:()=>JsonSchema_object,JsonSchema_string:()=>JsonSchema_string});var y={};__webpack_require__.r(y),__webpack_require__.d(y,{allowTryItOutFor:()=>allowTryItOutFor,basePath:()=>ga,canExecuteScheme:()=>canExecuteScheme,consumes:()=>pa,consumesOptionsFor:()=>consumesOptionsFor,contentTypeValues:()=>contentTypeValues,currentProducesFor:()=>currentProducesFor,definitions:()=>ha,externalDocs:()=>na,findDefinition:()=>findDefinition,getOAS3RequiredRequestBodyContentType:()=>getOAS3RequiredRequestBodyContentType,getParameter:()=>getParameter,hasHost:()=>Ca,host:()=>ya,info:()=>ra,isMediaTypeSchemaPropertiesEqual:()=>isMediaTypeSchemaPropertiesEqual,isOAS3:()=>aa,lastError:()=>Ht,mutatedLine truncated
//# sourceMappingURL=swagger-ui.js.map //# sourceMappingURL=swagger-ui.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui.js","mappings":"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 truncated {"version":3,"file":"swagger-ui.js","mappings":"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 truncated
+25 -2
View File
@@ -15,6 +15,10 @@ table.dataTable>tbody>tr.child ul.dtr-details>li {
border-bottom: 1px solid rgba(0, 0, 0, 0.129); border-bottom: 1px solid rgba(0, 0, 0, 0.129);
padding: 0.5em 0; padding: 0.5em 0;
} }
/* like the first entry has no top padding, the last one needs no bottom padding */
table.dataTable>tbody>tr.child ul.dtr-details>li:last-child {
padding-bottom: 0;
}
table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover, table.dataTable.dtr-inline.collapsed>tbody>tr>td.dtr-control:before:hover,
table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover { table.dataTable.dtr-inline.collapsed>tbody>tr>th.dtr-control:before:hover {
background-color: var(--mc-dtr-hover-bg); background-color: var(--mc-dtr-hover-bg);
@@ -28,7 +32,8 @@ table.dataTable td.dt-control:before {
color: var(--mc-dtr-color); color: var(--mc-dtr-color);
height: 1em; height: 1em;
width: 1em; width: 1em;
line-height: 1.25em; line-height: 1em;
margin-top: calc(-0.5em - 1.5px);
border-radius: 0px; border-radius: 0px;
box-shadow: none; box-shadow: none;
font-size: 14px; font-size: 14px;
@@ -39,11 +44,29 @@ table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th.dtr-control:before,
table.dataTable td.dt-control:before { table.dataTable td.dt-control:before {
background-color: var(--mc-dtr-active-bg) !important; background-color: var(--mc-dtr-active-bg) !important;
} }
/* The default initial order targets column 0, which is usually the non-orderable expand column */
table.dataTable thead > tr > th.sorting_disabled.sorting_asc,
table.dataTable thead > tr > th.sorting_disabled.sorting_desc {
padding-right: 0.5rem;
cursor: auto;
}
table.dataTable thead > tr > th.sorting_disabled.sorting_asc:before,
table.dataTable thead > tr > th.sorting_disabled.sorting_asc:after,
table.dataTable thead > tr > th.sorting_disabled.sorting_desc:before,
table.dataTable thead > tr > th.sorting_disabled.sorting_desc:after {
display: none;
}
table.dataTable.table-striped>tbody>tr>td { table.dataTable.table-striped>tbody>tr>td {
vertical-align: middle; vertical-align: middle;
} }
table.dataTable.table-striped>tbody>tr>td>input[type="checkbox"] { table.dataTable.table-striped>tbody>tr>td>input[type="checkbox"] {
margin-top: 7px; /* centers the 1em checkbox in the 1.5em line, level with the expand control */
margin-top: 0.25em;
}
/* the vertical margin is meant for the tag editor in edit forms, in tables it only adds height */
table.dataTable .tag-badge {
margin-top: 0;
margin-bottom: 0;
} }
td.dtr-col-lg { td.dtr-col-lg {
min-width: 350px; min-width: 350px;
+8
View File
@@ -139,6 +139,13 @@ body {
display: inline-block; display: inline-block;
font-size: inherit; font-size: inherit;
} }
.badge .bi {
font-size: inherit;
}
/* keeps icons from making a badge taller than one with text only */
.badge .bi::before {
line-height: 0;
}
.btn-group-xs > .btn, .btn-xs { .btn-group-xs > .btn, .btn-xs {
padding: .25rem .4rem; padding: .25rem .4rem;
font-size: .875rem; font-size: .875rem;
@@ -343,6 +350,7 @@ code {
} }
.tag-badge .bi { .tag-badge .bi {
font-size: 12px; font-size: 12px;
line-height: 0;
} }
.tag-badge.btn-badge:hover { .tag-badge.btn-badge:hover {
filter: brightness(0.9); filter: brightness(0.9);
+1 -1
View File
@@ -191,7 +191,7 @@
height: 2rem; height: 2rem;
width: 2rem; width: 2rem;
line-height: 2rem; line-height: 2rem;
margin-top: -15px; margin-top: calc(-1rem - 1.5px);
} }
li .dtr-data { li .dtr-data {
+14 -2
View File
@@ -1394,6 +1394,18 @@ function set_tfa($_data) {
case "webauthn": case "webauthn":
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"]; $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
// Refuse incomplete WebAuthn registrations. Without this guard a request
// that does not contain the parsed credential data (e.g. a plain form
// POST) would silently insert an empty keyHandle while reporting success.
if (empty($_data['registration']) || empty($_data['registration']->credentialId)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_data_log),
'msg' => 'webauthn_registration_failed'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')"); VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
$stmt->execute(array( $stmt->execute(array(
@@ -2420,7 +2432,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
case "use_ssl": case "use_ssl":
case "use_tls": case "use_tls":
case "login_provisioning": case "login_provisioning":
case "ignore_ssl_errors": case "ignore_ssl_error":
$settings[$row["key"]] = boolval($row["value"]); $settings[$row["key"]] = boolval($row["value"]);
break; break;
default: default:
@@ -2444,7 +2456,7 @@ function identity_provider($_action = null, $_data = null, $_extra = null) {
if ($settings["authsource"] == "ldap"){ if ($settings["authsource"] == "ldap"){
$settings['use_ssl'] = !isset($settings['use_ssl']) ? false : $settings['use_ssl']; $settings['use_ssl'] = !isset($settings['use_ssl']) ? false : $settings['use_ssl'];
$settings['use_tls'] = !isset($settings['use_tls']) ? false : $settings['use_tls']; $settings['use_tls'] = !isset($settings['use_tls']) ? false : $settings['use_tls'];
$settings['ignore_ssl_errors'] = !isset($settings['ignore_ssl_errors']) ? false : $settings['ignore_ssl_errors']; $settings['ignore_ssl_error'] = !isset($settings['ignore_ssl_error']) ? false : $settings['ignore_ssl_error'];
} }
return $settings; return $settings;
break; break;
+1 -1
View File
@@ -614,7 +614,7 @@ jQuery(function($){
defaultContent: '' defaultContent: ''
}, },
{ {
title: 'RL', title: lang_edit.ratelimit,
data: 'rl', data: 'rl',
searchable: false, searchable: false,
orderable: false, orderable: false,
+2 -1
View File
@@ -1127,7 +1127,7 @@
"mailbox_defaults_info": "Define default settings for new mailboxes.", "mailbox_defaults_info": "Define default settings for new mailboxes.",
"mailbox_defquota": "Default mailbox size", "mailbox_defquota": "Default mailbox size",
"mailbox_templates": "Mailbox Templates", "mailbox_templates": "Mailbox Templates",
"mailbox_quota": "Max. size of a mailbox", "mailbox_quota": "Max. mailbox size",
"mailboxes": "Mailboxes", "mailboxes": "Mailboxes",
"max_aliases": "Max. aliases", "max_aliases": "Max. aliases",
"max_mailboxes": "Max. possible mailboxes", "max_mailboxes": "Max. possible mailboxes",
@@ -1429,6 +1429,7 @@
"u2f_deprecated": "It seems that your Key was registered using the deprecated U2F method. We will deactivate Two-Factor-Authenticaiton for you and delete your Key.", "u2f_deprecated": "It seems that your Key was registered using the deprecated U2F method. We will deactivate Two-Factor-Authenticaiton for you and delete your Key.",
"u2f_deprecated_important": "Please register your Key in the admin panel with the new WebAuthn method.", "u2f_deprecated_important": "Please register your Key in the admin panel with the new WebAuthn method.",
"webauthn": "WebAuthn authentication", "webauthn": "WebAuthn authentication",
"webauthn_registration_failed": "WebAuthn registration failed. The credential could not be verified, please try again.",
"waiting_usb_auth": "<i>Waiting for USB device...</i><br><br>Please tap the button on your USB device now.", "waiting_usb_auth": "<i>Waiting for USB device...</i><br><br>Please tap the button on your USB device now.",
"waiting_usb_register": "<i>Waiting for USB device...</i><br><br>Please enter your password above and confirm your registration by tapping the button on your USB device.", "waiting_usb_register": "<i>Waiting for USB device...</i><br><br>Please enter your password above and confirm your registration by tapping the button on your USB device.",
"yubi_otp": "Yubico OTP authentication" "yubi_otp": "Yubico OTP authentication"
+16 -10
View File
@@ -416,18 +416,24 @@ function recursiveBase64StrToArrayBuffer(obj) {
return navigator.credentials.create(json); return navigator.credentials.create(json);
}).then(function(cred) { }).then(function(cred) {
return { return {
id: cred.id, clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON),
rawId: arrayBufferToBase64(cred.rawId),
response: {
attestationObject: arrayBufferToBase64(cred.response.attestationObject), attestationObject: arrayBufferToBase64(cred.response.attestationObject),
clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON) key_id: keyId,
}, tfa_method: 'webauthn',
type: cred.type confirm_password: ''
}; };
}).then(function(credData) { }).then(JSON.stringify).then(function(credentialData) {
$('#setup_webauthn_register_data').val(JSON.stringify(credData)); // send the attestation to the JSON API, like the regular WebAuthn modal does,
$('#setup_webauthn_reg_form input[name=set_tfa]').val('1'); // so that the credential is parsed and stored correctly
$('#setup_webauthn_reg_form').submit(); return window.fetch('/api/v1/add/webauthn-tfa-registration', {method: 'POST', body: credentialData, cache: 'no-cache'});
}).then(function(response) {
return response.json();
}).then(function(json) {
if (json.success) {
window.location = window.location.href.split('#')[0];
} else {
throw new Error(json.msg);
}
}).catch(function(err) { }).catch(function(err) {
$('#setup_webauthn_return_code').show().text(err.message || 'Registration failed'); $('#setup_webauthn_return_code').show().text(err.message || 'Registration failed');
}); });
+82
View File
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes admin: administrator accounts */
class AdminRoutesTest extends ApiTestCase
{
private const DETAILS = ['username' => 'admin', 'tfa_active' => 0, 'tfa_active_int' => 0, 'active' => 1, 'active_int' => 1,
'created' => '2026-09-21 10:23:08', 'attributes' => ['force_tfa' => '0', 'force_pw_update' => '0']];
public function testGetAllCollectsTheDetailsOfEveryAdmin(): void
{
$result = $this->get('get/admin/all', ['script' => [
self::answer('admin', ['get'], ['admin', 'gone']),
self::answer('admin', ['details', 'admin'], self::DETAILS),
self::answer('admin', ['details', 'gone'], false),
]]);
$this->assertSame([self::DETAILS], $result->json());
$this->assertSame([['get'], ['details', 'admin'], ['details', 'gone']], $result->callsTo('admin'));
}
public function testGetAllWithoutAdmins(): void
{
$result = $this->get('get/admin/all', ['script' => [self::answer('admin', ['get'], [])]]);
$this->assertSame('{}', $result->body);
$this->assertSame([['get']], $result->callsTo('admin'));
}
public function testGetOne(): void
{
$found = $this->get('get/admin/admin', ['script' => [self::answer('admin', ['details', 'admin'], self::DETAILS)]]);
$this->assertSame(self::DETAILS, $found->json());
$missing = $this->get('get/admin/nobody', ['script' => [self::answer('admin', ['details'], false)]]);
$this->assertSame('{}', $missing->body);
$this->assertCalled($missing, 'admin', ['details', 'nobody']);
}
public function testAdd(): void
{
$attr = ['username' => 'admin2', 'password' => 'Rand0m-Passw0rd!', 'password2' => 'Rand0m-Passw0rd!', 'active' => 1];
$messages = [self::message('success', ['admin_added', 'admin2'], ['admin', 'add', ['username' => 'admin2', 'password' => '*']])];
$result = $this->post('add/admin', $attr, ['script' => [self::answer('admin', ['add'], null, $messages)]]);
$this->assertSame([['add', $attr]], $result->callsTo('admin'));
$this->assertSame($messages, $result->json());
}
public function testEdit(): void
{
$result = $this->post('edit/admin', ['items' => ['admin2', 'admin3'], 'attr' => ['active' => '0', 'force_tfa' => 1]]);
$this->assertSame([['edit', ['username' => ['admin2', 'admin3'], 'active' => '0', 'force_tfa' => 1]]], $result->callsTo('admin'));
}
public function testEditTakesASingleUsername(): void
{
$result = $this->post('edit/admin', ['items' => 'admin2', 'attr' => ['password' => 'x', 'password2' => 'x']]);
$this->assertCalled($result, 'admin', ['edit', ['username' => ['admin2'], 'password' => 'x', 'password2' => 'x']]);
}
public function testDelete(): void
{
$messages = [
self::message('success', ['admin_removed', 'admin2']),
self::message('danger', ['username_invalid', 'nobody']),
];
$result = $this->post('delete/admin', ['admin2', 'nobody'], ['script' => [self::answer('admin', ['delete'], null, $messages)]]);
$this->assertSame([['delete', ['username' => ['admin2', 'nobody']]]], $result->callsTo('admin'));
$this->assertSame($messages, $result->json());
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes api-key: the ApiKey module */
class ApiKeyRoutesTest extends ApiTestCase
{
private const API = 'Mailcow\ApiKey\ApiKeyModule->api->';
private const KEY = ['id' => 3, 'description' => 'provisioning', 'access' => 'rw', 'api_key' => '1B7E2C-94F0A3-6D5B18-C2E7F4-03A9D6',
'allow_from' => ['192.0.2.10'], 'skip_ip_check' => 0, 'service' => 0, 'active' => 1, 'created' => '2026-11-02 09:12:44', 'modified' => null];
public function testGetAll(): void
{
$result = $this->get('get/api-key/all', ['script' => [self::answer(self::API . 'getKeys', [], [self::KEY])]]);
$this->assertSame([self::KEY], $result->json());
$this->assertSame([[]], $result->callsTo(self::API . 'getKeys'));
}
public function testGetAllAnswersAnEmptyObjectWithoutKeys(): void
{
// also the answer for read-only API keys: the module returns no keys for them
$result = $this->get('get/api-key/all', ['session' => self::API_RO, 'script' => [self::answer(self::API . 'getKeys', [], [])]]);
$this->assertSame('{}', $result->body);
}
public function testGetOne(): void
{
$found = $this->get('get/api-key/3', ['script' => [self::answer(self::API . 'getKey', ['3'], self::KEY)]]);
$this->assertSame(self::KEY, $found->json());
$missing = $this->get('get/api-key/99', ['script' => [self::answer(self::API . 'getKey', [], [])]]);
$this->assertSame('{}', $missing->body);
$this->assertSame([['99']], $missing->callsTo(self::API . 'getKey'));
}
public function testAdd(): void
{
$attr = ['description' => 'provisioning', 'access' => 'rw', 'allow_from' => '192.0.2.10, 2001:db8::/64', 'skip_ip_check' => 0, 'active' => 1];
$messages = [self::message('success', ['api_key_added', 'provisioning'], ['api', 'add', 'key', $attr])];
$result = $this->post('add/api-key', $attr, ['script' => [self::answer(self::API . 'addKey', [], true, $messages)]]);
$this->assertSame([[$attr]], $result->callsTo(self::API . 'addKey'));
$this->assertSame($messages, $result->json());
}
public function testEdit(): void
{
$result = $this->post('edit/api-key', ['items' => [3, 4], 'attr' => ['regen_key' => 1]]);
$this->assertSame([[[3, 4], ['regen_key' => 1]]], $result->callsTo(self::API . 'editKeys'));
}
public function testEditTakesASingleId(): void
{
$result = $this->post('edit/api-key', ['items' => 3, 'attr' => ['active' => 0]]);
$this->assertSame([[[3], ['active' => 0]]], $result->callsTo(self::API . 'editKeys'));
}
public function testDelete(): void
{
$messages = [self::message('danger', ['api_key_service_locked', 'API_KEY'])];
$result = $this->post('delete/api-key', [1], ['script' => [self::answer(self::API . 'deleteKeys', [], true, $messages)]]);
$this->assertSame([[[1]]], $result->callsTo(self::API . 'deleteKeys'));
$this->assertSame($messages, $result->json());
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes domain-admin, da-acl and sso/domain-admin */
class DomainAdminRoutesTest extends ApiTestCase
{
private const DETAILS = ['username' => 'domainadmin1', 'tfa_active' => 0, 'tfa_active_int' => 0, 'active' => 1, 'active_int' => 1,
'created' => '2026-09-21 10:24:48', 'attributes' => ['force_tfa' => '0', 'force_pw_update' => '0'],
'selected_domains' => ['example.org'], 'unselected_domains' => ['example.com']];
public function testGetAllCollectsTheDetailsOfEveryDomainAdmin(): void
{
$result = $this->get('get/domain-admin/all', ['script' => [
self::answer('domain_admin', ['get'], ['domainadmin1', 'gone']),
self::answer('domain_admin', ['details', 'domainadmin1'], self::DETAILS),
self::answer('domain_admin', ['details', 'gone'], false),
]]);
$this->assertSame([self::DETAILS], $result->json());
$this->assertSame([['get'], ['details', 'domainadmin1'], ['details', 'gone']], $result->callsTo('domain_admin'));
}
public function testGetAllWithoutDomainAdmins(): void
{
$result = $this->get('get/domain-admin/all', ['script' => [self::answer('domain_admin', ['get'], [])]]);
$this->assertSame('{}', $result->body);
}
public function testGetOne(): void
{
$found = $this->get('get/domain-admin/domainadmin1', ['script' => [self::answer('domain_admin', ['details', 'domainadmin1'], self::DETAILS)]]);
$this->assertSame(self::DETAILS, $found->json());
$missing = $this->get('get/domain-admin/nobody', ['script' => [self::answer('domain_admin', ['details'], false)]]);
$this->assertSame('{}', $missing->body);
$this->assertSame([['details', 'nobody']], $missing->callsTo('domain_admin'));
}
public function testAdd(): void
{
$attr = ['username' => 'domainadmin1', 'password' => 'Rand0m-Passw0rd!', 'password2' => 'Rand0m-Passw0rd!',
'domains' => ['example.org', 'example.com'], 'active' => 1];
$result = $this->post('add/domain-admin', $attr);
$this->assertSame([['add', $attr]], $result->callsTo('domain_admin'));
}
public function testEdit(): void
{
$result = $this->post('edit/domain-admin', ['items' => ['domainadmin1'], 'attr' => ['domains' => ['example.org'], 'disable_tfa' => 1]]);
$this->assertSame([['edit', ['username' => ['domainadmin1'], 'domains' => ['example.org'], 'disable_tfa' => 1]]], $result->callsTo('domain_admin'));
}
public function testDelete(): void
{
$result = $this->post('delete/domain-admin', ['domainadmin1', 'domainadmin2']);
$this->assertSame([['delete', ['username' => ['domainadmin1', 'domainadmin2']]]], $result->callsTo('domain_admin'));
}
public function testEditAcl(): void
{
$messages = [self::message('success', ['acl_saved', 'domainadmin1'])];
$result = $this->post('edit/da-acl', ['items' => 'domainadmin1', 'attr' => ['da_acl' => ['syncjobs', 'quarantine']]],
['script' => [self::answer('acl', ['edit', 'domainadmin'], null, $messages)]]);
$this->assertSame([['edit', 'domainadmin', ['username' => ['domainadmin1'], 'da_acl' => ['syncjobs', 'quarantine']]]], $result->callsTo('acl'));
$this->assertSame($messages, $result->json());
}
public function testSsoTokenIsTheAnswer(): void
{
$token = ['token' => '591F6D-5C3DD2-7455CD-DAF1C1-AA4FCC'];
$result = $this->post('add/sso/domain-admin', ['username' => 'domainadmin1'],
['script' => [self::answer('domain_admin_sso', ['issue'], $token)]]);
$this->assertSame([['issue', ['username' => 'domainadmin1']]], $result->callsTo('domain_admin_sso'));
$this->assertSame($token, $result->json());
}
public function testSsoTokenFailureAnswersTheMessages(): void
{
$messages = [self::message('danger', ['object_doesnt_exist', 'nobody'])];
$failed = $this->post('add/sso/domain-admin', ['username' => 'nobody'],
['script' => [self::answer('domain_admin_sso', ['issue'], false, $messages)]]);
$this->assertSame($messages, $failed->json());
$silent = $this->post('add/sso/domain-admin', ['username' => 'nobody'],
['script' => [self::answer('domain_admin_sso', ['issue'], false)]]);
$this->assertSame(['type' => 'error', 'msg' => 'Cannot add item'], $silent->json());
}
public function testSsoForOtherObjectsAnswersNothing(): void
{
$result = $this->post('add/sso/mailbox', ['username' => 'user@example.org']);
$this->assertSame([200, ''], [$result->status, $result->body]);
$this->assertNotCalled($result, 'domain_admin_sso');
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
/** Routes logs/<type>[/<count>] */
class LogRoutesTest extends ApiTestCase
{
/** @return array<string, array{0: string, 1: string}> route type => log name of get_logs() */
public static function logs(): array
{
return [
'dovecot' => ['dovecot', 'dovecot-mailcow'],
'ratelimited' => ['ratelimited', 'ratelimited'],
'netfilter' => ['netfilter', 'netfilter-mailcow'],
'cron' => ['cron', 'cron-mailcow'],
'postfix' => ['postfix', 'postfix-mailcow'],
'autodiscover' => ['autodiscover', 'autodiscover-mailcow'],
'sogo' => ['sogo', 'sogo-mailcow'],
'ui' => ['ui', 'mailcow-ui'],
'sasl' => ['sasl', 'sasl'],
'watchdog' => ['watchdog', 'watchdog-mailcow'],
'acme' => ['acme', 'acme-mailcow'],
'api' => ['api', 'api-mailcow'],
'rspamd-history' => ['rspamd-history', 'rspamd-history'],
];
}
#[DataProvider('logs')]
public function testWithoutCount(string $type, string $log): void
{
$entries = [['time' => '1794308452', 'message' => 'first'], ['time' => '1794308451', 'message' => 'second']];
$result = $this->get("get/logs/$type", ['script' => [self::answer('get_logs', [$log], $entries)]]);
$this->assertSame([[$log]], $result->callsTo('get_logs'));
$this->assertSame($entries, $result->json());
}
#[DataProvider('logs')]
public function testCountOrRange(string $type, string $log): void
{
$this->assertSame([[$log, '50']], $this->get("get/logs/$type/50")->callsTo('get_logs'));
$this->assertSame([[$log, '2-3']], $this->get("get/logs/$type/2-3")->callsTo('get_logs'));
}
#[DataProvider('logs')]
public function testEmptyLogs(string $type, string $log): void
{
$this->assertSame('{}', $this->get("get/logs/$type/5", ['script' => [self::answer('get_logs', [$log], false)]])->body);
$this->assertSame('{}', $this->get("get/logs/$type", ['script' => [self::answer('get_logs', [$log], [])]])->body);
}
public function testCountKeepsOnlyDigitsAndDashes(): void
{
$this->assertSame([['postfix-mailcow', '12-30']], $this->get('get/logs/postfix/a12-3x0')->callsTo('get_logs'));
}
public function testTrailingSlashPassesAnEmptyCount(): void
{
$this->assertSame([['postfix-mailcow', '']], $this->get('get/logs/postfix/')->callsTo('get_logs'));
}
public function testRspamdStatsIgnoresTheCount(): void
{
$stats = ['version' => '3.12.1', 'scanned' => 300, 'actions' => ['reject' => 38]];
$result = $this->get('get/logs/rspamd-stats/10', ['script' => [self::answer('get_logs', ['rspamd-stats'], $stats)]]);
$this->assertSame([['rspamd-stats']], $result->callsTo('get_logs'));
$this->assertSame($stats, $result->json());
}
public function testUnknownLog(): void
{
$this->assertApiError($this->get('get/logs/clamd'), 404, 'route not found');
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes oauth2-client */
class OAuth2ClientRoutesTest extends ApiTestCase
{
private const CLIENT = ['id' => 1, 'client_id' => '17c76aaa88c0', 'client_secret' => '73fc668a88147e32a31ff80c',
'redirect_uri' => 'https://app.example.org/oauth/callback', 'grant_types' => null, 'scope' => 'profile', 'user_id' => null];
public function testGetAllCollectsTheDetailsOfEveryClient(): void
{
$result = $this->get('get/oauth2-client/all', ['script' => [
self::answer('oauth2', ['get', 'clients'], [1, 2]),
self::answer('oauth2', ['details', 'client', 1], self::CLIENT),
self::answer('oauth2', ['details', 'client', 2], false),
]]);
$this->assertSame([self::CLIENT], $result->json());
$this->assertSame([['get', 'clients'], ['details', 'client', 1], ['details', 'client', 2]], $result->callsTo('oauth2'));
}
public function testGetAllWithoutClients(): void
{
$this->assertSame('{}', $this->get('get/oauth2-client/all', ['script' => [self::answer('oauth2', ['get', 'clients'], null)]])->body);
}
public function testGetOne(): void
{
$found = $this->get('get/oauth2-client/1', ['script' => [self::answer('oauth2', ['details', 'client', '1'], self::CLIENT)]]);
$this->assertSame(self::CLIENT, $found->json());
$missing = $this->get('get/oauth2-client/9', ['script' => [self::answer('oauth2', ['details'], false)]]);
$this->assertSame('{}', $missing->body);
$this->assertSame([['details', 'client', '9']], $missing->callsTo('oauth2'));
}
public function testAdd(): void
{
$result = $this->post('add/oauth2-client', ['redirect_uri' => 'https://app.example.org/oauth/callback']);
$this->assertSame([['add', 'client', ['redirect_uri' => 'https://app.example.org/oauth/callback']]], $result->callsTo('oauth2'));
}
public function testEdit(): void
{
$result = $this->post('edit/oauth2-client', ['items' => [1, 2], 'attr' => ['renew_secret' => 1]]);
$this->assertSame([['edit', 'client', ['id' => [1, 2], 'renew_secret' => 1]]], $result->callsTo('oauth2'));
}
public function testDelete(): void
{
$result = $this->post('delete/oauth2-client', [1, 2]);
$this->assertSame([['delete', 'client', ['id' => [1, 2]]]], $result->callsTo('oauth2'));
}
}
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes self and fido2-fn act on the account of the session; the pending password update of a login */
class OwnAccountRoutesTest extends ApiTestCase
{
private const PASSWORDS = ['user_old_pass' => 'old', 'user_new_pass' => 'N3w-Passw0rd!', 'user_new_pass2' => 'N3w-Passw0rd!'];
public function testSelfDoesNothingForAnApiKey(): void
{
$result = $this->post('edit/self', ['attr' => self::PASSWORDS]);
$this->assertSame([200, ''], [$result->status, $result->body]);
$this->assertNotCalled($result, 'domain_admin');
$this->assertNotCalled($result, 'edit_user_account');
}
public function testSelfOfADomainAdmin(): void
{
$result = $this->post('edit/self', ['attr' => self::PASSWORDS], ['session' => self::DOMAIN_ADMIN]);
$this->assertSame([['edit', self::PASSWORDS]], $result->callsTo('domain_admin'));
}
public function testSelfOfAMailboxUser(): void
{
$result = $this->post('edit/self', ['attr' => self::PASSWORDS], ['session' => self::USER]);
$this->assertSame([[self::PASSWORDS]], $result->callsTo('edit_user_account'));
}
public function testFido2FriendlyName(): void
{
$attr = ['fido2_cid' => hash('sha256', 'credential'), 'fido2_fn' => 'YubiKey'];
$result = $this->post('edit/fido2-fn', ['attr' => $attr]);
$this->assertSame([[['action' => 'edit_fn', 'fido2_attrs' => $attr]]], $result->callsTo('fido2'));
}
public function testPendingPasswordUpdateAllowsOnlyThePasswordRoutes(): void
{
$session = self::ADMIN + ['pending_pw_update' => true];
$this->assertCalled($this->post('edit/admin', ['items' => 'admin', 'attr' => ['password' => 'x', 'password2' => 'x']], ['session' => $session]),
'admin', ['edit', ['username' => ['admin'], 'password' => 'x', 'password2' => 'x']]);
$this->assertCalled($this->get('get/passwordpolicy', ['session' => $session]), 'password_complexity', ['get']);
$denied = $this->post('edit/domain-admin', ['items' => 'domainadmin1', 'attr' => ['active' => 0]], ['session' => $session]);
$this->assertApiError($denied, 403, 'API access denied until the pending account setup is finished');
$this->assertNotCalled($denied, 'domain_admin');
}
}
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
/** Routes passwordpolicy, reset-password-notification, the UI customization routes and cors */
class SettingsRoutesTest extends ApiTestCase
{
private const POLICY = ['length' => '8', 'chars' => '1', 'special_chars' => '0', 'lowerupper' => '1', 'numbers' => '1'];
public function testGetPasswordPolicy(): void
{
foreach (['get/passwordpolicy', 'get/passwordpolicy/all'] as $route) {
$result = $this->get($route, ['script' => [self::answer('password_complexity', ['get'], self::POLICY)]]);
$this->assertSame([['get']], $result->callsTo('password_complexity'), $route);
$this->assertSame(self::POLICY, $result->json(), $route);
}
}
public function testGetPasswordPolicyAsHtml(): void
{
$html = '<p class="help-block small">- Minimum password length is 8</p>';
$result = $this->get('get/passwordpolicy/html', ['script' => [self::answer('password_complexity', ['html'], $html)]]);
$this->assertSame([['html']], $result->callsTo('password_complexity'));
$this->assertSame($html, $result->json());
}
public function testGetPasswordPolicyFailure(): void
{
$this->assertSame('{}', $this->get('get/passwordpolicy', ['script' => [self::answer('password_complexity', ['get'], false)]])->body);
$this->assertSame('{}', $this->get('get/passwordpolicy/html', ['script' => [self::answer('password_complexity', ['html'], false)]])->body);
}
public function testEditPasswordPolicyIgnoresItems(): void
{
$result = $this->post('edit/passwordpolicy', ['items' => ['x'], 'attr' => ['length' => 12, 'numbers' => 1]]);
$this->assertSame([['edit', ['length' => 12, 'numbers' => 1]]], $result->callsTo('password_complexity'));
}
public function testEditPasswordResetNotification(): void
{
$attr = ['from' => 'noreply@example.org', 'subject' => 'Reset your password', 'text_tmpl' => '{{link}}', 'html_tmpl' => ''];
$result = $this->post('edit/reset-password-notification', ['attr' => $attr]);
$this->assertSame([['edit_notification', $attr]], $result->callsTo('reset_password'));
}
/** @return array<string, array{0: string, 1: array}> */
public static function customizations(): array
{
return [
'ui_texts' => ['ui_texts', ['title_name' => 'Example Mail', 'ui_announcement_type' => 'warning', 'ui_announcement_active' => 1]],
'ip_check' => ['ip_check', ['ip_check_opt_in' => '1']],
'custom_login' => ['custom_login', ['hide_admin_quicklink' => 1, 'force_sso' => 0]],
'ui_theme' => ['ui_theme', ['ui_theme' => 'servercow']],
'app_links' => ['app_links', ['app' => ['Wiki'], 'href' => ['https://wiki.example.org'], 'user_href' => [''], 'hide' => ['0']]],
];
}
#[DataProvider('customizations')]
public function testCustomizationsPassAttrOnly(string $item, array $attr): void
{
$messages = [self::message('success', $item === 'ip_check' ? 'ip_check_opt_in_modified' : $item)];
$result = $this->post("edit/$item", ['items' => ['ignored'], 'attr' => $attr], ['script' => [self::answer('customize', ['edit', $item], null, $messages)]]);
$this->assertSame([['edit', $item, $attr]], $result->callsTo('customize'));
$this->assertSame($messages, $result->json());
}
public function testCustomizationWithoutAttrPassesNothing(): void
{
$result = $this->post('edit/app_links', ['items' => []]);
$this->assertSame([['edit', 'app_links', []]], $result->callsTo('customize'));
}
public function testEditCors(): void
{
$attr = ['allowed_origins' => ['https://app.example.org'], 'allowed_methods' => ['GET', 'POST']];
$result = $this->post('edit/cors', ['attr' => $attr]);
$this->assertSame([['set_headers'], ['edit', $attr]], $result->callsTo('cors'));
}
}
+120
View File
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Admin;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes status/<object> */
class StatusRoutesTest extends ApiTestCase
{
public function testContainersAreReducedToStateStartAndImage(): void
{
$info = [
'dovecot-mailcow' => ['State' => ['Status' => 'running', 'StartedAt' => '2026-11-10T11:00:51.541059812Z', 'Pid' => 42],
'Config' => ['Image' => 'ghcr.io/mailcow/dovecot:2.3.21.1-4', 'Hostname' => 'dovecot'], 'Id' => '5d0f3cbb2a6e'],
'ofelia-mailcow' => ['State' => ['Status' => 'exited', 'StartedAt' => '2026-11-09T08:00:00Z'],
'Config' => ['Image' => 'mcuadros/ofelia:latest'], 'Id' => '77aa01'],
];
$result = $this->get('get/status/containers', ['script' => [self::answer('docker', ['info'], $info)]]);
$this->assertSame([['info']], $result->callsTo('docker'));
$this->assertSame([
'dovecot-mailcow' => ['type' => 'info', 'container' => 'dovecot-mailcow', 'state' => 'running',
'started_at' => '2026-11-10T11:00:51.541059812Z', 'image' => 'ghcr.io/mailcow/dovecot:2.3.21.1-4'],
'ofelia-mailcow' => ['type' => 'info', 'container' => 'ofelia-mailcow', 'state' => 'exited',
'started_at' => '2026-11-09T08:00:00Z', 'image' => 'mcuadros/ofelia:latest'],
], $result->json());
$this->assertStringContainsString('"ghcr.io/mailcow/dovecot', $result->body);
}
public function testContainersWithoutDockerapi(): void
{
$result = $this->get('get/status/containers', ['script' => [self::answer('docker', ['info'], 'Could not resolve host: dockerapi')]]);
$this->assertSame('null', $result->body);
}
public function testContainerStatsByContainerId(): void
{
$stats = [['read' => '2026-11-10T19:20:01Z', 'pids_stats' => ['current' => 38]], ['read' => '2026-11-10T19:20:06Z', 'pids_stats' => ['current' => 39]]];
$result = $this->get('get/status/container/5d0f3cbb2a6e', ['script' => [self::answer('docker', ['container_stats'], $stats)]]);
$this->assertSame([['container_stats', '5d0f3cbb2a6e']], $result->callsTo('docker'));
$this->assertSame($stats, $result->json());
}
public function testContainerStatsWithoutId(): void
{
$result = $this->get('get/status/container', ['script' => [self::answer('docker', ['container_stats'], false)]]);
$this->assertSame([['container_stats', null]], $result->callsTo('docker'));
$this->assertSame('false', $result->body);
}
public function testVmailSplitsTheDfLine(): void
{
$df = json_encode('/dev/sda1,105G,65G,40G,66%,/var/vmail');
$result = $this->get('get/status/vmail', ['script' => [self::answer('docker', ['post', 'dovecot-mailcow', 'exec'], $df)]]);
$this->assertSame([['post', 'dovecot-mailcow', 'exec', ['cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail']]], $result->callsTo('docker'));
$this->assertSame(['type' => 'info', 'disk' => '/dev/sda1', 'used' => '65G', 'total' => '105G', 'used_percent' => '66%'], $result->json());
}
public function testHost(): void
{
$stats = ['cpu' => ['cores' => 4, 'usage' => 6], 'memory' => ['total' => 12541669376, 'usage' => 62.5], 'uptime' => 703800.5,
'system_time' => '10.11.2026 19:13:07', 'architecture' => 'x86_64'];
$result = $this->get('get/status/host', ['script' => [self::answer('docker', ['host_stats'], $stats)]]);
$this->assertSame([['host_stats']], $result->callsTo('docker'));
$this->assertSame($stats, $result->json());
}
public function testPublicAddressesAskMailcowEmail(): void
{
$result = $this->get('get/status/host/ip', ['script' => [
self::answer('curl_exec', [], '198.51.100.7', once: true),
self::answer('curl_exec', [], '2001:db8::7', once: true),
]]);
$this->assertSame([[null, CURLOPT_URL, 'http://ipv4.mailcow.email'], [null, CURLOPT_URL, 'http://ipv6.mailcow.email']],
array_values(array_filter($result->callsTo('curl_setopt'), fn(array $args): bool => $args[1] === CURLOPT_URL)));
$this->assertSame(['ipv4' => '198.51.100.7', 'ipv6' => '2001:db8::7'], $result->json());
}
public function testHostWithAnotherSegmentAnswersNothing(): void
{
$result = $this->get('get/status/host/cpu');
$this->assertSame([200, ''], [$result->status, $result->body]);
$this->assertNotCalled($result, 'docker');
}
public function testVersion(): void
{
// $MAILCOW_GIT_VERSION comes from app_info.inc.php, which the harness does not load
$this->assertSame(['version' => null], $this->get('get/status/version')->json());
}
public function testUnknownStatusAnswersNothing(): void
{
$result = $this->get('get/status/disks');
$this->assertSame([200, ''], [$result->status, $result->body]);
}
public function testOnlyForAdmins(): void
{
foreach (['containers', 'vmail', 'host', 'version'] as $object) {
$result = $this->get("get/status/$object", ['session' => self::DOMAIN_ADMIN]);
$this->assertSame([200, ''], [$result->status, $result->body], $object);
$this->assertNotCalled($result, 'docker');
}
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api;
/** What json_api.php did for one request of ApiTestCase: status, output and the calls it made */
final class ApiResult
{
/**
* @param list<array{0: string, 1: array}> $calls function or method name and arguments, in call order
*/
public function __construct(
public readonly int $status,
public readonly string $body,
public readonly array $calls,
public readonly ?array $sessionReturn,
public readonly ?string $error
) {
}
/** The decoded output, null if it is no JSON */
public function json(): mixed
{
return json_decode($this->body, true);
}
/**
* The arguments of every call of $name, e.g. 'mailbox' or 'Mailcow\ApiKey\ApiKeyModule->api->addKey'
*
* @return list<array>
*/
public function callsTo(string $name): array
{
return array_values(array_map(fn(array $call): array => $call[1], array_filter($this->calls, fn(array $call): bool => $call[0] === $name)));
}
/** Names of the calls, without the API log of every request (redis->lPush) */
public function callNames(): array
{
return array_values(array_filter(array_column($this->calls, 0), fn(string $name): bool => $name !== 'redis->lPush'));
}
}
+150
View File
@@ -0,0 +1,150 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api;
use PHPUnit\Framework\TestCase;
/**
* Runs requests through the real data/web/json_api.php in a process of its own (Harness/runner.php). The legacy
* functions (mailbox(), bcc(), ...) and the module classes it calls are recorders: a test checks the HTTP behaviour of
* a route and which backend call it makes with which arguments, not the backend itself.
*
* $result = $this->post('add/alias', ['address' => 'a@example.org', 'goto' => 'b@example.org']);
* $this->assertCalled($result, 'mailbox', ['add', 'alias', ['address' => 'a@example.org', 'goto' => 'b@example.org']]);
*
* Backend answers are scripted with self::answer(); a call without one returns null (module chains like
* Module::fromGlobals()->api->x() resolve to recorders).
*/
abstract class ApiTestCase extends TestCase
{
/** Session of a read/write API key (sessions.inc.php logs it in as the admin "API") */
protected const API_RW = ['mailcow_cc_username' => 'API', 'mailcow_cc_role' => 'admin', 'mailcow_cc_api' => true, 'mailcow_cc_api_access' => 'rw'];
/** Session of a read-only API key */
protected const API_RO = ['mailcow_cc_username' => 'API', 'mailcow_cc_role' => 'admin', 'mailcow_cc_api' => true, 'mailcow_cc_api_access' => 'ro'];
/** UI sessions */
protected const ADMIN = ['mailcow_cc_username' => 'admin', 'mailcow_cc_role' => 'admin'];
protected const DOMAIN_ADMIN = ['mailcow_cc_username' => 'domainadmin', 'mailcow_cc_role' => 'domainadmin'];
protected const USER = ['mailcow_cc_username' => 'user@example.org', 'mailcow_cc_role' => 'user'];
protected const NO_SESSION = [];
private const JSON_API = __DIR__ . '/../../../data/web/json_api.php';
/** PHP functions with outside effects that json_api.php calls directly: disabled, so the harness records them */
private const DISABLED = ['curl_init', 'curl_setopt', 'curl_setopt_array', 'curl_exec', 'curl_close', 'curl_error', 'curl_errno', 'curl_getinfo'];
/**
* One scripted backend answer: the first call of $call whose arguments start with $args returns $return and
* appends $messages to $_SESSION['return'] (what the legacy functions do), $once makes it answer one call only.
*/
protected static function answer(string $call, array $args = [], mixed $return = null, array $messages = [], bool $once = false): array
{
return ['call' => $call, 'args' => $args, 'return' => $return, 'messages' => $messages, 'once' => $once];
}
/** A mailcow message as the legacy functions put it into $_SESSION['return'] */
protected static function message(string $type, string|array $msg, array $log = []): array
{
return ['type' => $type, 'log' => $log, 'msg' => $msg];
}
/**
* @param array{session?: array, script?: list<array>, server?: array, query?: array} $options
*/
protected function get(string $route, array $options = []): ApiResult
{
return $this->request('GET', $route, $options);
}
/**
* POST with a JSON body (any value; a string is sent as it is), as API clients send it
*
* @param array{session?: array, script?: list<array>, server?: array, query?: array, content_type?: string} $options
*/
protected function post(string $route, mixed $json = null, array $options = []): ApiResult
{
if ($json !== null) {
$options['body'] = is_string($json) ? $json : json_encode($json);
$options['content_type'] ??= 'application/json';
}
return $this->request('POST', $route, $options);
}
/**
* POST with form fields (attr, items as JSON strings), as the mailcow UI sends it
*
* @param array<string, string> $fields
*/
protected function postForm(string $route, array $fields, array $options = []): ApiResult
{
$options['post'] = $fields;
$options['content_type'] ??= 'application/x-www-form-urlencoded';
return $this->request('POST', $route, $options);
}
/**
* @param string $route the part after /api/v1/, e.g. "get/domain/all"
* @param array{session?: array, script?: list<array>, server?: array, query?: array, post?: array, body?: string,
* content_type?: string, globals?: array, allow_error?: bool} $options globals: variables prerequisites.inc.php
* would set (e.g. iam_settings)
*/
protected function request(string $method, string $route, array $options = []): ApiResult
{
$dir = sys_get_temp_dir();
$requestFile = tempnam($dir, 'api-req-');
$resultFile = tempnam($dir, 'api-res-');
try {
file_put_contents($requestFile, json_encode([
'json_api' => realpath(self::JSON_API),
'method' => $method,
'query' => ltrim(preg_replace('#^/?api/v1/#', '', $route), '/'),
'get' => $options['query'] ?? [],
'post' => $options['post'] ?? [],
'body' => $options['body'] ?? null,
'content_type' => $options['content_type'] ?? null,
'server' => $options['server'] ?? [],
'session' => $options['session'] ?? self::API_RW,
'script' => $options['script'] ?? [],
'globals' => $options['globals'] ?? [],
'result' => $resultFile,
], JSON_THROW_ON_ERROR));
$process = proc_open([PHP_BINARY, '-d', 'display_errors=stderr', '-d', 'disable_functions=' . implode(',', self::DISABLED),
__DIR__ . '/Harness/runner.php', $requestFile],
[0 => ['file', '/dev/null', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
$data = json_decode((string)file_get_contents($resultFile), true);
$this->assertIsArray($data, "the harness wrote no result for $method $route:\n$stdout$stderr");
} finally {
@unlink($requestFile);
@unlink($resultFile);
}
$result = new ApiResult((int)$data['status'], (string)$data['body'], $data['calls'], $data['session_return'], $data['error']);
if (empty($options['allow_error'])) {
$this->assertNull($result->error, "json_api.php failed for $method $route");
}
return $result;
}
/** At least one call of $name had exactly $args */
protected function assertCalled(ApiResult $result, string $name, array $args): void
{
$this->assertContains($args, $result->callsTo($name),
"no call $name(" . json_encode($args) . "), calls were:\n" . json_encode($result->calls, JSON_PRETTY_PRINT));
}
/** $name was not called at all */
protected function assertNotCalled(ApiResult $result, string $name): void
{
$this->assertSame([], $result->callsTo($name), "unexpected call of $name");
}
/** The JSON answer json_api.php gives without calling a backend, e.g. 404 route not found */
protected function assertApiError(ApiResult $result, int $status, string $msg): void
{
$this->assertSame([$status, ['type' => 'error', 'msg' => $msg]], [$result->status, $result->json()], $result->body);
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** get/add/edit/delete alias-domain */
class AliasDomainTest extends ApiTestCase
{
public function testAllAliasDomainsAreReadOneByOne(): void
{
$result = $this->get('get/alias-domain/all', ['script' => [
self::answer('mailbox', ['get', 'alias_domains'], ['example.net', 'example.com']),
self::answer('mailbox', ['get', 'alias_domain_details', 'example.net'], ['alias_domain' => 'example.net', 'target_domain' => 'example.org']),
self::answer('mailbox', ['get', 'alias_domain_details', 'example.com'], false),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'alias_domains']);
$this->assertSame([['alias_domain' => 'example.net', 'target_domain' => 'example.org']], $result->json());
}
public function testAllAliasDomainsWithoutAliasDomains(): void
{
$result = $this->get('get/alias-domain/all', ['script' => [self::answer('mailbox', ['get', 'alias_domains'], [])]]);
$this->assertSame('{}', $result->body);
$this->assertSame([['get', 'alias_domains']], $result->callsTo('mailbox'));
}
public function testOneAliasDomain(): void
{
$result = $this->get('get/alias-domain/example.net', ['script' => [
self::answer('mailbox', ['get', 'alias_domain_details', 'example.net'], ['alias_domain' => 'example.net', 'rl' => false]),
]]);
$this->assertSame(['alias_domain' => 'example.net', 'rl' => false], $result->json());
}
public function testUnknownAliasDomain(): void
{
$result = $this->get('get/alias-domain/example.com', ['script' => [self::answer('mailbox', ['get', 'alias_domain_details'], false)]]);
$this->assertCalled($result, 'mailbox', ['get', 'alias_domain_details', 'example.com']);
$this->assertSame('{}', $result->body);
}
public function testAddAliasDomains(): void
{
$attr = ['alias_domain' => "example.net\nexample.com", 'target_domain' => 'example.org', 'active' => '1', 'key_size' => '2048', 'dkim_selector' => 'dkim'];
$result = $this->post('add/alias-domain', $attr);
$this->assertCalled($result, 'mailbox', ['add', 'alias_domain', $attr]);
}
public function testEditAliasDomains(): void
{
$result = $this->post('edit/alias-domain', ['items' => ['example.net', 'example.com'], 'attr' => ['active' => '0']]);
$this->assertCalled($result, 'mailbox', ['edit', 'alias_domain', ['alias_domain' => ['example.net', 'example.com'], 'active' => '0']]);
}
public function testDeleteAliasDomains(): void
{
$result = $this->post('delete/alias-domain', ['example.net']);
$this->assertCalled($result, 'mailbox', ['delete', 'alias_domain', ['alias_domain' => ['example.net']]]);
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** get/dkim, add/dkim, add/dkim_duplicate, add/dkim_import, delete/dkim */
class DkimTest extends ApiTestCase
{
public function testDkimKeyOfADomain(): void
{
$key = ['pubkey' => 'MIIBIjAN', 'length' => '2048', 'dkim_txt' => 'v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjAN', 'dkim_selector' => 'dkim', 'privkey' => ''];
$result = $this->get('get/dkim/example.org', ['script' => [self::answer('dkim', ['details', 'example.org'], $key)]]);
$this->assertSame([['details', 'example.org']], $result->callsTo('dkim'));
$this->assertSame($key, $result->json());
}
public function testDomainWithoutKey(): void
{
$result = $this->get('get/dkim/example.com', ['script' => [self::answer('dkim', ['details'], [])]]);
$this->assertSame('{}', $result->body);
}
public function testGenerateKeys(): void
{
$attr = ['domains' => 'example.org, example.net', 'dkim_selector' => 'dkim', 'key_size' => 2048];
$result = $this->post('add/dkim', $attr);
$this->assertSame([['add', $attr]], $result->callsTo('dkim'));
}
public function testDuplicateKey(): void
{
$attr = ['from_domain' => 'example.org', 'to_domain' => ['example.net', 'example.com']];
$result = $this->post('add/dkim_duplicate', $attr);
$this->assertSame([['duplicate', $attr]], $result->callsTo('dkim'));
}
public function testImportKey(): void
{
$attr = ['domain' => 'example.org', 'dkim_selector' => 'dkim2026', 'private_key_file' => "-----BEGIN PRIVATE KEY-----\nMIIE\n-----END PRIVATE KEY-----", 'overwrite_existing' => '1'];
$result = $this->post('add/dkim_import', $attr);
$this->assertSame([['import', $attr]], $result->callsTo('dkim'));
}
public function testDeleteKeys(): void
{
$result = $this->post('delete/dkim', ['example.org', 'example.net']);
$this->assertSame([['delete', ['domains' => ['example.org', 'example.net']]]], $result->callsTo('dkim'));
}
}
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** get/policy_wl_domain, get/policy_bl_domain, add/domain-policy, delete/domain-policy */
class DomainPolicyTest extends ApiTestCase
{
private const LISTS = [
'whitelist' => [['object' => 'example.org', 'value' => '*@partner.example.com', 'prefid' => 12]],
'blacklist' => [['object' => 'anna@example.org', 'value' => '*@spam.example.com', 'prefid' => 13]],
];
public function testAllowListOfADomain(): void
{
$result = $this->get('get/policy_wl_domain/example.org', ['script' => [self::answer('policy', ['get', 'domain', 'example.org'], self::LISTS)]]);
$this->assertSame([['get', 'domain', 'example.org']], $result->callsTo('policy'));
$this->assertSame(self::LISTS['whitelist'], $result->json());
}
public function testDenyListOfADomain(): void
{
$result = $this->get('get/policy_bl_domain/example.org', ['script' => [self::answer('policy', ['get', 'domain', 'example.org'], self::LISTS)]]);
$this->assertSame([['get', 'domain', 'example.org']], $result->callsTo('policy'));
$this->assertSame(self::LISTS['blacklist'], $result->json());
}
public function testEmptyListsAndUnknownDomains(): void
{
$empty = $this->get('get/policy_wl_domain/example.org', ['script' => [self::answer('policy', ['get', 'domain'], ['whitelist' => [], 'blacklist' => []])]]);
$this->assertSame('{}', $empty->body);
$unknown = $this->get('get/policy_bl_domain/example.com', ['script' => [self::answer('policy', ['get', 'domain'], false)]]);
$this->assertSame('{}', $unknown->body);
}
public function testAddEntry(): void
{
$attr = ['domain' => 'example.org', 'object_list' => 'wl', 'object_from' => '*@partner.example.com'];
$result = $this->post('add/domain-policy', $attr);
$this->assertSame([['add', 'domain', $attr]], $result->callsTo('policy'));
}
public function testDeleteEntries(): void
{
$result = $this->post('delete/domain-policy', [12, 13]);
$this->assertSame([['delete', 'domain', ['prefid' => [12, 13]]]], $result->callsTo('policy'));
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** get/rl-domain, edit/rl-domain */
class DomainRatelimitTest extends ApiTestCase
{
public function testRatelimitOfADomain(): void
{
$result = $this->get('get/rl-domain/example.org', ['script' => [self::answer('ratelimit', ['get', 'domain', 'example.org'], ['value' => '100', 'frame' => 'h'])]]);
$this->assertSame([['get', 'domain', 'example.org']], $result->callsTo('ratelimit'));
$this->assertSame(['value' => '100', 'frame' => 'h'], $result->json());
}
public function testDomainWithoutRatelimit(): void
{
$result = $this->get('get/rl-domain/example.org', ['script' => [self::answer('ratelimit', ['get', 'domain'], false)]]);
$this->assertSame('{}', $result->body);
}
public function testAllRatelimitsOfDomainsAndAliasDomains(): void
{
$result = $this->get('get/rl-domain/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com']),
self::answer('mailbox', ['get', 'alias_domains'], ['example.net']),
self::answer('ratelimit', ['get', 'domain', 'example.org'], ['value' => '100', 'frame' => 'h']),
self::answer('ratelimit', ['get', 'domain', 'example.com'], false),
self::answer('ratelimit', ['get', 'domain', 'example.net'], ['value' => '10', 'frame' => 's']),
]]);
$this->assertSame([['get', 'domains'], ['get', 'alias_domains']], $result->callsTo('mailbox'));
$this->assertSame([['get', 'domain', 'example.org'], ['get', 'domain', 'example.com'], ['get', 'domain', 'example.net']], $result->callsTo('ratelimit'));
$this->assertSame([
['value' => '100', 'frame' => 'h', 'domain' => 'example.org'],
['value' => '10', 'frame' => 's', 'domain' => 'example.net'],
], $result->json());
}
public function testAllWithoutRatelimits(): void
{
$none = $this->get('get/rl-domain/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org']),
self::answer('mailbox', ['get', 'alias_domains'], []),
self::answer('ratelimit', ['get', 'domain'], false),
]]);
$this->assertSame('{}', $none->body);
$noDomains = $this->get('get/rl-domain/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], []),
self::answer('mailbox', ['get', 'alias_domains'], []),
]]);
$this->assertSame('{}', $noDomains->body);
$this->assertNotCalled($noDomains, 'ratelimit');
}
public function testSetRatelimits(): void
{
$result = $this->post('edit/rl-domain', ['items' => ['example.org', 'example.net'], 'attr' => ['rl_value' => 100, 'rl_frame' => 'h']]);
$this->assertSame([['edit', 'domain', ['object' => ['example.org', 'example.net'], 'rl_value' => 100, 'rl_frame' => 'h']]], $result->callsTo('ratelimit'));
}
}
+115
View File
@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiResult;
use Mailcow\Tests\Api\ApiTestCase;
/** search/domain: DataTables server-side processing (SSP) of the domain table */
class DomainSearchTest extends ApiTestCase
{
private const REQUEST = [
'draw' => 3,
'start' => 0,
'length' => 10,
'search' => ['value' => 'example'],
'order' => [['column' => 2, 'dir' => 'asc']],
'columns' => [
['data' => '', 'searchable' => false, 'orderable' => false],
['data' => 'chkbox', 'searchable' => false, 'orderable' => false],
['data' => 'domain_h_name', 'searchable' => true, 'orderable' => true, 'search' => ['value' => '']],
],
];
private const PAGE = ['draw' => 3, 'recordsTotal' => 12, 'recordsFiltered' => 2, 'data' => [[2 => 'example.org'], [2 => 'example.com']]];
public function testAdminsSearchAllDomains(): void
{
$result = $this->post('search/domain', self::REQUEST, ['script' => [self::answer('SSP::simple', [], self::PAGE)]]);
$call = $this->sspCall($result, 'SSP::simple');
$this->assertSame([self::REQUEST, '@pdo', ['domain', 'd'], 'domain'], array_slice($call, 0, 4));
$this->assertSame([
2 => 'domain', 3 => 'aliases', 4 => 'mailboxes', 5 => 'quota', 6 => 'stats', 7 => 'defquota', 8 => 'maxquota',
10 => 'backupmx', 14 => 'tags', 15 => 'active',
], array_column($call[4], 'db', 'dt'));
$this->assertNotCalled($result, 'SSP::complex');
}
public function testRowsAreReplacedByTheDomainDetails(): void
{
$result = $this->post('search/domain', self::REQUEST, ['script' => [
self::answer('SSP::simple', [], self::PAGE),
self::answer('mailbox', ['get', 'domain_details', 'example.org'], ['domain_name' => 'example.org']),
self::answer('mailbox', ['get', 'domain_details', 'example.com'], false),
]]);
$this->assertSame([['get', 'domain_details', 'example.org'], ['get', 'domain_details', 'example.com']], $result->callsTo('mailbox'));
$this->assertSame(['draw' => 3, 'recordsTotal' => 12, 'recordsFiltered' => 2, 'data' => [['domain_name' => 'example.org']]], $result->json());
}
public function testAnEmptyPageIsAnsweredAsItIs(): void
{
$page = ['draw' => 1, 'recordsTotal' => 12, 'recordsFiltered' => 0, 'data' => []];
$result = $this->post('search/domain', ['draw' => 1], ['script' => [self::answer('SSP::simple', [], $page)]]);
$this->assertSame($page, $result->json());
$this->assertNotCalled($result, 'mailbox');
}
public function testErrorsOfTheSearchArePassedOn(): void
{
$result = $this->post('search/domain', self::REQUEST, ['script' => [self::answer('SSP::simple', [], ['error' => 'An SQL error occurred: unknown column'])]]);
$this->assertSame(['error' => 'An SQL error occurred: unknown column'], $result->json());
}
public function testReadOnlyKeysCanSearch(): void
{
$result = $this->post('search/domain', self::REQUEST, ['session' => self::API_RO, 'script' => [self::answer('SSP::simple', [], self::PAGE)]]);
$this->assertSame(200, $result->status);
$this->assertSame(self::REQUEST, $this->sspCall($result, 'SSP::simple')[0]);
}
public function testDomainAdminsSearchTheirDomains(): void
{
$result = $this->post('search/domain', self::REQUEST, ['session' => self::DOMAIN_ADMIN, 'script' => [self::answer('SSP::complex', [], self::PAGE)]]);
$call = $this->sspCall($result, 'SSP::complex');
$this->assertSame([self::REQUEST, '@pdo', ['domain', 'd'], 'domain'], array_slice($call, 0, 4));
$this->assertSame(['INNER JOIN domain_admins as da ON da.domain = d.domain', [
'condition' => 'da.active = 1 and da.username = :username',
'bindings' => ['username' => 'domainadmin'],
]], array_slice($call, 5));
$this->assertNotCalled($result, 'SSP::simple');
}
public function testOtherLoginsAreRefused(): void
{
foreach ([self::USER, self::NO_SESSION] as $session) {
$result = $this->post('search/domain', self::REQUEST, ['session' => $session]);
$this->assertApiError($result, 403, 'Insufficient permissions');
$this->assertNotCalled($result, 'SSP::simple');
$this->assertNotCalled($result, 'SSP::complex');
}
}
public function testWithoutJsonBodyTheSearchGetsNoParameters(): void
{
$result = $this->request('POST', 'search/domain', ['script' => [self::answer('SSP::simple', [], self::PAGE)]]);
$this->assertNull($this->sspCall($result, 'SSP::simple')[0]);
}
private function sspCall(ApiResult $result, string $name): array
{
$calls = $result->callsTo($name);
$this->assertCount(1, $calls, "one call of $name");
return $calls[0];
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** get/add/edit/delete domain/template */
class DomainTemplateTest extends ApiTestCase
{
private const TEMPLATE = ['id' => 1, 'template' => 'Default', 'type' => 'domain', 'attributes' => ['max_num_aliases_for_domain' => 400]];
public function testAllTemplates(): void
{
$result = $this->get('get/domain/template/all', ['script' => [self::answer('mailbox', ['get', 'domain_templates'], [self::TEMPLATE])]]);
$this->assertSame([['get', 'domain_templates']], $result->callsTo('mailbox'));
$this->assertSame([self::TEMPLATE], $result->json());
}
public function testOneTemplate(): void
{
$result = $this->get('get/domain/template/1', ['script' => [self::answer('mailbox', ['get', 'domain_templates', '1'], self::TEMPLATE)]]);
$this->assertSame(self::TEMPLATE, $result->json());
}
public function testUnknownTemplate(): void
{
$result = $this->get('get/domain/template/9', ['script' => [self::answer('mailbox', ['get', 'domain_templates'], false)]]);
$this->assertCalled($result, 'mailbox', ['get', 'domain_templates', '9']);
$this->assertSame('{}', $result->body);
}
public function testTemplateWithoutIdAsksForAll(): void
{
$result = $this->get('get/domain/template');
$this->assertSame([['get', 'domain_templates', null]], $result->callsTo('mailbox'));
}
public function testAddTemplate(): void
{
$attr = ['template' => 'Small business', 'max_num_mboxes_for_domain' => 25, 'def_quota_for_mbox' => 2048, 'tags' => ['small']];
$result = $this->post('add/domain/template', $attr);
$this->assertSame([['add', 'domain_templates', $attr]], $result->callsTo('mailbox'));
}
public function testEditTemplates(): void
{
$result = $this->post('edit/domain/template', ['items' => [2, 3], 'attr' => ['template' => 'Small business', 'active' => 1]]);
$this->assertCalled($result, 'mailbox', ['edit', 'domain_templates', ['ids' => [2, 3], 'template' => 'Small business', 'active' => 1]]);
}
public function testDeleteTemplates(): void
{
$result = $this->post('delete/domain/template', [2, 3]);
$this->assertCalled($result, 'mailbox', ['delete', 'domain_templates', ['ids' => [2, 3]]]);
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** get/domain, add/domain, edit/domain, delete/domain, delete/domain/tag, edit/domain/footer */
class DomainTest extends ApiTestCase
{
public function testAllDomainsAreReadOneByOne(): void
{
$result = $this->get('get/domain/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com', 'example.net']),
self::answer('mailbox', ['get', 'domain_details', 'example.org'], ['domain_name' => 'example.org']),
self::answer('mailbox', ['get', 'domain_details', 'example.com'], false),
self::answer('mailbox', ['get', 'domain_details', 'example.net'], ['domain_name' => 'example.net']),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'domains', null, null]);
$this->assertSame([['domain_name' => 'example.org'], ['domain_name' => 'example.net']], $result->json());
}
public function testAllDomainsFilteredByTags(): void
{
$result = $this->get('get/domain/all', ['query' => ['tags' => 'customer,premium'], 'script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org']),
self::answer('mailbox', ['get', 'domain_details', 'example.org'], ['domain_name' => 'example.org', 'tags' => ['customer']]),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'domains', null, ['customer', 'premium']]);
$this->assertSame([['domain_name' => 'example.org', 'tags' => ['customer']]], $result->json());
}
public function testAnEmptyTagsParameterDoesNotFilter(): void
{
$result = $this->get('get/domain/all', ['query' => ['tags' => ''], 'script' => [self::answer('mailbox', ['get', 'domains'], [])]]);
$this->assertCalled($result, 'mailbox', ['get', 'domains', null, null]);
}
public function testAllDomainsWithoutDomains(): void
{
$result = $this->get('get/domain/all', ['script' => [self::answer('mailbox', ['get', 'domains'], [])]]);
$this->assertSame('{}', $result->body);
$this->assertSame([['get', 'domains', null, null]], $result->callsTo('mailbox'));
}
public function testAllDomainsWithoutReadableDetails(): void
{
$result = $this->get('get/domain/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org']),
self::answer('mailbox', ['get', 'domain_details'], false),
]]);
$this->assertSame('{}', $result->body);
}
public function testOneDomain(): void
{
$result = $this->get('get/domain/example.org', ['script' => [
self::answer('mailbox', ['get', 'domain_details', 'example.org'], ['domain_name' => 'example.org', 'rl' => false]),
]]);
$this->assertSame(['domain_name' => 'example.org', 'rl' => false], $result->json());
$this->assertSame([['get', 'domain_details', 'example.org']], $result->callsTo('mailbox'));
}
public function testUnknownDomain(): void
{
$result = $this->get('get/domain/example.com', ['script' => [self::answer('mailbox', ['get', 'domain_details'], false)]]);
$this->assertSame('{}', $result->body);
}
public function testAddDomain(): void
{
$attr = ['domain' => 'example.org', 'description' => 'Example Org', 'defquota' => 3072, 'tags' => ['customer'], 'restart_sogo' => '1'];
$result = $this->post('add/domain', $attr);
$this->assertCalled($result, 'mailbox', ['add', 'domain', $attr]);
}
public function testAddDomainIgnoresFurtherSegments(): void
{
$result = $this->post('add/domain/example.org', ['domain' => 'example.org']);
$this->assertCalled($result, 'mailbox', ['add', 'domain', ['domain' => 'example.org']]);
}
public function testEditDomains(): void
{
$result = $this->post('edit/domain', ['items' => ['example.org', 'example.com'], 'attr' => ['quota' => 51200, 'tags' => ['premium']]]);
$this->assertCalled($result, 'mailbox', ['edit', 'domain', ['domain' => ['example.org', 'example.com'], 'quota' => 51200, 'tags' => ['premium']]]);
}
public function testEditDomainWithASingleItem(): void
{
$result = $this->post('edit/domain', ['items' => 'example.org', 'attr' => ['active' => '0']]);
$this->assertCalled($result, 'mailbox', ['edit', 'domain', ['domain' => ['example.org'], 'active' => '0']]);
}
public function testAnAttributeNamedLikeTheItemsWins(): void
{
$result = $this->post('edit/domain', ['items' => ['example.org'], 'attr' => ['domain' => 'example.com']]);
$this->assertCalled($result, 'mailbox', ['edit', 'domain', ['domain' => 'example.com']]);
}
public function testDeleteDomains(): void
{
$result = $this->post('delete/domain', ['example.org', 'example.com']);
$this->assertCalled($result, 'mailbox', ['delete', 'domain', ['domain' => ['example.org', 'example.com']]]);
}
public function testDeleteDomainTags(): void
{
$result = $this->post('delete/domain/tag/example.org', ['customer', 'premium']);
$this->assertCalled($result, 'mailbox', ['delete', 'tags_domain', ['tags' => ['customer', 'premium'], 'domain' => 'example.org']]);
}
public function testDeleteDomainTagsWithoutTags(): void
{
$result = $this->post('delete/domain/tag/example.org', [], ['script' => [self::answer('mailbox', ['delete', 'tags_domain'], false)]]);
$this->assertCalled($result, 'mailbox', ['delete', 'tags_domain', ['tags' => [], 'domain' => 'example.org']]);
$this->assertSame(['type' => 'error', 'msg' => 'Cannot delete item'], $result->json());
}
public function testDeleteDomainTagsWithoutDomain(): void
{
$result = $this->post('delete/domain/tag', ['customer']);
$this->assertCalled($result, 'mailbox', ['delete', 'tags_domain', ['tags' => ['customer'], 'domain' => null]]);
}
public function testEditDomainFooter(): void
{
$attr = ['html' => '<p>{= from_name =}</p>', 'plain' => '{= from_name =}', 'skip_replies' => 1, 'exclude' => ['noreply@example.org', 'example.net']];
$result = $this->post('edit/domain/footer', ['items' => ['example.org'], 'attr' => $attr]);
$this->assertCalled($result, 'mailbox', ['edit', 'domain_wide_footer', ['domains' => ['example.org']] + $attr]);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Domains;
use Mailcow\Tests\Api\ApiTestCase;
/** add/mta-sts, edit/mta-sts (there is no read route) */
class MtaStsTest extends ApiTestCase
{
public function testAddPolicy(): void
{
$attr = ['domain' => 'example.org', 'version' => 'stsv1', 'mode' => 'enforce', 'mx' => 'mail.example.org,mx2.example.org', 'max_age' => 86400, 'active' => 1];
$result = $this->post('add/mta-sts', $attr);
$this->assertSame([['add', 'mta_sts', $attr]], $result->callsTo('mailbox'));
// the backend reports no success message of its own
$this->assertSame(['type' => 'success', 'msg' => 'Task completed'], $result->json());
}
public function testEditPolicies(): void
{
$result = $this->post('edit/mta-sts', ['items' => ['example.org'], 'attr' => ['version' => 'stsv1', 'mode' => 'testing']]);
$this->assertSame([['edit', 'mta_sts', ['domains' => ['example.org'], 'version' => 'stsv1', 'mode' => 'testing']]], $result->callsTo('mailbox'));
}
public function testThereIsNoReadRoute(): void
{
$this->assertApiError($this->get('get/mta-sts/example.org'), 404, 'route not found');
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Filtering;
use Mailcow\Tests\Api\ApiTestCase;
/** Fail2Ban settings, bans and the banlist URL */
class Fail2BanTest extends ApiTestCase
{
private const BANLIST_ID = 'a2c11baf-5770-47de-9c5e-575bd7bc9218';
public function testGetFail2BanWithAnyObject(): void
{
$settings = ['ban_time' => 1800, 'ban_time_increment' => true, 'banlist_id' => self::BANLIST_ID, 'whitelist' => '192.0.2.0/24'];
foreach (['get/fail2ban', 'get/fail2ban/all'] as $route) {
$result = $this->get($route, ['script' => [self::answer('fail2ban', ['get'], $settings)]]);
$this->assertSame([['get']], $result->callsTo('fail2ban'), $route);
$this->assertSame($settings, $result->json(), $route);
}
$this->assertSame('{}', $this->get('get/fail2ban', ['script' => [self::answer('fail2ban', ['get'], false)]])->body);
}
public function testEditFail2BanSettings(): void
{
$attr = ['ban_time' => '1800', 'max_attempts' => '10', 'whitelist' => "192.0.2.0/24\nmail.example.org"];
$result = $this->post('edit/fail2ban', ['items' => [], 'attr' => $attr]);
$this->assertSame([['edit', ['network' => []] + $attr]], $result->callsTo('fail2ban'));
}
public function testItemsAreTheNetworks(): void
{
$list = $this->post('edit/fail2ban', ['items' => ['203.0.113.7/32', '2001:db8::/64'], 'attr' => ['action' => 'unban']]);
$this->assertCalled($list, 'fail2ban', ['edit', ['network' => ['203.0.113.7/32', '2001:db8::/64'], 'action' => 'unban']]);
$single = $this->post('edit/fail2ban', ['items' => '198.51.100.66/32', 'attr' => ['action' => 'blacklist']]);
$this->assertCalled($single, 'fail2ban', ['edit', ['network' => ['198.51.100.66/32'], 'action' => 'blacklist']]);
}
public function testFurtherSegmentsAreIgnored(): void
{
$result = $this->post('edit/fail2ban/self', ['items' => ['self'], 'attr' => ['action' => 'unban-all']]);
$this->assertSame([['edit', ['network' => ['self'], 'action' => 'unban-all']]], $result->callsTo('fail2ban'));
}
public function testRefreshBanlist(): void
{
$messages = [self::message('success', 'f2b_banlist_refreshed')];
$result = $this->post('edit/fail2ban/banlist', ['items' => [self::BANLIST_ID]], ['script' => [self::answer('fail2ban', ['banlist', 'refresh'], true, $messages)]]);
$this->assertSame([['banlist', 'refresh', [self::BANLIST_ID]]], $result->callsTo('fail2ban'));
$this->assertSame($messages, $result->json());
$single = $this->post('edit/fail2ban/banlist', ['items' => self::BANLIST_ID, 'attr' => []]);
$this->assertSame([['banlist', 'refresh', [self::BANLIST_ID]]], $single->callsTo('fail2ban'));
}
public function testRefreshBanlistWithAWrongId(): void
{
$result = $this->post('edit/fail2ban/banlist', ['items' => ['wrong']], ['script' => [self::answer('fail2ban', ['banlist'], false)]]);
$this->assertSame(['type' => 'error', 'msg' => 'Cannot edit item'], $result->json());
}
}
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Filtering;
use Mailcow\Tests\Api\ApiTestCase;
/** Global Sieve prefilter and postfilter */
class GlobalFilterTest extends ApiTestCase
{
private const FILTERS = ['prefilter' => "# before\n", 'postfilter' => "require \"fileinto\";\n"];
public function testAddGlobalFilter(): void
{
$attr = ['filter_type' => 'postfilter', 'script_data' => "require \"fileinto\";\nfileinto \"Junk\";"];
$messages = [
self::message('success', 'dovecot_restart_success'),
self::message('success', 'global_filter_written'),
];
$result = $this->post('add/global-filter', $attr, ['script' => [self::answer('mailbox', ['add', 'global_filter'], true, $messages)]]);
$this->assertCalled($result, 'mailbox', ['add', 'global_filter', $attr]);
$this->assertSame($messages, $result->json());
}
public function testGetAllGlobalFilters(): void
{
$result = $this->get('get/global_filters/all', ['script' => [self::answer('mailbox', ['get', 'global_filter_details'], self::FILTERS)]]);
$this->assertSame([['get', 'global_filter_details']], $result->callsTo('mailbox'));
$this->assertSame(self::FILTERS, $result->json());
$this->assertSame('{}', $this->get('get/global_filters/all', ['script' => [self::answer('mailbox', ['get', 'global_filter_details'], false)]])->body);
}
public function testOneGlobalFilterIsAJsonString(): void
{
$script = [self::answer('mailbox', ['get', 'global_filter_details'], self::FILTERS)];
$this->assertSame("# before\n", $this->get('get/global_filters/prefilter', ['script' => $script])->json());
$this->assertSame("require \"fileinto\";\n", $this->get('get/global_filters/postfilter', ['script' => $script])->json());
$empty = [self::answer('mailbox', ['get', 'global_filter_details'], ['prefilter' => '', 'postfilter' => ''])];
$this->assertSame('{}', $this->get('get/global_filters/prefilter', ['script' => $empty])->body);
$this->assertSame('{}', $this->get('get/global_filters/postfilter', ['script' => $empty])->body);
}
public function testOtherObjectsAnswerNothing(): void
{
$result = $this->get('get/global_filters/before', ['script' => [self::answer('mailbox', ['get', 'global_filter_details'], self::FILTERS)]]);
$this->assertSame([200, ''], [$result->status, $result->body]);
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Filtering;
use Mailcow\Tests\Api\ApiTestCase;
/** Quarantine items and settings */
class QuarantineTest extends ApiTestCase
{
public function testGetQuarantine(): void
{
$items = [['id' => 33, 'qid' => '8224615004C1', 'subject' => 'You have won', 'virus_flag' => 0, 'score' => 15.48, 'rcpt' => 'john.doe@example.org']];
$result = $this->get('get/quarantine/all', ['script' => [self::answer('quarantine', ['get'], $items)]]);
$this->assertSame([['get']], $result->callsTo('quarantine'));
$this->assertSame($items, $result->json());
$this->assertSame('[]', $this->get('get/quarantine/all')->body);
}
public function testGetQuarantineItem(): void
{
$item = ['id' => 33, 'qid' => '8224615004C1', 'msg' => "Subject: You have won\r\n\r\nClick here\r\n", 'qhash' => str_repeat('a', 64)];
$result = $this->get('get/quarantine/33', ['script' => [self::answer('quarantine', ['details', '33'], $item)]]);
$this->assertSame([['details', '33']], $result->callsTo('quarantine'));
$this->assertSame($item, $result->json());
$this->assertSame('[]', $this->get('get/quarantine/34', ['script' => [self::answer('quarantine', ['details'], false)]])->body);
}
public function testEditQuarantineItems(): void
{
$release = $this->post('edit/qitem', ['items' => ['33', '34'], 'attr' => ['action' => 'release']]);
$this->assertCalled($release, 'quarantine', ['edit', ['id' => ['33', '34'], 'action' => 'release']]);
$spam = $this->post('edit/qitem', ['items' => 35, 'attr' => ['action' => 'learnspam']]);
$this->assertCalled($spam, 'quarantine', ['edit', ['id' => [35], 'action' => 'learnspam']]);
}
public function testDeleteQuarantineItems(): void
{
$this->assertCalled($this->post('delete/qitem', ['33', '34']), 'quarantine', ['delete', ['id' => ['33', '34']]]);
$this->assertCalled($this->post('delete/qitem', '"33"'), 'quarantine', ['delete', ['id' => ['33']]]);
}
public function testEditQuarantineSettingsIgnoresItems(): void
{
$attr = ['action' => 'settings', 'retention_size' => '20', 'max_size' => '15', 'exclude_domains' => ['example.com']];
$messages = [self::message('success', 'saved_settings')];
$result = $this->post('edit/quarantine', ['items' => ['self'], 'attr' => $attr], ['script' => [self::answer('quarantine', ['edit'], true, $messages)]]);
$this->assertSame([['edit', $attr]], $result->callsTo('quarantine'));
$this->assertSame($messages, $result->json());
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Filtering;
use Mailcow\Tests\Api\ApiTestCase;
/** Postfix mail queue */
class QueueTest extends ApiTestCase
{
public function testGetMailQueuePassesTheJsonOfTheBackendThrough(): void
{
$queue = '[{"queue_name":"deferred","queue_id":"B98C6260CA1","sender":"sender@example.org","recipients":["rcpt@example.com"]}]';
$result = $this->get('get/mailq/all', ['script' => [self::answer('mailq', ['get'], $queue)]]);
$this->assertSame([['get']], $result->callsTo('mailq'));
$this->assertSame($queue, $result->body);
}
public function testEmptyMailQueue(): void
{
$this->assertSame('[]', $this->get('get/mailq/all', ['script' => [self::answer('mailq', ['get'], '[]')]])->body);
$this->assertSame('[]', $this->get('get/mailq/all', ['script' => [self::answer('mailq', ['get'], false)]])->body);
}
public function testPostcatAnswersThePlainText(): void
{
$text = "*** ENVELOPE RECORDS deferred/B/B98C6260CA1 ***\nsender: sender@example.org\n*** MESSAGE CONTENTS deferred/B/B98C6260CA1 ***\nSubject: Test\n";
$result = $this->get('get/postcat/B98C6260CA1', ['script' => [self::answer('mailq', ['cat'], $text)]]);
$this->assertSame([['cat', ['qid' => 'B98C6260CA1']]], $result->callsTo('mailq'));
$this->assertSame($text, $result->body);
$this->assertNull($result->json());
}
public function testPostcatWithoutAccessAnswersNothing(): void
{
$result = $this->get('get/postcat/B98C6260CA1', ['script' => [self::answer('mailq', ['cat'], false)]]);
$this->assertSame([200, ''], [$result->status, $result->body]);
}
public function testEditMailQueue(): void
{
$hold = $this->post('edit/mailq', ['items' => ['B98C6260CA1', '4C1E8260D02'], 'attr' => ['action' => 'hold']]);
$this->assertCalled($hold, 'mailq', ['edit', ['qid' => ['B98C6260CA1', '4C1E8260D02'], 'action' => 'hold']]);
$deliver = $this->post('edit/mailq', ['items' => 'B98C6260CA1', 'attr' => ['action' => 'deliver']]);
$this->assertCalled($deliver, 'mailq', ['edit', ['qid' => ['B98C6260CA1'], 'action' => 'deliver']]);
$flush = $this->post('edit/mailq', ['items' => [], 'attr' => ['action' => 'flush']]);
$this->assertCalled($flush, 'mailq', ['edit', ['qid' => [], 'action' => 'flush']]);
}
public function testAnActionOutsideOfAttrIsNotSeen(): void
{
$result = $this->post('edit/mailq', ['action' => 'flush']);
$this->assertCalled($result, 'mailq', ['edit', ['qid' => []]]);
$this->assertSame(['type' => 'success', 'msg' => 'Task completed'], $result->json());
}
public function testDeleteQueuedMessages(): void
{
$this->assertCalled($this->post('delete/mailq', ['B98C6260CA1', '4C1E8260D02']), 'mailq', ['delete', ['qid' => ['B98C6260CA1', '4C1E8260D02']]]);
$this->assertCalled($this->post('delete/mailq', '"B98C6260CA1"'), 'mailq', ['delete', ['qid' => ['B98C6260CA1']]]);
}
public function testDeleteTakesNoAction(): void
{
$result = $this->post('delete/mailq', ['action' => 'super_delete']);
$this->assertSame([['delete', ['qid' => ['action' => 'super_delete']]]], $result->callsTo('mailq'));
}
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Filtering;
use Mailcow\Tests\Api\ApiTestCase;
/** Quota warning mails and their per-domain BCC */
class QuotaNotificationTest extends ApiTestCase
{
public function testEditQuotaNotificationIgnoresItems(): void
{
$attr = ['sender' => 'quota-warning@example.org', 'subject' => 'Quota warning', 'html_tmpl' => ''];
$result = $this->post('edit/quota_notification', ['items' => ['quota_notification'], 'attr' => $attr]);
$this->assertSame([['edit', $attr]], $result->callsTo('quota_notification'));
}
public function testEditQuotaNotificationBccIgnoresItems(): void
{
$attr = ['domain' => 'example.org', 'bcc_rcpt' => "postmaster@example.org\nhelpdesk@example.org", 'active' => '1'];
$messages = [self::message('success', 'saved_settings')];
$result = $this->post('edit/quota_notification_bcc', ['items' => ['quota_bcc'], 'attr' => $attr], ['script' => [self::answer('quota_notification_bcc', ['edit'], null, $messages)]]);
$this->assertSame([['edit', $attr]], $result->callsTo('quota_notification_bcc'));
$this->assertSame($messages, $result->json());
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Filtering;
use Mailcow\Tests\Api\ApiTestCase;
/** Rspamd settings rules, global regex maps, action statistics and presets */
class RspamdTest extends ApiTestCase
{
private const RULE = ['desc' => 'Disable Rspamd for example.org', 'content' => 'priority = 10;', 'active' => '1'];
public function testAddRsetting(): void
{
$messages = [self::message('success', 'settings_map_added', ['rsettings', 'add', self::RULE])];
$result = $this->post('add/rsetting', self::RULE, ['script' => [self::answer('rsettings', ['add'], null, $messages)]]);
$this->assertCalled($result, 'rsettings', ['add', self::RULE]);
$this->assertSame($messages, $result->json());
}
public function testGetAllRsettingsReadsTheDetailsOfEveryRule(): void
{
$first = ['id' => 1, 'desc' => 'first', 'content' => 'priority = 10;', 'active' => 1];
$third = ['id' => 3, 'desc' => 'third', 'content' => 'priority = 5;', 'active' => 0];
$result = $this->get('get/rsetting/all', ['script' => [
self::answer('rsettings', ['get'], [['id' => 1, 'desc' => 'first', 'active' => 1], ['id' => 2, 'desc' => 'gone', 'active' => 1], ['id' => 3, 'desc' => 'third', 'active' => 0]]),
self::answer('rsettings', ['details', 1], $first),
self::answer('rsettings', ['details', 2], false),
self::answer('rsettings', ['details', 3], $third),
]]);
$this->assertSame([['get'], ['details', 1], ['details', 2], ['details', 3]], $result->callsTo('rsettings'));
$this->assertSame([$first, $third], $result->json());
}
public function testGetAllRsettingsWithoutRules(): void
{
$result = $this->get('get/rsetting/all', ['script' => [self::answer('rsettings', ['get'], false)]]);
$this->assertSame('{}', $result->body);
$this->assertSame([['get']], $result->callsTo('rsettings'));
}
public function testGetOneRsetting(): void
{
$rule = ['id' => 7, 'desc' => 'seven', 'content' => 'priority = 10;', 'active' => 1];
$found = $this->get('get/rsetting/7', ['script' => [self::answer('rsettings', ['details', '7'], $rule)]]);
$this->assertSame([['details', '7']], $found->callsTo('rsettings'));
$this->assertSame($rule, $found->json());
$this->assertSame('{}', $this->get('get/rsetting/8', ['script' => [self::answer('rsettings', ['details'], false)]])->body);
}
public function testEditRsetting(): void
{
$result = $this->post('edit/rsetting', ['items' => [1, 2], 'attr' => ['desc' => 'renamed', 'active' => '0']]);
$this->assertCalled($result, 'rsettings', ['edit', ['id' => [1, 2], 'desc' => 'renamed', 'active' => '0']]);
$single = $this->post('edit/rsetting', ['items' => '3', 'attr' => ['content' => 'priority = 1;']]);
$this->assertCalled($single, 'rsettings', ['edit', ['id' => ['3'], 'content' => 'priority = 1;']]);
}
public function testDeleteRsetting(): void
{
$this->assertCalled($this->post('delete/rsetting', [1, 2]), 'rsettings', ['delete', ['id' => [1, 2]]]);
$this->assertCalled($this->post('delete/rsetting', 3), 'rsettings', ['delete', ['id' => [3]]]);
}
public function testEditRspamdMap(): void
{
$data = "/^spammer@example\\.com$/i\n/@spam\\.example\\.net$/i";
$result = $this->post('edit/rspamd-map', ['items' => ['global_smtp_from_blacklist.map', 'global_rcpt_blacklist.map'], 'attr' => ['rspamd_map_data' => $data]]);
$this->assertCalled($result, 'rspamd_maps', ['edit', ['map' => ['global_smtp_from_blacklist.map', 'global_rcpt_blacklist.map'], 'rspamd_map_data' => $data]]);
}
public function testRspamdActions(): void
{
$actions = [['reject', 38], ['soft reject', 1], ['add header', 6], ['no action', 255]];
$result = $this->get('get/rspamd/actions', ['script' => [self::answer('rspamd_actions', [], $actions)]]);
$this->assertSame([[]], $result->callsTo('rspamd_actions'));
$this->assertSame($actions, $result->json());
$this->assertSame('{}', $this->get('get/rspamd/actions', ['script' => [self::answer('rspamd_actions', [], false)]])->body);
}
public function testOtherRspamdObjectsAnswerNothing(): void
{
$result = $this->get('get/rspamd/stat');
$this->assertSame([200, ''], [$result->status, $result->body]);
$this->assertNotCalled($result, 'rspamd_actions');
}
public function testPresets(): void
{
$preset = [['name' => 'preset_1', 'headline' => 'Disable all but DKIM', 'content' => 'priority = 10;']];
$rspamd = $this->get('get/presets/rspamd', ['script' => [self::answer('presets', ['get', 'rspamd'], $preset)]]);
$this->assertSame([['get', 'rspamd']], $rspamd->callsTo('presets'));
$this->assertSame($preset, $rspamd->json());
$sieve = $this->get('get/presets/sieve', ['session' => self::DOMAIN_ADMIN]);
$this->assertSame([['get', 'sieve']], $sieve->callsTo('presets'));
$this->assertSame('{}', $sieve->body);
$other = $this->get('get/presets/other');
$this->assertSame([200, ''], [$other->status, $other->body]);
$this->assertNotCalled($other, 'presets');
}
}
+266
View File
@@ -0,0 +1,266 @@
<?php
// Runs data/web/json_api.php for one request of ApiTestCase in a process of its own. The request comes from the JSON
// file in argv[1]; every legacy function and class json_api.php calls is a recorder; the status code, the output and
// the recorded calls go to the result file named in the request.
namespace ApiHarness {
/** The recorded calls and the scripted answers of one request */
final class Calls
{
/** @var list<array{0: string, 1: array}> */
public static array $calls = [];
/** @var list<array{call: string, args?: array, return?: mixed, messages?: list<array>, once?: bool}> */
public static array $script = [];
/** Methods that return a recorder unless scripted, so that chains like Module::fromGlobals()->api->x() work */
private const CHAINED = ['fromGlobals', 'create', 'prepare', 'query'];
public static function record(string $name, array $args, ?string $method = null): mixed
{
$args = self::plain($args);
self::$calls[] = [$name, $args];
foreach (self::$script as $i => $entry) {
if ($entry['call'] !== $name || !self::startsWith($args, $entry['args'] ?? [])) {
continue;
}
foreach ($entry['messages'] ?? [] as $message) {
$_SESSION['return'][] = $message;
}
if (!empty($entry['once'])) {
unset(self::$script[$i]);
}
return $entry['return'] ?? null;
}
return ($method !== null && in_array($method, self::CHAINED, true))
? new Recorder(preg_replace('/::(fromGlobals|create)$/', '', $name)) : null;
}
/** Arguments as JSON data: recorders and other objects become "@<name>" */
private static function plain(mixed $value): mixed
{
if ($value instanceof Recorder) {
return '@' . $value->label();
}
if (is_object($value)) {
return '@' . get_class($value);
}
return is_array($value) ? array_map([self::class, 'plain'], $value) : $value;
}
private static function startsWith(array $args, array $prefix): bool
{
foreach (array_values($prefix) as $i => $expected) {
if (!array_key_exists($i, $args) || $args[$i] !== $expected) {
return false;
}
}
return true;
}
}
/** Stand-in for objects and classes: records method calls, reads give further recorders */
class Recorder implements \Countable, \IteratorAggregate, \ArrayAccess, \JsonSerializable
{
private string $recorderLabel;
public function __construct(mixed ...$args)
{
$this->recorderLabel = (isset($args[0]) && is_string($args[0]) && static::class === self::class) ? $args[0] : static::class;
if (static::class !== self::class) {
Calls::record('new ' . static::class, $args);
}
}
public function label(): string
{
return $this->recorderLabel;
}
public function __get(string $name): self
{
return new self($this->recorderLabel . '->' . $name);
}
public function __call(string $name, array $args): mixed
{
return Calls::record($this->recorderLabel . '->' . $name, $args, $name);
}
public static function __callStatic(string $name, array $args): mixed
{
return Calls::record(static::class . '::' . $name, $args, $name);
}
public function count(): int
{
return 0;
}
public function getIterator(): \Iterator
{
return new \EmptyIterator();
}
public function offsetExists(mixed $offset): bool
{
return false;
}
public function offsetGet(mixed $offset): mixed
{
return null;
}
public function offsetSet(mixed $offset, mixed $value): void
{
}
public function offsetUnset(mixed $offset): void
{
}
public function jsonSerialize(): mixed
{
return ['@' => $this->recorderLabel];
}
}
/** php://input with the request body (the CLI has none); every other php:// stream is the real one */
final class InputStream
{
public static string $body = '';
/** @var resource|null */
public $context;
private int $position = 0;
/** @var resource|null */
private $inner = null;
public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool
{
if (strtolower($path) === 'php://input') {
return true;
}
stream_wrapper_restore('php');
$this->inner = fopen($path, $mode) ?: null;
stream_wrapper_unregister('php');
stream_wrapper_register('php', self::class);
return $this->inner !== null;
}
public function stream_read(int $count): string|false
{
if ($this->inner !== null) {
return fread($this->inner, $count);
}
$chunk = substr(self::$body, $this->position, $count);
$this->position += strlen($chunk);
return $chunk;
}
public function stream_write(string $data): int
{
return ($this->inner !== null) ? (int)fwrite($this->inner, $data) : 0;
}
public function stream_eof(): bool
{
return ($this->inner !== null) ? feof($this->inner) : $this->position >= strlen(self::$body);
}
public function stream_stat(): array|false
{
return ($this->inner !== null) ? fstat($this->inner) : ['size' => strlen(self::$body)];
}
public function stream_close(): void
{
if ($this->inner !== null) {
fclose($this->inner);
}
}
}
/** Names of the functions $file calls that are neither PHP functions nor declared in $file */
function calledFunctions(string $file): array
{
$tokens = array_values(array_filter(token_get_all(file_get_contents($file)),
fn($t) => !is_array($t) || !in_array($t[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)));
$declared = [];
$called = [];
foreach ($tokens as $i => $token) {
if (!is_array($token) || $token[0] !== T_STRING) {
continue;
}
$previous = $tokens[$i - 1] ?? null;
$next = $tokens[$i + 1] ?? null;
if (is_array($previous) && $previous[0] === T_FUNCTION) {
$declared[strtolower($token[1])] = true;
continue;
}
if ($next !== '(' || (is_array($previous) && in_array($previous[0], [T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR, T_DOUBLE_COLON, T_NEW], true))) {
continue;
}
$called[strtolower($token[1])] = $token[1];
}
return array_values(array_filter($called, fn(string $name): bool => !isset($declared[strtolower($name)]) && !function_exists($name)));
}
}
namespace {
use ApiHarness\Calls;
use ApiHarness\InputStream;
$request = json_decode(file_get_contents($argv[1]), true, 512, JSON_THROW_ON_ERROR);
$jsonApi = $request['json_api'];
foreach (ApiHarness\calledFunctions($jsonApi) as $name) {
eval('function ' . $name . '(...$args) { return \ApiHarness\Calls::record(' . var_export($name, true) . ', $args); }');
}
spl_autoload_register(function (string $class): void {
$parts = explode('\\', ltrim($class, '\\'));
$short = array_pop($parts);
$namespace = implode('\\', $parts);
eval(($namespace !== '' ? "namespace $namespace; " : '') . "class $short extends \\ApiHarness\\Recorder {}");
});
$_SERVER['DOCUMENT_ROOT'] = __DIR__ . '/webroot';
$_SERVER['REQUEST_METHOD'] = $request['method'];
$_SERVER['REQUEST_URI'] = '/api/v1/' . $request['query'];
$_SERVER['REMOTE_ADDR'] = '192.0.2.10';
if (isset($request['content_type'])) {
$_SERVER['HTTP_CONTENT_TYPE'] = $request['content_type'];
}
foreach ($request['server'] ?? [] as $key => $value) {
$_SERVER[$key] = $value;
}
$_GET = ['query' => $request['query']] + ($request['get'] ?? []);
$_POST = $request['post'] ?? [];
$GLOBALS['__harness_session'] = $request['session'] ?? [];
$GLOBALS['__harness_globals'] = $request['globals'] ?? [];
Calls::$script = $request['script'] ?? [];
InputStream::$body = (string)($request['body'] ?? '');
stream_wrapper_unregister('php');
stream_wrapper_register('php', InputStream::class);
register_shutdown_function(function () use ($request): void {
$output = '';
while (ob_get_level() > 0) {
$output = ob_get_clean() . $output;
}
$error = error_get_last();
$fatal = ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR], true));
stream_wrapper_restore('php');
file_put_contents($request['result'], json_encode([
'status' => http_response_code() ?: 200,
'body' => $output,
'calls' => Calls::$calls,
'session_return' => $_SESSION['return'] ?? null,
'error' => $fatal ? $error['message'] . ' in ' . $error['file'] . ':' . $error['line'] : null,
], JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE));
});
ob_start();
require $jsonApi;
}
@@ -0,0 +1,3 @@
<?php
// Stand-in for data/web/inc/lib/ssp.class.php in the API harness: the class SSP is a recorder (runner.php autoloader).
@@ -0,0 +1,11 @@
<?php
// Stand-in for data/web/inc/prerequisites.inc.php in the API harness (runner.php): the session of the request
// instead of the API key login, recorders for the database and Redis, the globals the test sets.
$pdo = new \ApiHarness\Recorder('pdo');
$redis = new \ApiHarness\Recorder('redis');
$_SESSION = $GLOBALS['__harness_session'] ?? [];
foreach ($GLOBALS['__harness_globals'] ?? [] as $name => $value) {
$GLOBALS[$name] = $value;
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiTestCase;
/** App passwords of mailboxes */
class AppPasswordRoutesTest extends ApiTestCase
{
public function testAddAppPassword(): void
{
$body = ['username' => 'john@example.org', 'app_name' => 'Thunderbird', 'app_passwd' => 'eiQu4ahz8iePh', 'app_passwd2' => 'eiQu4ahz8iePh', 'protocols' => ['imap_access']];
$this->assertCalled($this->post('add/app-passwd', $body), 'app_passwd', ['add', $body]);
}
public function testListsOfAMailboxAndOfTheSessionUser(): void
{
$script = [
self::answer('app_passwd', ['get'], [['id' => 23, 'name' => 'Thunderbird'], ['id' => 24, 'name' => 'Gone']]),
self::answer('app_passwd', ['details', 23], ['id' => 23, 'mailbox' => 'john@example.org']),
self::answer('app_passwd', ['details', 24], false),
];
$mailbox = $this->get('get/app-passwd/all/john@example.org', ['script' => $script]);
$this->assertSame([['get', ['username' => 'john@example.org']], ['details', 23], ['details', 24]], $mailbox->callsTo('app_passwd'));
$this->assertSame([['id' => 23, 'mailbox' => 'john@example.org']], $mailbox->json());
$own = $this->get('get/app-passwd/all', ['session' => self::USER, 'script' => $script]);
$this->assertSame(['get'], $own->callsTo('app_passwd')[0]);
$this->assertSame('{}', $this->get('get/app-passwd/all')->body);
}
public function testSingleAppPasswordFails(): void
{
$result = $this->get('get/app-passwd/23', ['allow_error' => true]);
$this->assertStringContainsString('Cannot access offset of type string on string', (string)$result->error);
$this->assertNotCalled($result, 'app_passwd');
}
public function testEditAndDeleteAppPasswords(): void
{
$edit = $this->post('edit/app-passwd', ['items' => [23], 'attr' => ['username' => 'john@example.org', 'active' => '0']]);
$this->assertCalled($edit, 'app_passwd', ['edit', ['id' => [23], 'username' => 'john@example.org', 'active' => '0']]);
$this->assertCalled($this->post('delete/app-passwd', [23, 24]), 'app_passwd', ['delete', ['id' => [23, 24]]]);
}
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiTestCase;
/** Sieve filters and the spam allow/deny lists of mailboxes */
class FilterAndPolicyRoutesTest extends ApiTestCase
{
public function testAddEditDeleteFilters(): void
{
$body = ['username' => 'john@example.org', 'script_desc' => 'Move newsletters', 'script_data' => 'keep;', 'filter_type' => 'prefilter', 'active' => 1];
$this->assertCalled($this->post('add/filter', $body), 'mailbox', ['add', 'filter', $body]);
$this->assertCalled($this->post('edit/filter', ['items' => [7], 'attr' => ['active' => 0]]), 'mailbox', ['edit', 'filter', ['id' => [7], 'active' => 0]]);
$this->assertCalled($this->post('delete/filter', [7]), 'mailbox', ['delete', 'filter', ['id' => [7]]]);
}
public function testAllFiltersOfAllMailboxes(): void
{
$result = $this->get('get/filters/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org']),
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['john@example.org', 'jane@example.org']),
self::answer('mailbox', ['get', 'filters', 'john@example.org'], [7, 8]),
self::answer('mailbox', ['get', 'filter_details', 7], ['id' => 7]),
self::answer('mailbox', ['get', 'filter_details', 8], false),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'filters', 'jane@example.org']);
$this->assertSame([['id' => 7]], $result->json());
$this->assertSame('{}', $this->get('get/filters/all')->body);
}
public function testFiltersOfAMailbox(): void
{
$result = $this->get('get/filters/john@example.org', ['script' => [
self::answer('mailbox', ['get', 'filters'], [7]),
self::answer('mailbox', ['get', 'filter_details', 7], ['id' => 7, 'username' => 'john@example.org']),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'filters', 'john@example.org']);
$this->assertSame([['id' => 7, 'username' => 'john@example.org']], $result->json());
$this->assertSame('{}', $this->get('get/filters/jane@example.org')->body);
}
public function testActiveUserSieveIsAListWithOneScript(): void
{
$result = $this->get('get/active-user-sieve/john@example.org', ['script' => [self::answer('mailbox', ['get', 'active_user_sieve'], "require \"fileinto\";\nkeep;")]]);
$this->assertSame([['get', 'active_user_sieve', 'john@example.org']], $result->callsTo('mailbox'));
$this->assertSame(["require \"fileinto\";\nkeep;"], $result->json());
$this->assertSame('{}', $this->get('get/active-user-sieve/john@example.org', ['script' => [self::answer('mailbox', [], false)]])->body);
$none = $this->get('get/active-user-sieve');
$this->assertNotCalled($none, 'mailbox');
$this->assertSame('{}', $none->body);
}
public function testAddAndDeleteMailboxPolicies(): void
{
$body = ['username' => 'john@example.org', 'object_list' => 'bl', 'object_from' => '*@spam.example.com'];
$this->assertCalled($this->post('add/mailbox-policy', $body), 'policy', ['add', 'mailbox', $body]);
$this->assertCalled($this->post('delete/mailbox-policy', [13]), 'policy', ['delete', 'mailbox', ['prefid' => [13]]]);
}
public function testAllowAndDenyListsArePartsOfThePolicy(): void
{
$policy = [
'whitelist' => [['object' => 'john@example.org', 'value' => '*@partner.example.com', 'prefid' => 12]],
'blacklist' => [['object' => 'example.org', 'value' => '*@spam.example.com', 'prefid' => 13]],
];
$script = [self::answer('policy', ['get', 'mailbox'], $policy)];
$allow = $this->get('get/policy_wl_mailbox/john@example.org', ['script' => $script]);
$this->assertSame([['get', 'mailbox', 'john@example.org']], $allow->callsTo('policy'));
$this->assertSame($policy['whitelist'], $allow->json());
$this->assertSame($policy['blacklist'], $this->get('get/policy_bl_mailbox/john@example.org', ['script' => $script])->json());
$this->assertSame('{}', $this->get('get/policy_bl_mailbox/nobody@example.org')->body);
}
}
@@ -0,0 +1,193 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiTestCase;
/** Mailboxes, their templates, tags, custom attributes, renaming, ACLs, logins and resets */
class MailboxRoutesTest extends ApiTestCase
{
public function testAddMailbox(): void
{
$body = ['local_part' => 'john.doe', 'domain' => 'example.org', 'password' => 'Moo2Shoh5aiXu', 'password2' => 'Moo2Shoh5aiXu', 'tags' => ['sales']];
$this->assertCalled($this->post('add/mailbox', $body), 'mailbox', ['add', 'mailbox', $body]);
// any other object is a mailbox as well
$this->assertCalled($this->post('add/mailbox/something', $body), 'mailbox', ['add', 'mailbox', $body]);
}
public function testAddMailboxTemplate(): void
{
$body = ['template' => 'Sales', 'quota' => 2048, 'acl' => ['spam_alias']];
$this->assertCalled($this->post('add/mailbox/template', $body), 'mailbox', ['add', 'mailbox_templates', $body]);
}
public function testGetAllMailboxesWalksAllDomains(): void
{
$result = $this->get('get/mailbox/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com']),
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['john@example.org', 'gone@example.org']),
self::answer('mailbox', ['get', 'mailboxes', 'example.com'], ['jane@example.com']),
self::answer('mailbox', ['get', 'mailbox_details', 'john@example.org'], ['username' => 'john@example.org']),
self::answer('mailbox', ['get', 'mailbox_details', 'gone@example.org'], false),
self::answer('mailbox', ['get', 'mailbox_details', 'jane@example.com'], ['username' => 'jane@example.com']),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'mailboxes', 'example.org', null]);
$this->assertCalled($result, 'mailbox', ['get', 'mailbox_details', 'john@example.org', 'all']);
$this->assertSame([['username' => 'john@example.org'], ['username' => 'jane@example.com']], $result->json());
}
public function testReducedDetailsOfGivenDomainsWithTags(): void
{
$result = $this->get('get/mailbox/reduced/example.org,example.com', [
'query' => ['tags' => 'sales,support'],
'script' => [
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['john@example.org']),
self::answer('mailbox', ['get', 'mailbox_details'], ['username' => 'john@example.org']),
],
]);
$this->assertNotContains(['get', 'domains'], $result->callsTo('mailbox'));
$this->assertCalled($result, 'mailbox', ['get', 'mailboxes', 'example.org', ['sales', 'support']]);
$this->assertCalled($result, 'mailbox', ['get', 'mailboxes', 'example.com', ['sales', 'support']]);
$this->assertCalled($result, 'mailbox', ['get', 'mailbox_details', 'john@example.org', 'reduced']);
$this->assertSame([['username' => 'john@example.org']], $result->json());
}
public function testMailboxListsWithoutResults(): void
{
$this->assertSame('{}', $this->get('get/mailbox/all')->body);
$this->assertSame('{}', $this->get('get/mailbox/all/example.org')->body);
}
public function testGetOneMailbox(): void
{
$found = $this->get('get/mailbox/john@example.org', ['script' => [
self::answer('mailbox', ['get', 'mailbox_details', 'john@example.org'], ['username' => 'john@example.org', 'quota' => 0]),
]]);
$this->assertCalled($found, 'mailbox', ['get', 'mailbox_details', 'john@example.org']);
$this->assertSame(['username' => 'john@example.org', 'quota' => 0], $found->json());
$this->assertSame('{}', $this->get('get/mailbox/nobody@example.org')->body);
}
public function testGetMailboxWithTagsSearchesByAddressPart(): void
{
$result = $this->get('get/mailbox/example.org', [
'query' => ['tags' => 'sales'],
'script' => [
self::answer('mailbox', ['get', 'mailboxes', 'example.org', ['sales']], ['john@example.org']),
self::answer('mailbox', ['get', 'mailbox_details', 'john@example.org'], ['username' => 'john@example.org']),
],
]);
$this->assertCalled($result, 'mailbox', ['get', 'mailbox_details', 'john@example.org']);
$this->assertSame([['username' => 'john@example.org']], $result->json());
$this->assertSame('[]', $this->get('get/mailbox/example.org', ['query' => ['tags' => 'sales']])->body);
}
public function testGetMailboxTemplates(): void
{
$templates = [['id' => 2, 'template' => 'Default', 'type' => 'mailbox', 'attributes' => ['quota' => 0]]];
$all = $this->get('get/mailbox/template/all', ['script' => [self::answer('mailbox', ['get', 'mailbox_templates'], $templates)]]);
$this->assertSame([['get', 'mailbox_templates']], $all->callsTo('mailbox'));
$this->assertSame($templates, $all->json());
$this->assertCalled($this->get('get/mailbox/template/2'), 'mailbox', ['get', 'mailbox_templates', '2']);
}
public function testEditMailbox(): void
{
$result = $this->post('edit/mailbox', ['items' => 'john@example.org', 'attr' => ['quota' => 5120, 'sender_acl' => ['@example.com']]]);
$this->assertCalled($result, 'mailbox', ['edit', 'mailbox', ['username' => ['john@example.org'], 'quota' => 5120, 'sender_acl' => ['@example.com']]]);
}
public function testEditMailboxTemplatesAndCustomAttributes(): void
{
$template = $this->post('edit/mailbox/template', ['items' => [3], 'attr' => ['template' => 'Sales', 'quarantine_category' => 'reject']]);
$this->assertCalled($template, 'mailbox', ['edit', 'mailbox_templates', ['ids' => [3], 'template' => 'Sales', 'quarantine_category' => 'reject']]);
$custom = $this->post('edit/mailbox/custom-attribute', ['items' => ['john@example.org'], 'attr' => ['attribute' => ['department'], 'value' => ['Sales']]]);
$this->assertCalled($custom, 'mailbox', ['edit', 'mailbox_custom_attribute', ['mailboxes' => ['john@example.org'], 'attribute' => ['department'], 'value' => ['Sales']]]);
}
public function testRenameMailboxTakesTheNamesFromAttr(): void
{
$attr = ['domain' => 'example.org', 'old_local_part' => 'john.doe', 'new_local_part' => 'john', 'create_alias' => 1];
$result = $this->post('edit/rename-mbox', ['items' => [], 'attr' => $attr]);
$this->assertCalled($result, 'mailbox', ['edit', 'mailbox_rename', ['mailbox' => []] + $attr]);
}
public function testEditUserAcl(): void
{
$result = $this->post('edit/user-acl', ['items' => ['john@example.org'], 'attr' => ['user_acl' => ['spam_alias', 'app_passwds']]]);
$this->assertCalled($result, 'acl', ['edit', 'user', ['username' => ['john@example.org'], 'user_acl' => ['spam_alias', 'app_passwds']]]);
}
public function testDeleteMailboxesTagsAndTemplates(): void
{
$this->assertCalled($this->post('delete/mailbox', ['john@example.org']), 'mailbox', ['delete', 'mailbox', ['username' => ['john@example.org']]]);
$tags = $this->post('delete/mailbox/tag/john@example.org', ['sales', 'trainee']);
$this->assertCalled($tags, 'mailbox', ['delete', 'tags_mailbox', ['tags' => ['sales', 'trainee'], 'username' => 'john@example.org']]);
$this->assertCalled($this->post('delete/mailbox/template', [3]), 'mailbox', ['delete', 'mailbox_templates', ['ids' => [3]]]);
}
public function testResetEasCacheAndSogoProfile(): void
{
$this->assertCalled($this->post('delete/eas_cache', ['john@example.org']), 'mailbox', ['delete', 'eas_cache', ['username' => ['john@example.org']]]);
$this->assertCalled($this->post('delete/sogo_profile', ['john@example.org']), 'mailbox', ['delete', 'sogo_profile', ['username' => ['john@example.org']]]);
}
public function testLastLogin(): void
{
$logins = ['ui' => false, 'sasl' => [['real_rip' => '198.51.100.23', 'service' => 'imap']]];
$default = $this->get('get/last-login/john@example.org', ['script' => [self::answer('last_login', [], $logins)]]);
$this->assertSame([['get', 'john@example.org']], $default->callsTo('last_login'));
$this->assertSame($logins, $default->json());
$this->assertSame([['get', 'john@example.org', 30]], $this->get('get/last-login/john@example.org/30')->callsTo('last_login'));
$this->assertSame([['get', 'john@example.org']], $this->get('get/last-login/john@example.org/0')->callsTo('last_login'));
$none = $this->get('get/last-login');
$this->assertSame('', $none->body);
$this->assertNotCalled($none, 'last_login');
}
public function testResetLastLoginIsAReadRoute(): void
{
$result = $this->get('get/reset-last-login/john@example.org', ['session' => self::API_RO, 'script' => [self::answer('last_login', ['reset'], true)]]);
$this->assertSame([['reset', 'john@example.org']], $result->callsTo('last_login'));
$this->assertSame('true', $result->body);
}
public function testBccDestinationOptions(): void
{
$result = $this->get('get/bcc-destination-options', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org']),
self::answer('mailbox', ['get', 'alias_domains'], ['example.net']),
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['john@example.org']),
self::answer('user_get_alias_details', ['john@example.org'], [
'direct_aliases' => ['sales@example.org' => ['public_comment' => ''], 'john@other.example' => ['public_comment' => '']],
'shared_aliases' => ['team@example.org' => ['public_comment' => '']],
]),
self::answer('hasDomainAccess', ['API', 'admin', 'example.org'], true),
]]);
$this->assertSame([
'domains' => ['example.org'],
'mailboxes' => ['john@example.org' => ['john@example.org', 'sales@example.org', 'team@example.org']],
'alias_domains' => ['example.net'],
], $result->json());
}
}
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiResult;
use Mailcow\Tests\Api\ApiTestCase;
/** search/mailbox: the DataTables server-side search of the mailbox table */
class MailboxSearchTest extends ApiTestCase
{
private const REQUEST = [
'draw' => 1,
'start' => 0,
'length' => 25,
'search' => ['value' => 'john'],
'order' => [['column' => 2, 'dir' => 'asc']],
'columns' => [['data' => 'username', 'searchable' => true, 'orderable' => true]],
];
private const KIND_FILTER = "(`m`.`kind` = '' OR `m`.`kind` = NULL)";
public function testAdminsSearchAllMailboxes(): void
{
$call = $this->searchCall($this->post('search/mailbox', self::REQUEST));
$this->assertSame([self::REQUEST, '@pdo', ['mailbox', 'm'], 'username'], array_slice($call, 0, 4));
$this->assertSame([null, self::KIND_FILTER], array_slice($call, 5));
$this->assertSame([2 => 'username', 3 => 'quota', 4 => 'last_mail_login', 5 => 'last_pw_change', 6 => 'in_use', 7 => 'name',
20 => 'messages', 23 => 'tags', 24 => 'active'], array_column($call[4], 'db', 'dt'));
}
public function testReadOnlyKeysMaySearch(): void
{
$this->searchCall($this->post('search/mailbox', self::REQUEST, ['session' => self::API_RO]));
}
public function testDomainAdminsSearchTheirDomains(): void
{
$call = $this->searchCall($this->post('search/mailbox', self::REQUEST, ['session' => self::DOMAIN_ADMIN]));
$this->assertSame('INNER JOIN domain_admins as da ON da.domain = m.domain', $call[5]);
$this->assertSame([
'condition' => self::KIND_FILTER . ' AND `da`.`active` = 1 AND `da`.`username` = :username',
'bindings' => ['username' => 'domainadmin'],
], $call[6]);
}
public function testOthersAreRefused(): void
{
foreach ([self::USER, self::NO_SESSION] as $session) {
$result = $this->post('search/mailbox', self::REQUEST, ['session' => $session]);
$this->assertApiError($result, 403, 'Insufficient permissions');
$this->assertNotCalled($result, 'SSP::complex');
}
}
public function testRowsBecomeMailboxDetails(): void
{
$page = ['draw' => 1, 'recordsTotal' => 2, 'recordsFiltered' => 2, 'data' => [
[2 => 'john@example.org', 3 => 0, 4 => '', 7 => 'John'],
[2 => 'gone@example.org', 3 => 0, 4 => '', 7 => 'Gone'],
]];
$result = $this->post('search/mailbox', self::REQUEST, ['script' => [
self::answer('SSP::complex', [], $page),
self::answer('mailbox', ['get', 'mailbox_details', 'john@example.org'], ['username' => 'john@example.org']),
]]);
$this->assertSame([['get', 'mailbox_details', 'john@example.org'], ['get', 'mailbox_details', 'gone@example.org']], $result->callsTo('mailbox'));
$this->assertSame(['draw' => 1, 'recordsTotal' => 2, 'recordsFiltered' => 2, 'data' => [['username' => 'john@example.org']]], $result->json());
}
public function testEmptyPageIsPassedOn(): void
{
$page = ['draw' => 4, 'recordsTotal' => 0, 'recordsFiltered' => 0, 'data' => []];
$result = $this->post('search/mailbox', self::REQUEST, ['script' => [self::answer('SSP::complex', [], $page)]]);
$this->assertNotCalled($result, 'mailbox');
$this->assertSame($page, $result->json());
}
private function searchCall(ApiResult $result): array
{
$calls = $result->callsTo('SSP::complex');
$this->assertCount(1, $calls, $result->body);
$this->assertNotCalled($result, 'SSP::simple');
return $calls[0];
}
}
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiTestCase;
/** Settings a mailbox user changes on its page: time-limited aliases, Pushover, tagged mail, TLS, quarantine, spam score */
class MailboxSettingsRoutesTest extends ApiTestCase
{
public function testTimeLimitedAliases(): void
{
$body = ['username' => 'john@example.org', 'domain' => 'example.org', 'validity' => 720];
$this->assertCalled($this->post('add/time_limited_alias', $body), 'mailbox', ['add', 'time_limited_alias', $body]);
$edit = $this->post('edit/time_limited_alias', ['items' => ['kahd.osuwe@example.org'], 'attr' => ['validity' => 24]]);
$this->assertCalled($edit, 'mailbox', ['edit', 'time_limited_alias', ['address' => ['kahd.osuwe@example.org'], 'validity' => 24]]);
$delete = $this->post('delete/time_limited_alias', ['kahd.osuwe@example.org']);
$this->assertCalled($delete, 'mailbox', ['delete', 'time_limited_alias', ['address' => ['kahd.osuwe@example.org']]]);
}
public function testListTimeLimitedAliases(): void
{
$aliases = [['address' => 'kahd.osuwe@example.org', 'goto' => 'john@example.org', 'permanent' => 0]];
$result = $this->get('get/time_limited_aliases/john@example.org', ['script' => [self::answer('mailbox', ['get', 'time_limited_aliases'], $aliases)]]);
$this->assertSame([['get', 'time_limited_aliases', 'john@example.org']], $result->callsTo('mailbox'));
$this->assertSame($aliases, $result->json());
$this->assertSame('{}', $this->get('get/time_limited_aliases/jane@example.org')->body);
}
public function testPushover(): void
{
$attr = ['key' => str_repeat('a', 30), 'token' => str_repeat('b', 30), 'active' => 1];
$this->assertCalled($this->post('edit/pushover', ['items' => ['john@example.org'], 'attr' => $attr]), 'pushover', ['edit', ['username' => ['john@example.org']] + $attr]);
$this->assertCalled($this->post('edit/pushover-test', ['items' => ['john@example.org'], 'attr' => []]), 'pushover', ['test', ['username' => ['john@example.org']]]);
}
public function testMailboxUserSettingsGetTheMailboxesAsUsername(): void
{
$routes = [
'delimiter_action' => ['tagged_mail_handler' => 'subfolder'],
'tls_policy' => ['tls_enforce_in' => 1],
'quarantine_notification' => ['quarantine_notification' => 'daily'],
'quarantine_category' => ['quarantine_category' => 'all'],
'spam_score' => ['spam_score' => '6,12'],
];
foreach ($routes as $type => $attr) {
$route = 'edit/' . ($type === 'spam_score' ? 'spam-score' : $type);
$result = $this->post($route, ['items' => 'john@example.org', 'attr' => $attr]);
$this->assertCalled($result, 'mailbox', ['edit', $type, ['username' => ['john@example.org']] + $attr]);
}
}
public function testTheUiSendsItsOwnAddressAsItem(): void
{
$result = $this->postForm('edit/delimiter_action', ['items' => '["user@example.org"]', 'attr' => '{"tagged_mail_handler":"subject","csrf_token":"t"}'], ['session' => self::USER]);
$this->assertCalled($result, 'mailbox', ['edit', 'delimiter_action', ['username' => ['user@example.org'], 'tagged_mail_handler' => 'subject']]);
}
public function testSpamScoreWithoutSpaces(): void
{
$mailbox = $this->get('get/spam-score/john@example.org', ['script' => [self::answer('mailbox', ['get', 'spam_score'], '6, 12')]]);
$this->assertSame([['get', 'spam_score', 'john@example.org']], $mailbox->callsTo('mailbox'));
$this->assertSame(['score' => '6,12'], $mailbox->json());
$default = $this->get('get/spam-score', ['script' => [self::answer('mailbox', ['get', 'spam_score'], '8, 15')]]);
$this->assertSame([['get', 'spam_score', null]], $default->callsTo('mailbox'));
$this->assertSame(['score' => '8,15'], $default->json());
$this->assertSame('{}', $this->get('get/spam-score/jane@example.org', ['script' => [self::answer('mailbox', [], false)]])->body);
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiTestCase;
/** Rate limits of mailboxes and the reset of rate limit counters */
class RatelimitRoutesTest extends ApiTestCase
{
public function testRatelimitsOfAllMailboxesAddTheMailbox(): void
{
$result = $this->get('get/rl-mbox/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org']),
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['john@example.org', 'jane@example.org']),
self::answer('ratelimit', ['get', 'mailbox', 'john@example.org'], ['value' => '100', 'frame' => 'h']),
self::answer('ratelimit', ['get', 'mailbox', 'jane@example.org'], false),
]]);
$this->assertSame([['value' => '100', 'frame' => 'h', 'mailbox' => 'john@example.org']], $result->json());
$this->assertSame('{}', $this->get('get/rl-mbox/all')->body);
}
public function testRatelimitOfAMailbox(): void
{
$result = $this->get('get/rl-mbox/john@example.org', ['script' => [self::answer('ratelimit', [], ['value' => '100', 'frame' => 'h'])]]);
$this->assertSame([['get', 'mailbox', 'john@example.org']], $result->callsTo('ratelimit'));
$this->assertSame(['value' => '100', 'frame' => 'h'], $result->json());
$this->assertSame('{}', $this->get('get/rl-mbox/jane@example.org')->body);
}
public function testEditRatelimit(): void
{
$result = $this->post('edit/rl-mbox', ['items' => ['john@example.org'], 'attr' => ['rl_value' => 100, 'rl_frame' => 'h']]);
$this->assertCalled($result, 'ratelimit', ['edit', 'mailbox', ['object' => ['john@example.org'], 'rl_value' => 100, 'rl_frame' => 'h']]);
}
public function testDeleteRatelimitHashAnswersTheRawResult(): void
{
$deleted = $this->post('delete/rlhash', ['RLabc123'], ['script' => [self::answer('ratelimit', [], true, [self::message('success', 'hash_deleted')])]]);
$this->assertSame([['delete', null, 'RLabc123']], $deleted->callsTo('ratelimit'));
$this->assertSame('1', $deleted->body);
$missing = $this->post('delete/rlhash', ['RLabc123'], ['script' => [self::answer('ratelimit', [], false, [self::message('warning', 'hash_not_found')])]]);
$this->assertSame('', $missing->body);
$this->assertSame([['delete', null, 'RLabcRLdef']], $this->post('delete/rlhash', ['RLabc', 'RLdef'])->callsTo('ratelimit'));
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Mailboxes;
use Mailcow\Tests\Api\ApiTestCase;
/** Resources (rooms, equipment, groups) */
class ResourceRoutesTest extends ApiTestCase
{
public function testAddResource(): void
{
$body = ['domain' => 'example.org', 'description' => 'Meeting Room 1', 'kind' => 'location', 'multiple_bookings' => 0, 'active' => 1];
$this->assertCalled($this->post('add/resource', $body), 'mailbox', ['add', 'resource', $body]);
}
public function testGetAllResourcesWalksAllDomains(): void
{
$result = $this->get('get/resource/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com']),
self::answer('mailbox', ['get', 'resources', 'example.org'], ['room1@example.org', 'gone@example.org']),
self::answer('mailbox', ['get', 'resource_details', 'room1@example.org'], ['name' => 'room1@example.org']),
self::answer('mailbox', ['get', 'resource_details', 'gone@example.org'], false),
]]);
$this->assertCalled($result, 'mailbox', ['get', 'resources', 'example.com']);
$this->assertSame([['name' => 'room1@example.org']], $result->json());
$this->assertSame('{}', $this->get('get/resource/all')->body);
}
public function testGetOneResource(): void
{
$result = $this->get('get/resource/room1@example.org', ['script' => [self::answer('mailbox', ['get', 'resource_details'], ['name' => 'room1@example.org'])]]);
$this->assertSame([['get', 'resource_details', 'room1@example.org']], $result->callsTo('mailbox'));
$this->assertSame(['name' => 'room1@example.org'], $result->json());
}
public function testEditAndDeleteResources(): void
{
$edit = $this->post('edit/resource', ['items' => ['room1@example.org'], 'attr' => ['multiple_bookings' => -1]]);
$this->assertCalled($edit, 'mailbox', ['edit', 'resource', ['name' => ['room1@example.org'], 'multiple_bookings' => -1]]);
$delete = $this->post('delete/resource', ['room1@example.org']);
$this->assertCalled($delete, 'mailbox', ['delete', 'resource', ['name' => ['room1@example.org']]]);
}
}
@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Modules;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes identity-provider, identity-provider-test, identity-provider-sync and identity-link */
class IdentityRoutesTest extends ApiTestCase
{
private const API = 'Mailcow\Identity\IdentityModule->api->';
/** The Identity Provider settings that prerequisites.inc.php loads into $iam_settings */
private const IAM_SETTINGS = ['iam_settings' => [
'authsource' => 'keycloak',
'server_url' => 'https://auth.example.org',
'realm' => 'mailcow',
'client_id' => 'mailcow',
'client_secret' => 'client-secret-value',
'bindpass' => 'bind-password',
'access_token' => 'cached-token',
'redirect_url_extra' => ['https://webmail.example.org'],
'login_provisioning' => true,
]];
private const SETTINGS = [
'authsource' => 'keycloak',
'server_url' => 'https://auth.example.org',
'realm' => 'mailcow',
'client_id' => 'mailcow',
'client_secret' => 'client-secret-value',
'bindpass' => 'bind-password',
'access_token' => 'cached-token',
'redirect_url_extra' => ['https://webmail.example.org'],
'login_provisioning' => true,
];
public function testSettingsForReadWriteKeys(): void
{
$result = $this->get('get/identity-provider', ['globals' => self::IAM_SETTINGS]);
$this->assertSame(self::SETTINGS, $result->json());
$this->assertNotCalled($result, self::API . 'getStatus');
}
public function testReadOnlyKeysGetNoSecrets(): void
{
$result = $this->get('get/identity-provider', ['globals' => self::IAM_SETTINGS, 'session' => self::API_RO]);
$this->assertSame(array_replace(self::SETTINGS, ['client_secret' => '*', 'bindpass' => '*', 'access_token' => '*']), $result->json());
}
public function testSettingsOnlyForAdmins(): void
{
foreach ([self::DOMAIN_ADMIN, self::USER] as $session) {
$this->assertSame('{}', $this->get('get/identity-provider', ['globals' => self::IAM_SETTINGS, 'session' => $session])->body);
}
}
public function testOtherObjectsAnswerTheSettingsToo(): void
{
$result = $this->get('get/identity-provider/anything', ['globals' => self::IAM_SETTINGS]);
$this->assertSame(self::SETTINGS, $result->json());
}
public function testSyncStatus(): void
{
$status = ['provider' => 'keycloak', 'can_sync' => true, 'sync_enabled' => false, 'counts' => ['accounts' => 2], 'runs' => []];
$result = $this->get('get/identity-provider/sync-status', ['session' => self::API_RO, 'script' => [self::answer(self::API . 'getStatus', [], $status)]]);
$this->assertCalled($result, self::API . 'getStatus', []);
$this->assertSame($status, $result->json());
}
public function testLink(): void
{
$link = ['provider' => 'keycloak', 'external_id' => '8d5da28d-7541-428b-8356-a9704f73e7f4', 'idp_disabled' => 0, 'missing_since' => null];
$result = $this->get('get/identity-link/alice@example.org', ['script' => [self::answer(self::API . 'getLink', ['alice@example.org'], $link)]]);
$this->assertCalled($result, self::API . 'getLink', ['alice@example.org']);
$this->assertSame($link, $result->json());
$none = $this->get('get/identity-link');
$this->assertCalled($none, self::API . 'getLink', ['']);
$this->assertSame('{}', $none->body);
}
public function testEditSettings(): void
{
$attr = ['authsource' => 'ldap', 'host' => 'ldap.example.org', 'basedn' => 'dc=example,dc=org', 'binddn' => 'cn=mailcow', 'bindpass' => 'secret'];
$messages = [self::message('success', ['object_modified', ''], ['identity_provider', 'edit', ['bindpass' => '*']])];
$result = $this->post('edit/identity-provider', ['items' => ['identity-provider'], 'attr' => $attr],
['script' => [self::answer('identity_provider', ['edit'], true, $messages)]]);
$this->assertCalled($result, 'identity_provider', ['edit', $attr]);
$this->assertSame($messages, $result->json());
}
public function testConnectionTestAnswersWithoutDetails(): void
{
$attr = ['authsource' => 'keycloak', 'server_url' => 'https://auth.example.org', 'realm' => 'mailcow', 'client_id' => 'mailcow', 'client_secret' => 'secret'];
$ok = $this->post('edit/identity-provider-test', ['attr' => $attr], ['script' => [self::answer('identity_provider', ['test'], true)]]);
$this->assertCalled($ok, 'identity_provider', ['test', $attr]);
$this->assertSame(['type' => 'success', 'msg' => 'Task completed'], $ok->json());
$failed = $this->post('edit/identity-provider-test', ['attr' => $attr], ['script' => [self::answer('identity_provider', ['test'], false)]]);
$this->assertSame(['type' => 'error', 'msg' => 'Cannot edit item'], $failed->json());
}
public function testRequestSync(): void
{
$result = $this->post('edit/identity-provider-sync', ['items' => ['sync'], 'attr' => ['mode' => 'preview']]);
$this->assertCalled($result, self::API . 'requestRun', [['mode' => 'preview']]);
}
public function testDeleteSettingsIgnoresTheBody(): void
{
$result = $this->post('delete/identity-provider', ['anything']);
$this->assertSame([['delete']], $result->callsTo('identity_provider'));
}
public function testDeleteLinks(): void
{
$messages = [self::message('success', ['iam_link_removed', 'alice@example.org'], ['identity_provider', 'delete', 'link', ['alice@example.org']])];
$result = $this->post('delete/identity-link', ['alice@example.org'], ['script' => [self::answer(self::API . 'unlink', [], true, $messages)]]);
$this->assertCalled($result, self::API . 'unlink', [['alice@example.org']]);
$this->assertSame($messages, $result->json());
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Modules;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes mailbox-group: json_api.php hands them to the MailboxGroup module and passes its answers through */
class MailboxGroupRoutesTest extends ApiTestCase
{
private const API = 'Mailcow\MailboxGroup\MailboxGroupModule->api->';
public function testAdd(): void
{
$attr = ['domain' => 'example.org', 'name' => 'Sales', 'members' => ['alice@example.org'], 'sender_allowed' => 1];
$messages = [
self::message('success', ['group_added', 'Sales'], ['mailbox_group', 'add', 'group', $attr]),
self::message('warning', ['group_imap_grant_failed', 'shared@example.org'], ['mailbox_group', 'add', 'group', $attr]),
];
$result = $this->post('add/mailbox-group', $attr, ['script' => [self::answer(self::API . 'addGroup', [], true, $messages)]]);
$this->assertCalled($result, self::API . 'addGroup', [$attr]);
$this->assertSame($messages, $result->json());
}
public function testGetAll(): void
{
$groups = [['id' => 7, 'domain' => 'example.org', 'name' => 'Sales', 'can_edit' => true]];
$result = $this->get('get/mailbox-group/all', ['script' => [self::answer(self::API . 'getGroups', [], $groups)]]);
$this->assertCalled($result, self::API . 'getGroups', [null]);
$this->assertSame($groups, $result->json());
}
public function testGetAllOfDomains(): void
{
$result = $this->get('get/mailbox-group/all/example.org,example.com');
$this->assertCalled($result, self::API . 'getGroups', ['example.org,example.com']);
}
public function testNoGroupsAnswerAnEmptyObject(): void
{
$result = $this->get('get/mailbox-group/all', ['script' => [self::answer(self::API . 'getGroups', [], [])]]);
$this->assertSame('{}', $result->body);
}
public function testGetMemberships(): void
{
$memberships = ['groups' => [['id' => 7, 'name' => 'Sales', 'source' => 'manual']], 'template' => 'Staff', 'send_as' => ['sales@example.org']];
$result = $this->get('get/mailbox-group/member/alice@example.org', ['script' => [self::answer(self::API . 'getMemberships', [], $memberships)]]);
$this->assertCalled($result, self::API . 'getMemberships', ['alice@example.org']);
$this->assertSame($memberships, $result->json());
$this->assertNotCalled($result, self::API . 'getGroup');
}
public function testGetMembershipsWithoutMailbox(): void
{
$result = $this->get('get/mailbox-group/member');
$this->assertCalled($result, self::API . 'getMemberships', ['']);
$this->assertSame('{}', $result->body);
}
public function testGetOne(): void
{
$group = ['id' => 7, 'domain' => 'example.org', 'name' => 'Sales'];
$result = $this->get('get/mailbox-group/7', ['script' => [self::answer(self::API . 'getGroup', ['7'], $group)]]);
$this->assertCalled($result, self::API . 'getGroup', ['7']);
$this->assertSame($group, $result->json());
}
public function testEdit(): void
{
$attr = ['add_members' => ['carol@example.org'], 'remove_members' => 'bob@example.org'];
$result = $this->post('edit/mailbox-group', ['items' => [7, '8'], 'attr' => $attr]);
$this->assertCalled($result, self::API . 'editGroups', [[7, '8'], $attr]);
}
public function testEditWithASingleIdOrWithoutItems(): void
{
$single = $this->post('edit/mailbox-group', ['items' => 7, 'attr' => ['active' => 0]]);
$this->assertCalled($single, self::API . 'editGroups', [[7], ['active' => 0]]);
$none = $this->post('edit/mailbox-group', ['attr' => ['id' => [9], 'active' => 0]]);
$this->assertCalled($none, self::API . 'editGroups', [[], ['id' => [9], 'active' => 0]]);
}
public function testDelete(): void
{
$messages = [
self::message('success', ['group_removed', 'Sales'], ['mailbox_group', 'delete', 'group', ['id' => [7, 'x']]]),
self::message('danger', 'access_denied', ['mailbox_group', 'delete', 'group', ['id' => [7, 'x']]]),
];
$result = $this->post('delete/mailbox-group', [7, 'x'], ['script' => [self::answer(self::API . 'deleteGroups', [], true, $messages)]]);
$this->assertCalled($result, self::API . 'deleteGroups', [[7, 'x']]);
$this->assertSame($messages, $result->json());
}
}
+194
View File
@@ -0,0 +1,194 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Modules;
use Mailcow\Tests\Api\ApiTestCase;
/** Routes syncjob, syncjobs, syncjob_source, syncjob_options and imapsync_settings: handed to the Imapsync module */
class SyncJobRoutesTest extends ApiTestCase
{
private const API = 'Mailcow\Imapsync\ImapsyncModule->api->';
public function testAddJob(): void
{
$attr = ['username' => 'alice@example.org', 'source_id' => 3, 'user1' => 'alice@example.com', 'password1' => 'secret', 'mins_interval' => 20];
$messages = [self::message('success', ['mailbox_modified', 'alice@example.org'], ['syncjob', 'add', 'job', ['password1' => '*']])];
$result = $this->post('add/syncjob', $attr, ['script' => [self::answer(self::API . 'addJob', [], true, $messages)]]);
$this->assertCalled($result, self::API . 'addJob', [$attr]);
$this->assertSame($messages, $result->json());
}
public function testAddSource(): void
{
$attr = ['name' => 'Old provider', 'host' => 'imap.example.com', 'port' => 993, 'enc' => 'SSL', 'scope' => 'domain', 'domains' => ['example.org']];
$result = $this->post('add/syncjob_source', $attr);
$this->assertCalled($result, self::API . 'addSource', [$attr]);
}
public function testAllJobsOfAnApiKey(): void
{
$jobs = [['id' => 12, 'user2' => 'alice@example.org', 'log' => 'done']];
$result = $this->get('get/syncjobs/all', ['script' => [self::answer(self::API . 'getAllJobs', [], $jobs)]]);
$this->assertCalled($result, self::API . 'getAllJobs', [null, []]);
$this->assertNotCalled($result, 'mailbox');
$this->assertSame($jobs, $result->json());
}
public function testAllJobsWithoutLog(): void
{
$result = $this->get('get/syncjobs/all/no_log');
$this->assertCalled($result, self::API . 'getAllJobs', ['no_log', []]);
$this->assertSame('{}', $result->body);
}
public function testAllJobsOfADomainAdminAreTheJobsOfHisMailboxes(): void
{
$result = $this->get('get/syncjobs/all/no_log', ['session' => self::DOMAIN_ADMIN, 'script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com']),
self::answer('mailbox', ['get', 'mailboxes', 'example.org'], ['alice@example.org', 'bob@example.org']),
self::answer('mailbox', ['get', 'mailboxes', 'example.com'], false),
]]);
$this->assertCalled($result, self::API . 'getAllJobs', ['no_log', ['alice@example.org', 'bob@example.org']]);
}
public function testAllJobsOfAUser(): void
{
$result = $this->get('get/syncjobs/all', ['session' => self::USER, 'script' => [self::answer('mailbox', ['get', 'domains'], false)]]);
$this->assertCalled($result, self::API . 'getAllJobs', [null, []]);
}
public function testJobsOfAMailbox(): void
{
$withLog = $this->get('get/syncjobs/alice@example.org');
$this->assertCalled($withLog, self::API . 'getJobs', ['alice@example.org', null]);
$this->assertNotCalled($withLog, self::API . 'getAllJobs');
$withoutLog = $this->get('get/syncjobs/alice@example.org/no_log');
$this->assertCalled($withoutLog, self::API . 'getJobs', ['alice@example.org', 'no_log']);
$own = $this->get('get/syncjobs');
$this->assertCalled($own, self::API . 'getJobs', [null, null]);
}
public function testSources(): void
{
$sources = [['id' => 3, 'name' => 'Old provider', 'has_client_secret' => false]];
$all = $this->get('get/syncjob_source/all', ['script' => [self::answer(self::API . 'getSources', [], $sources)]]);
$this->assertCalled($all, self::API . 'getSources', []);
$this->assertSame($sources, $all->json());
$one = $this->get('get/syncjob_source/3', ['script' => [self::answer(self::API . 'getSource', ['3'], $sources[0])]]);
$this->assertCalled($one, self::API . 'getSource', ['3']);
$this->assertSame($sources[0], $one->json());
$this->assertSame('{}', $this->get('get/syncjob_source/99')->body);
}
public function testOptions(): void
{
$options = ['folder' => true, 'syncinternaldates' => false, 'sslargs1' => '^SSL_verify_mode=[01]$'];
$result = $this->get('get/syncjob_options', ['session' => self::USER, 'script' => [self::answer(self::API . 'getOptions', [], $options)]]);
$this->assertCalled($result, self::API . 'getOptions', []);
$this->assertSame($options, $result->json());
}
public function testSettings(): void
{
$settings = ['max_parallel' => 3, 'max_bytes_per_second' => 2048000, 'max_kb_per_second' => 2000];
$result = $this->get('get/imapsync_settings', ['script' => [self::answer(self::API . 'getSettings', [], $settings)]]);
$this->assertCalled($result, self::API . 'getSettings', []);
$this->assertSame($settings, $result->json());
$this->assertSame('{}', $this->get('get/imapsync_settings', ['session' => self::DOMAIN_ADMIN, 'script' => [self::answer(self::API . 'getSettings', [], [])]])->body);
}
public function testEditJobs(): void
{
$result = $this->post('edit/syncjob', ['items' => [12, '13'], 'attr' => ['active' => 0, 'mins_interval' => 60]]);
$this->assertCalled($result, self::API . 'editJobs', [[12, '13'], ['active' => 0, 'mins_interval' => 60]]);
$this->assertNotCalled($result, self::API . 'runNext');
$other = $this->post('edit/syncjob/anything', ['items' => [12], 'attr' => ['active' => 1]]);
$this->assertCalled($other, self::API . 'editJobs', [[12], ['active' => 1]]);
}
public function testRunNext(): void
{
$messages = [
self::message('danger', ['imapsync_job_running', 13], ['syncjob', 'edit', 'run_next', ['id' => [12, 13]]]),
self::message('success', ['imapsync_run_next_done', 1], ['syncjob', 'edit', 'run_next', ['id' => [12, 13]]]),
];
$result = $this->post('edit/syncjob/run_next', ['items' => [12, 13], 'attr' => ['ignored' => 1]],
['script' => [self::answer(self::API . 'runNext', [], true, $messages)]]);
$this->assertCalled($result, self::API . 'runNext', [[12, 13]]);
$this->assertNotCalled($result, self::API . 'editJobs');
$this->assertSame($messages, $result->json());
}
public function testRunNextWithoutMovedJobs(): void
{
$result = $this->post('edit/syncjob/run_next', ['items' => [99]], ['script' => [self::answer(self::API . 'runNext', [], false)]]);
$this->assertSame(['type' => 'error', 'msg' => 'Cannot edit item'], $result->json());
}
public function testEditSettings(): void
{
$result = $this->post('edit/imapsync_settings', ['items' => ['ignored'], 'attr' => ['max_parallel' => 4, 'max_kb_per_second' => 2000]]);
$this->assertCalled($result, self::API . 'editSettings', [['max_parallel' => 4, 'max_kb_per_second' => 2000]]);
}
public function testEditSources(): void
{
$attr = ['description' => 'Migration finished', 'scope' => 'user', 'users' => ['alice@example.org']];
$result = $this->post('edit/syncjob_source', ['items' => [3], 'attr' => $attr]);
$this->assertCalled($result, self::API . 'editSources', [[3], $attr]);
$this->assertNotCalled($result, self::API . 'refreshSourceToken');
}
public function testRefreshToken(): void
{
$result = $this->post('edit/syncjob_source/refresh_token', ['items' => [5, 6], 'attr' => []]);
$this->assertCalled($result, self::API . 'refreshSourceToken', [[5, 6]]);
$this->assertNotCalled($result, self::API . 'editSources');
}
public function testDeleteJobs(): void
{
$result = $this->post('delete/syncjob', [12, '13']);
$this->assertCalled($result, self::API . 'deleteJobs', [[12, '13']]);
}
public function testDeleteSources(): void
{
$messages = [self::message('danger', ['imapsync_source_in_use', 'Old provider'], ['syncjob', 'delete', 'source', ['id' => [3]]])];
$result = $this->post('delete/syncjob_source', [3], ['script' => [self::answer(self::API . 'deleteSources', [], true, $messages)]]);
$this->assertCalled($result, self::API . 'deleteSources', [[3]]);
$this->assertSame($messages, $result->json());
}
}
+155
View File
@@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api;
use Symfony\Component\Yaml\Yaml;
/** data/web/api/openapi.yaml against json_api.php: every route documented, every documented route real, the spec consistent */
class OpenApiSpecTest extends ApiTestCase
{
private const SPEC = __DIR__ . '/../../../data/web/api/openapi.yaml';
private const JSON_API = __DIR__ . '/../../../data/web/json_api.php';
/** Routes of the mailcow UI that an API client cannot use, with the reason */
private const UNDOCUMENTED = [
// WebAuthn ceremonies of the browser: the challenge lives in the UI session
'add/fido2-registration' => 'WebAuthn',
'add/webauthn-tfa-registration' => 'WebAuthn',
'get/fido2-registration' => 'WebAuthn',
'get/fido2-get-args' => 'WebAuthn',
'get/webauthn-tfa-registration' => 'WebAuthn',
'get/webauthn-tfa-get-args' => 'WebAuthn',
// the own account of a UI session (domain admin, mailbox user): an API key gets an empty answer
'edit/self' => 'own account',
// renames a FIDO2 key of the logged-in account, the API account has none
'edit/fido2-fn' => 'own account',
];
private static ?array $spec = null;
private static function spec(): array
{
return self::$spec ??= Yaml::parseFile(self::SPEC);
}
/** @return list<array{0: string, 1: string, 2: array}> path, method, operation */
private static function operations(): array
{
$operations = [];
foreach (self::spec()['paths'] as $path => $item) {
foreach ($item as $method => $operation) {
if (in_array($method, ['get', 'post', 'put', 'patch', 'delete'], true)) {
$operations[] = [$path, $method, $operation];
}
}
}
return $operations;
}
public function testEveryRouteIsDocumented(): void
{
$paths = array_keys(self::spec()['paths']);
$missing = [];
foreach (RouteMap::fromFile(self::JSON_API) as $route) {
$covered = isset(self::UNDOCUMENTED[$route]) || !empty(array_filter($paths, fn(string $path): bool => RouteMap::covers($path, $route)));
if (!$covered) {
$missing[] = $route;
}
}
$this->assertSame([], $missing, 'routes of json_api.php missing in openapi.yaml');
}
public function testTheUndocumentedRoutesExist(): void
{
$this->assertSame([], array_values(array_diff(array_keys(self::UNDOCUMENTED), RouteMap::fromFile(self::JSON_API))));
}
public function testEveryDocumentedRouteExists(): void
{
$unknown = [];
foreach (self::operations() as [$path, $method, $operation]) {
$route = preg_replace_callback('/\{([^}]+)\}/', fn(array $m): string => self::exampleOf($operation, $m[1]), $path);
$action = explode('/', trim(preg_replace('#^/api/v1/#', '', $path), '/'))[0];
$body = match ($action) {
'edit' => ['items' => [], 'attr' => (object)[]],
'delete' => [],
default => (object)[],
};
$result = ($method === 'get') ? $this->get($route, ['allow_error' => true]) : $this->post($route, $body, ['allow_error' => true]);
$answer = $result->json();
if ($result->status === 404 && is_array($answer) && ($answer['type'] ?? '') === 'error') {
$unknown[] = strtoupper($method) . ' ' . $path . ': ' . ($answer['msg'] ?? '');
}
}
$this->assertSame([], $unknown, 'documented routes that json_api.php does not know');
}
public function testMethodsFollowTheAction(): void
{
foreach (self::operations() as [$path, $method]) {
$action = explode('/', trim(preg_replace('#^/api/v1/#', '', $path), '/'))[0];
$this->assertSame(($action === 'get') ? 'get' : 'post', $method, $path);
}
}
public function testOperationsAreComplete(): void
{
$tags = array_column(self::spec()['tags'], 'name');
$ids = [];
foreach (self::operations() as [$path, $method, $operation]) {
$where = strtoupper($method) . ' ' . $path;
$this->assertNotEmpty($operation['summary'] ?? null, "$where: summary");
$this->assertNotEmpty($operation['operationId'] ?? null, "$where: operationId");
$ids[] = $operation['operationId'];
$this->assertNotEmpty($operation['tags'] ?? [], "$where: tags");
$this->assertSame([], array_values(array_diff($operation['tags'] ?? [], $tags)), "$where: tags not in the tag list");
$this->assertArrayHasKey('200', $operation['responses'] ?? [], "$where: 200 response");
$this->assertArrayHasKey('401', $operation['responses'] ?? [], "$where: 401 response");
preg_match_all('/\{([^}]+)\}/', $path, $placeholders);
$declared = array_column(array_filter($operation['parameters'] ?? [], fn(array $p): bool => ($p['in'] ?? '') === 'path'), 'name');
sort($declared);
$expected = $placeholders[1];
sort($expected);
$this->assertSame($expected, $declared, "$where: path parameters");
}
$this->assertSame(array_values(array_unique($ids)), $ids, 'operationIds must be unique');
}
public function testEveryTagIsUsed(): void
{
$used = array_merge(...array_map(fn(array $op): array => $op[2]['tags'] ?? [], self::operations()));
$this->assertSame([], array_values(array_diff(array_column(self::spec()['tags'], 'name'), $used)));
}
public function testReferencesResolve(): void
{
$refs = [];
$spec = self::spec();
array_walk_recursive($spec, function (mixed $value, mixed $key) use (&$refs): void {
if ($key === '$ref') {
$refs[] = $value;
}
});
foreach (array_unique($refs) as $ref) {
$node = self::spec();
foreach (explode('/', substr($ref, 2)) as $part) {
$this->assertIsArray($node, $ref);
$this->assertArrayHasKey(str_replace(['~1', '~0'], ['/', '~'], $part), $node, "unresolved $ref");
$node = $node[str_replace(['~1', '~0'], ['/', '~'], $part)];
}
}
}
private static function exampleOf(array $operation, string $name): string
{
foreach ($operation['parameters'] ?? [] as $parameter) {
if (($parameter['name'] ?? null) === $name) {
$example = $parameter['example'] ?? $parameter['schema']['example'] ?? $parameter['schema']['enum'][0] ?? 'test';
return rawurlencode((string)$example);
}
}
return 'test';
}
}
+131
View File
@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api;
/** What json_api.php does for every route: methods, read-only keys, request bodies, answers, unknown routes */
class RequestHandlingTest extends ApiTestCase
{
public function testWritesNeedPost(): void
{
foreach (['add/alias', 'edit/alias', 'delete/alias'] as $route) {
$result = $this->request('GET', $route, ['post' => ['attr' => '{}', 'items' => '[]']]);
$this->assertApiError($result, 405, 'only POST method is allowed');
$this->assertNotCalled($result, 'mailbox');
}
}
public function testReadsNeedGet(): void
{
$this->assertApiError($this->post('get/domain/all'), 405, 'only GET method is allowed');
}
public function testSearchNeedsPost(): void
{
$this->assertApiError($this->get('search/domain'), 405, 'only POST method is allowed');
}
public function testReadOnlyKeysCannotWrite(): void
{
foreach (['add/alias' => ['address' => 'a@example.org'], 'edit/alias' => ['items' => [1], 'attr' => []], 'delete/alias' => [1]] as $route => $body) {
$result = $this->post($route, $body, ['session' => self::API_RO]);
$this->assertApiError($result, 403, 'API read/write access denied');
$this->assertNotCalled($result, 'mailbox');
}
}
public function testReadOnlyKeysCanRead(): void
{
$result = $this->get('get/alias/1', ['session' => self::API_RO]);
$this->assertSame(200, $result->status);
$this->assertCalled($result, 'mailbox', ['get', 'alias_details', '1']);
}
public function testWritesNeedALogin(): void
{
$this->assertApiError($this->post('add/alias', ['address' => 'a@example.org'], ['session' => self::NO_SESSION]), 403, 'API read/write access denied');
}
public function testReadsWithoutLoginAnswerNothing(): void
{
// a wrong API key ends in sessions.inc.php with 401; without key and session the read routes are skipped
$result = $this->get('get/domain/all', ['session' => self::NO_SESSION]);
$this->assertSame([200, ''], [$result->status, $result->body]);
$this->assertNotCalled($result, 'mailbox');
}
public function testUnknownRoutes(): void
{
$this->assertApiError($this->get('get/no-such-thing'), 404, 'route not found');
$this->assertApiError($this->post('add/no-such-thing', ['x' => 1]), 404, 'route not found');
$this->assertApiError($this->post('edit/no-such-thing', ['items' => [], 'attr' => []]), 404, 'route not found');
$this->assertApiError($this->post('delete/no-such-thing', [1]), 404, 'route not found');
$this->assertApiError($this->get('nothing/at/all'), 404, 'route not found');
}
public function testInvalidJsonBody(): void
{
$this->assertApiError($this->post('add/alias', '{"address": '), 400, 'Request body doesn\'t contain valid json!');
}
public function testJsonBodies(): void
{
// add: the body is attr
$add = $this->post('add/alias', ['address' => 'a@example.org', 'goto' => 'b@example.org']);
$this->assertCalled($add, 'mailbox', ['add', 'alias', ['address' => 'a@example.org', 'goto' => 'b@example.org']]);
// edit: {"items": ..., "attr": ...}
$edit = $this->post('edit/alias', ['items' => [3, 4], 'attr' => ['active' => '0']]);
$this->assertCalled($edit, 'mailbox', ['edit', 'alias', ['id' => [3, 4], 'active' => '0']]);
// delete: the body is the list of items
$delete = $this->post('delete/alias', [3, 4]);
$this->assertCalled($delete, 'mailbox', ['delete', 'alias', ['id' => [3, 4]]]);
}
public function testFormBodiesOfTheUi(): void
{
$result = $this->postForm('edit/alias', ['items' => '[3]', 'attr' => '{"active":"1","csrf_token":"t"}'], ['session' => self::ADMIN]);
$this->assertCalled($result, 'mailbox', ['edit', 'alias', ['id' => [3], 'active' => '1']]);
}
public function testMissingAttributes(): void
{
$this->assertSame(['type' => 'error', 'msg' => 'Cannot find attributes in post data'], $this->request('POST', 'add/alias')->json());
$this->assertSame(['type' => 'error', 'msg' => 'Cannot find attributes in post data'], $this->request('POST', 'delete/alias')->json());
}
public function testWriteAnswers(): void
{
$messages = [self::message('success', ['alias_added', 'a@example.org'], ['mailbox', 'add', 'alias'])];
$done = $this->post('add/alias', ['address' => 'a@example.org'], ['script' => [self::answer('mailbox', ['add', 'alias'], true, $messages)]]);
$this->assertSame($messages, $done->json());
$silent = $this->post('add/alias', ['address' => 'a@example.org'], ['script' => [self::answer('mailbox', ['add', 'alias'], true)]]);
$this->assertSame(['type' => 'success', 'msg' => 'Task completed'], $silent->json());
$failed = $this->post('add/alias', ['address' => 'a@example.org'], ['script' => [self::answer('mailbox', ['add', 'alias'], false)]]);
$this->assertSame(['type' => 'error', 'msg' => 'Cannot add item'], $failed->json());
}
public function testReadAnswers(): void
{
$found = $this->get('get/alias/1', ['script' => [self::answer('mailbox', ['get', 'alias_details'], ['id' => 1, 'address' => 'a@example.org'])]]);
$this->assertSame(['id' => 1, 'address' => 'a@example.org'], $found->json());
$this->assertSame('{}', $this->get('get/alias/1')->body);
}
public function testRequestsFromABrowserPageAreRefused(): void
{
$result = $this->get('get/domain/all', ['server' => ['HTTP_SEC_FETCH_DEST' => 'document']]);
$this->assertSame('', $result->body);
$this->assertSame(['cors'], $result->callNames());
}
}
+115
View File
@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api;
/**
* The routes json_api.php answers, read from its nested switch statements over $action, $category, $object and
* $extra: "get/mailbox/all", "add/alias", "*" for a default case ("get/mailbox/*" = get/mailbox/<anything else>).
* Default cases that answer 404 are no routes. Only the most specific routes are listed (get/mailbox/all, not get/mailbox).
*/
final class RouteMap
{
private const ROUTE_VARS = ['$action', '$category', '$object', '$extra'];
/** @return list<string> */
public static function fromFile(string $file): array
{
$tokens = array_values(array_filter(token_get_all((string)file_get_contents($file)),
fn($t): bool => !is_array($t) || !in_array($t[0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT], true)));
$stack = [];
$depth = 0;
$pending = null;
$routes = [];
foreach ($tokens as $i => $token) {
$id = is_array($token) ? $token[0] : $token;
if ($id === T_SWITCH) {
$isVar = ($tokens[$i + 1] ?? null) === '(' && is_array($tokens[$i + 2] ?? null) && $tokens[$i + 2][0] === T_VARIABLE && ($tokens[$i + 3] ?? null) === ')';
$pending = ($isVar && in_array($tokens[$i + 2][1], self::ROUTE_VARS, true)) ? $tokens[$i + 2][1] : '';
} elseif ($id === '{' || $id === T_CURLY_OPEN || $id === T_DOLLAR_OPEN_CURLY_BRACES) {
$depth++;
if ($pending !== null) {
$stack[] = ['var' => $pending, 'depth' => $depth, 'label' => null];
$pending = null;
}
} elseif ($id === '}') {
if (!empty($stack) && end($stack)['depth'] === $depth) {
array_pop($stack);
}
$depth--;
} elseif (($id === T_CASE || $id === T_DEFAULT) && !empty($stack) && end($stack)['depth'] === $depth) {
$label = ($id === T_DEFAULT) ? '*' : self::literal($tokens[$i + 1] ?? null);
$stack[count($stack) - 1]['label'] = $label;
if (end($stack)['var'] === '' || $label === null || ($id === T_DEFAULT && self::answers404($tokens, $i))) {
continue;
}
$route = [];
foreach ($stack as $switch) {
if ($switch['var'] !== '') {
$route[] = $switch['label'];
}
}
$routes[implode('/', $route)] = true;
}
}
$routes = array_keys($routes);
return array_values(array_filter($routes, function (string $route) use ($routes): bool {
foreach ($routes as $other) {
if (str_starts_with($other, $route . '/')) {
return false;
}
}
return true;
}));
}
/**
* Whether $documented (an OpenAPI path like /api/v1/get/mailbox/{id}) covers $route: same leading segments, a
* {parameter} matches anything, trailing "*" of the route are optional (add/mailbox/* = add/mailbox)
*/
public static function covers(string $documented, string $route): bool
{
$doc = explode('/', trim(preg_replace('#^/?api/v1/#', '', $documented), '/'));
$segments = explode('/', $route);
while (count($segments) > count($doc) && end($segments) === '*') {
array_pop($segments);
}
if (count($segments) > count($doc)) {
return false;
}
foreach ($segments as $i => $segment) {
$isParam = (bool)preg_match('/^\{.+\}$/', $doc[$i]);
if ($segment === '*' ? !$isParam : ($segment !== $doc[$i] && !$isParam)) {
return false;
}
}
return true;
}
private static function literal(mixed $token): ?string
{
return (is_array($token) && $token[0] === T_CONSTANT_ENCAPSED_STRING) ? substr($token[1], 1, -1) : null;
}
/** A default case whose code (up to the next case of its switch) sets the status 404 */
private static function answers404(array $tokens, int $start): bool
{
$level = 0;
for ($i = $start + 1; $i < count($tokens); $i++) {
$id = is_array($tokens[$i]) ? $tokens[$i][0] : $tokens[$i];
if ($id === '{' || $id === T_CURLY_OPEN || $id === T_DOLLAR_OPEN_CURLY_BRACES) {
$level++;
} elseif ($id === '}') {
if ($level-- === 0) {
return false;
}
} elseif ($level === 0 && ($id === T_CASE || $id === T_DEFAULT)) {
return false;
} elseif ($id === T_STRING && $tokens[$i][1] === 'http_response_code' && is_array($tokens[$i + 2] ?? null) && $tokens[$i + 2][1] === '404') {
return true;
}
}
return false;
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Routing;
use Mailcow\Tests\Api\ApiTestCase;
/** add/edit/delete/get alias */
class AliasRoutesTest extends ApiTestCase
{
private const SALES = ['id' => 12, 'address' => 'sales@example.org', 'goto' => 'anna@example.org,ben@example.org', 'active' => 1];
private const INFO = ['id' => 13, 'address' => 'info@example.org', 'goto' => 'anna@example.org', 'active' => 1];
private const SHOP = ['id' => 20, 'address' => 'shop@example.net', 'goto' => 'ben@example.org', 'active' => 0];
public function testAddPassesTheBody(): void
{
$attr = [
'address' => "sales@example.org, info@example.org",
'goto' => 'anna@example.org,ben@example.org',
'active' => '1',
'sogo_visible' => 1,
'internal' => '0',
'sender_allowed' => true,
'public_comment' => 'Sales team',
];
$result = $this->post('add/alias', $attr + ['csrf_token' => 'x']);
$this->assertSame([['add', 'alias', $attr]], $result->callsTo('mailbox'));
}
public function testEditMergesItemsAndAttributes(): void
{
$list = $this->post('edit/alias', ['items' => [12, 'info@example.org'], 'attr' => ['goto_null' => '1', 'active' => '0']]);
$this->assertCalled($list, 'mailbox', ['edit', 'alias', ['id' => [12, 'info@example.org'], 'goto_null' => '1', 'active' => '0']]);
$single = $this->post('edit/alias', ['items' => 12, 'attr' => ['expand_alias' => true]]);
$this->assertCalled($single, 'mailbox', ['edit', 'alias', ['id' => [12], 'expand_alias' => true]]);
}
public function testDeleteTakesIdsOrAddresses(): void
{
$result = $this->post('delete/alias', [12, 'info@example.org']);
$this->assertSame([['delete', 'alias', ['id' => [12, 'info@example.org']]]], $result->callsTo('mailbox'));
}
public function testGetOneAliasByIdOrAddress(): void
{
$byId = $this->get('get/alias/12', ['script' => [self::answer('mailbox', ['get', 'alias_details', '12'], self::SALES)]]);
$this->assertSame(self::SALES, $byId->json());
$byAddress = $this->get('get/alias/sales@example.org');
$this->assertSame([['get', 'alias_details', 'sales@example.org']], $byAddress->callsTo('mailbox'));
$this->assertSame('{}', $byAddress->body);
}
public function testGetAllWalksTheDomainsAndAliasDomains(): void
{
$result = $this->get('get/alias/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], ['example.org', 'example.com']),
self::answer('mailbox', ['get', 'alias_domains'], ['example.net']),
self::answer('mailbox', ['get', 'aliases', 'example.org'], [12, 13, 14]),
self::answer('mailbox', ['get', 'aliases', 'example.com'], []),
self::answer('mailbox', ['get', 'aliases', 'example.net'], [20]),
self::answer('mailbox', ['get', 'alias_details', 12], self::SALES),
self::answer('mailbox', ['get', 'alias_details', 13], self::INFO),
self::answer('mailbox', ['get', 'alias_details', 14], false),
self::answer('mailbox', ['get', 'alias_details', 20], self::SHOP),
]]);
$this->assertSame([self::SALES, self::INFO, self::SHOP], $result->json());
$this->assertSame([
['get', 'domains'],
['get', 'alias_domains'],
['get', 'aliases', 'example.org'],
['get', 'alias_details', 12],
['get', 'alias_details', 13],
['get', 'alias_details', 14],
['get', 'aliases', 'example.com'],
['get', 'aliases', 'example.net'],
['get', 'alias_details', 20],
], $result->callsTo('mailbox'));
}
public function testGetAllOfTheNamedDomains(): void
{
$result = $this->get('get/alias/all/example.org,example.net', ['script' => [
self::answer('mailbox', ['get', 'aliases', 'example.org'], [12]),
self::answer('mailbox', ['get', 'aliases', 'example.net'], [20]),
self::answer('mailbox', ['get', 'alias_details', 12], self::SALES),
self::answer('mailbox', ['get', 'alias_details', 20], self::SHOP),
]]);
$this->assertSame([self::SALES, self::SHOP], $result->json());
$this->assertSame([
['get', 'aliases', 'example.org'],
['get', 'alias_details', 12],
['get', 'aliases', 'example.net'],
['get', 'alias_details', 20],
], $result->callsTo('mailbox'));
}
public function testGetAllWithoutAliases(): void
{
$noDomains = $this->get('get/alias/all', ['script' => [
self::answer('mailbox', ['get', 'domains'], []),
self::answer('mailbox', ['get', 'alias_domains'], []),
]]);
$this->assertSame('{}', $noDomains->body);
$noAliases = $this->get('get/alias/all/example.org', ['script' => [self::answer('mailbox', ['get', 'aliases'], false)]]);
$this->assertSame('{}', $noAliases->body);
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Routing;
use Mailcow\Tests\Api\ApiTestCase;
/** add/edit/delete/get fwdhost */
class ForwardingHostRoutesTest extends ApiTestCase
{
private const HOSTS = [
['host' => '192.0.2.25', 'source' => 'relay.example.com', 'keep_spam' => 'yes'],
['host' => '2001:db8::25', 'source' => 'relay.example.com', 'keep_spam' => 'yes'],
];
public function testAddPassesTheBody(): void
{
$result = $this->post('add/fwdhost', ['hostname' => 'relay.example.com', 'filter_spam' => '1']);
$this->assertSame([['add', ['hostname' => 'relay.example.com', 'filter_spam' => '1']]], $result->callsTo('fwdhost'));
}
public function testEditNamesTheHostsFwdhost(): void
{
$result = $this->post('edit/fwdhost', ['items' => ['192.0.2.25', '198.51.100.0/24'], 'attr' => ['keep_spam' => '0']]);
$this->assertSame([['edit', ['fwdhost' => ['192.0.2.25', '198.51.100.0/24'], 'keep_spam' => '0']]], $result->callsTo('fwdhost'));
}
public function testDeleteNamesTheHostsForwardinghost(): void
{
$result = $this->post('delete/fwdhost', ['192.0.2.25', '2001:db8::25']);
$this->assertSame([['delete', ['forwardinghost' => ['192.0.2.25', '2001:db8::25']]]], $result->callsTo('fwdhost'));
}
public function testGetAllPassesTheList(): void
{
$result = $this->get('get/fwdhost/all', ['script' => [self::answer('fwdhost', ['get'], self::HOSTS)]]);
$this->assertSame(self::HOSTS, $result->json());
$this->assertSame([['get']], $result->callsTo('fwdhost'));
$this->assertSame('{}', $this->get('get/fwdhost/all', ['script' => [self::answer('fwdhost', ['get'], [])]])->body);
}
public function testGetOneHost(): void
{
$found = $this->get('get/fwdhost/192.0.2.25', ['script' => [self::answer('fwdhost', ['details', '192.0.2.25'], self::HOSTS[0])]]);
$this->assertSame(self::HOSTS[0], $found->json());
$this->assertSame('{}', $this->get('get/fwdhost/192.0.2.99', ['script' => [self::answer('fwdhost', ['details'], [])]])->body);
}
public function testGetOneNetworkIsCutAtTheSlash(): void
{
$result = $this->get('get/fwdhost/198.51.100.0/24');
$this->assertSame([['details', '198.51.100.0']], $result->callsTo('fwdhost'));
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Routing;
use Mailcow\Tests\Api\ApiTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
/** bcc, recipient_map and tls-policy-map: json_api.php handles the three alike */
class MapRoutesTest extends ApiTestCase
{
public static function maps(): array
{
return [
'bcc' => ['bcc', 'bcc',
['local_dest' => 'example.org', 'bcc_dest' => 'archive@example.com', 'type' => 'rcpt', 'active' => '1'],
['id' => 3, 'local_dest' => '@example.org', 'bcc_dest' => 'archive@example.com', 'active' => 1, 'type' => 'rcpt', 'domain' => 'example.org']],
'recipient_map' => ['recipient_map', 'recipient_map',
['recipient_map_old' => 'old@example.org', 'recipient_map_new' => 'new@example.org', 'active' => 1],
['id' => 3, 'recipient_map_old' => 'old@example.org', 'recipient_map_new' => 'new@example.org', 'active' => 1]],
'tls-policy-map' => ['tls-policy-map', 'tls_policy_maps',
['dest' => 'example.com', 'policy' => 'encrypt', 'parameters' => 'protocols=>=TLSv1.2', 'active' => '1'],
['id' => 3, 'dest' => 'example.com', 'policy' => 'encrypt', 'parameters' => '', 'active' => 1]],
];
}
#[DataProvider('maps')]
public function testAddPassesTheBody(string $category, string $function, array $attr): void
{
$result = $this->post("add/$category", $attr);
$this->assertSame([['add', $attr]], $result->callsTo($function));
}
#[DataProvider('maps')]
public function testEditMergesItemsAndAttributes(string $category, string $function): void
{
$list = $this->post("edit/$category", ['items' => [3, '4'], 'attr' => ['active' => '0']]);
$this->assertSame([['edit', ['id' => [3, '4'], 'active' => '0']]], $list->callsTo($function));
$single = $this->post("edit/$category", ['items' => 3, 'attr' => ['active' => '1']]);
$this->assertSame([['edit', ['id' => [3], 'active' => '1']]], $single->callsTo($function));
}
#[DataProvider('maps')]
public function testDeleteTakesIds(string $category, string $function): void
{
$result = $this->post("delete/$category", [3, 4]);
$this->assertSame([['delete', ['id' => [3, 4]]]], $result->callsTo($function));
}
#[DataProvider('maps')]
public function testGetAllCollectsTheDetailsOfEveryId(string $category, string $function, array $attr, array $details): void
{
$result = $this->get("get/$category/all", ['script' => [
self::answer($function, ['get'], [3, 4, 5]),
self::answer($function, ['details', 3], $details),
self::answer($function, ['details', 4], false),
self::answer($function, ['details', 5], ['id' => 5] + $details),
]]);
$this->assertSame([$details, ['id' => 5] + $details], $result->json());
$this->assertSame([['get'], ['details', 3], ['details', 4], ['details', 5]], $result->callsTo($function));
}
#[DataProvider('maps')]
public function testGetAllWithoutEntries(string $category, string $function): void
{
$result = $this->get("get/$category/all", ['script' => [self::answer($function, ['get'], [])]]);
$this->assertSame('{}', $result->body);
}
#[DataProvider('maps')]
public function testGetOneAddsANullMember(string $category, string $function, array $attr, array $details): void
{
// json_api.php appends the undefined $details to the answer
$found = $this->get("get/$category/3", ['script' => [self::answer($function, ['details', '3'], $details)]]);
$this->assertSame($details + [0 => null], $found->json());
$missing = $this->get("get/$category/9", ['script' => [self::answer($function, ['details', '9'], false)]]);
$this->assertSame('{}', $missing->body);
}
}
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Mailcow\Tests\Api\Routing;
use Mailcow\Tests\Api\ApiTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
/** relayhost (sender-dependent transports) and transport (transport maps): json_api.php handles both alike */
class TransportRoutesTest extends ApiTestCase
{
public static function transports(): array
{
return [
'relayhost' => ['relayhost',
['hostname' => '[smtp.example.com]:587', 'username' => 'relay-user', 'password' => 'secret'],
['id' => 1, 'hostname' => '[smtp.example.com]:587', 'username' => 'relay-user', 'active' => 1, 'used_by_domains' => 'example.org']],
'transport' => ['transport',
['destination' => 'example.com, .example.com', 'nexthop' => '[mx.example.com]:25', 'is_mx_based' => '0', 'active' => '1'],
['id' => 1, 'is_mx_based' => 0, 'destination' => 'example.com', 'nexthop' => '[mx.example.com]:25', 'active' => 1]],
];
}
#[DataProvider('transports')]
public function testAddPassesTheBody(string $category, array $attr): void
{
$result = $this->post("add/$category", $attr);
$this->assertSame([['add', $attr]], $result->callsTo($category));
}
#[DataProvider('transports')]
public function testEditMergesItemsAndAttributes(string $category): void
{
$result = $this->post("edit/$category", ['items' => [1, 2], 'attr' => ['active' => '0', 'password' => '']]);
$this->assertSame([['edit', ['id' => [1, 2], 'active' => '0', 'password' => '']]], $result->callsTo($category));
}
#[DataProvider('transports')]
public function testDeleteTakesIds(string $category): void
{
$result = $this->post("delete/$category", ['1', 2]);
$this->assertSame([['delete', ['id' => ['1', 2]]]], $result->callsTo($category));
}
#[DataProvider('transports')]
public function testGetAllCollectsTheDetailsOfEveryRow(string $category, array $attr, array $details): void
{
$result = $this->get("get/$category/all", ['script' => [
self::answer($category, ['get'], [['id' => 1, 'active' => 1], ['id' => 2, 'active' => 1], ['id' => 3, 'active' => 0]]),
self::answer($category, ['details', 1], $details),
self::answer($category, ['details', 2], false),
self::answer($category, ['details', 3], ['id' => 3] + $details),
]]);
$this->assertSame([$details, ['id' => 3] + $details], $result->json());
$this->assertSame([['get'], ['details', 1], ['details', 2], ['details', 3]], $result->callsTo($category));
}
#[DataProvider('transports')]
public function testGetAllWithoutEntries(string $category): void
{
$none = $this->get("get/$category/all", ['script' => [self::answer($category, ['get'], [])]]);
$this->assertSame('{}', $none->body);
$noDetails = $this->get("get/$category/all", ['script' => [
self::answer($category, ['get'], [['id' => 1]]),
self::answer($category, ['details'], false),
]]);
$this->assertSame('{}', $noDetails->body);
}
#[DataProvider('transports')]
public function testGetOne(string $category, array $attr, array $details): void
{
$found = $this->get("get/$category/1", ['script' => [self::answer($category, ['details', '1'], $details)]]);
$this->assertSame($details, $found->json());
$missing = $this->get("get/$category/9", ['script' => [self::answer($category, ['details', '9'], false)]]);
$this->assertSame('{}', $missing->body);
}
}
+2 -1
View File
@@ -4,7 +4,8 @@
"type": "project", "type": "project",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"require-dev": { "require-dev": {
"phpunit/phpunit": "^11.5" "phpunit/phpunit": "^11.5",
"symfony/yaml": "^7.3"
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
+235 -2
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "9a4f6f2c4beb80cf38c831d11167da8d", "content-hash": "4211ddb7f0a4ed07b766e52d30177798",
"packages": [], "packages": [],
"packages-dev": [ "packages-dev": [
{ {
@@ -1721,6 +1721,239 @@
], ],
"time": "2024-10-20T05:08:20+00:00" "time": "2024-10-20T05:08:20+00:00"
}, },
{
"name": "symfony/deprecation-contracts",
"version": "v3.8.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "feb1edf4e546af3b85c5caebe8359a20db230ca3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/feb1edf4e546af3b85c5caebe8359a20db230ca3",
"reference": "feb1edf4e546af3b85c5caebe8359a20db230ca3",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.8-dev"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.8.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-09-16T14:15:10+00:00",
"published-time": "2026-11-04T11:25:34+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.37.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "141046a8f9477948ff284fa65be2095baafb94f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2",
"reference": "141046a8f9477948ff284fa65be2095baafb94f2",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-04-10T16:19:22+00:00",
"published-time": "2026-04-26T14:44:50+00:00"
},
{
"name": "symfony/yaml",
"version": "v7.4.23",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "1e601207ffe627a2e621faa9d661f1d32dcf1cec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/1e601207ffe627a2e621faa9d661f1d32dcf1cec",
"reference": "1e601207ffe627a2e621faa9d661f1d32dcf1cec",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0|^8.0"
},
"bin": [
"Resources/bin/yaml-lint"
],
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.4.23"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-11-04T08:27:22+00:00",
"published-time": "2026-11-04T11:28:41+00:00"
},
{ {
"name": "theseer/tokenizer", "name": "theseer/tokenizer",
"version": "1.3.1", "version": "1.3.1",
@@ -1782,5 +2015,5 @@
"platform-overrides": { "platform-overrides": {
"php": "8.2.29" "php": "8.2.29"
}, },
"plugin-api-version": "2.9.0" "plugin-api-version": "2.11.0"
} }
+4 -2
View File
@@ -374,10 +374,12 @@ if ! $COMPOSE_COMMAND config -q; then
fi fi
echo -e "\e[32mChecking for conflicting bridges...\e[0m" echo -e "\e[32mChecking for conflicting bridges...\e[0m"
MAILCOW_BRIDGE=$($COMPOSE_COMMAND config | grep -i com.docker.network.bridge.name | cut -d':' -f2) MAILCOW_BRIDGE=$($COMPOSE_COMMAND config | grep -i com.docker.network.bridge.name | cut -d':' -f2 | tr -d "[:space:]\"'")
if [[ -n "${MAILCOW_BRIDGE}" ]]; then
while read NAT_ID; do while read NAT_ID; do
iptables -t nat -D POSTROUTING "$NAT_ID" iptables -t nat -D POSTROUTING "$NAT_ID"
done < <(iptables -L -vn -t nat --line-numbers | grep "$IPV4_NETWORK" | grep -E 'MASQUERADE.*all' | grep -v "${MAILCOW_BRIDGE}" | cut -d' ' -f1) done < <(iptables -L -vn -t nat --line-numbers | grep "$IPV4_NETWORK" | grep -E 'MASQUERADE.*all' | grep -vE "[[:space:]]!?${MAILCOW_BRIDGE}[[:space:]]" | cut -d' ' -f1)
fi
DIFF_DIRECTORY=update_diffs DIFF_DIRECTORY=update_diffs
DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S") DIFF_FILE=${DIFF_DIRECTORY}/diff_before_update_$(date +"%Y-%m-%d-%H-%M-%S")