Compare commits

..

1 Commits

Author SHA1 Message Date
Jamie Curnow
6bcc30bfe9 Cypress test for Streams
and updated cypress + packages
2025-02-05 12:46:49 +10:00
21 changed files with 538 additions and 630 deletions

View File

@@ -1 +1 @@
2.12.3
2.12.2

View File

@@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<br><br>
<img src="https://img.shields.io/badge/version-2.12.3-green.svg?style=for-the-badge">
<img src="https://img.shields.io/badge/version-2.12.2-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a>

View File

@@ -508,13 +508,8 @@ const internalAccessList = {
if (typeof item.password !== 'undefined' && item.password.length) {
logger.info('Adding: ' + item.username);
utils.execFile('openssl', ['passwd', '-apr1', item.password])
.then((res) => {
try {
fs.appendFileSync(htpasswd_file, item.username + ':' + res + '\n', {encoding: 'utf8'});
} catch (err) {
reject(err);
}
utils.execFile('/usr/bin/htpasswd', ['-b', htpasswd_file, item.username, item.password])
.then((/*result*/) => {
next();
})
.catch((err) => {

View File

@@ -313,9 +313,6 @@ const internalCertificate = {
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowGraph('[owner]')
.allowGraph('[proxy_hosts]')
.allowGraph('[redirection_hosts]')
.allowGraph('[dead_hosts]')
.first();
if (access_data.permission_visibility !== 'all') {
@@ -467,9 +464,6 @@ const internalCertificate = {
.where('is_deleted', 0)
.groupBy('id')
.allowGraph('[owner]')
.allowGraph('[proxy_hosts]')
.allowGraph('[redirection_hosts]')
.allowGraph('[dead_hosts]')
.orderBy('nice_name', 'ASC');
if (access_data.permission_visibility !== 'all') {

View File

@@ -4,6 +4,7 @@
const db = require('../db');
const helpers = require('../lib/helpers');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
Model.knex(db);
@@ -67,11 +68,6 @@ class Certificate extends Model {
}
static get relationMappings () {
const ProxyHost = require('./proxy_host');
const DeadHost = require('./dead_host');
const User = require('./user');
const RedirectionHost = require('./redirection_host');
return {
owner: {
relation: Model.HasOneRelation,
@@ -83,39 +79,6 @@ class Certificate extends Model {
modify: function (qb) {
qb.where('user.is_deleted', 0);
}
},
proxy_hosts: {
relation: Model.HasManyRelation,
modelClass: ProxyHost,
join: {
from: 'certificate.id',
to: 'proxy_host.certificate_id'
},
modify: function (qb) {
qb.where('proxy_host.is_deleted', 0);
}
},
dead_hosts: {
relation: Model.HasManyRelation,
modelClass: DeadHost,
join: {
from: 'certificate.id',
to: 'dead_host.certificate_id'
},
modify: function (qb) {
qb.where('dead_host.is_deleted', 0);
}
},
redirection_hosts: {
relation: Model.HasManyRelation,
modelClass: RedirectionHost,
join: {
from: 'certificate.id',
to: 'redirection_host.certificate_id'
},
modify: function (qb) {
qb.where('redirection_host.is_deleted', 0);
}
}
};
}

View File

@@ -19,7 +19,26 @@
"incoming_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
"maximum": 65535,
"if": {
"type": "object",
"properties": {
"tcp_forwarding": {
"const": true
}
}
},
"then": {
"not": {
"oneOf": [
{
"const": 80
}, {
"const": 443
}
]
}
}
},
"forwarding_host": {
"anyOf": [
@@ -63,14 +82,7 @@
"$ref": "./user-object.json"
},
"certificate": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "./certificate-object.json"
}
]
}
}
}

View File

@@ -5,9 +5,10 @@
"additionalProperties": false,
"properties": {
"expires": {
"description": "Token Expiry ISO Time String",
"example": "2025-02-04T20:40:46.340Z",
"type": "string"
"description": "Token Expiry Unix Time",
"example": 1566540249,
"minimum": 1,
"type": "number"
},
"token": {
"description": "JWT Token",

View File

@@ -15,7 +15,7 @@
"examples": {
"default": {
"value": {
"expires": "2025-02-04T20:40:46.340Z",
"expires": 1566540510,
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
}
}

View File

@@ -38,7 +38,7 @@
"default": {
"value": {
"result": {
"expires": "2025-02-04T20:40:46.340Z",
"expires": 1566540510,
"token": "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey...xaHKYr3Kk6MvkUjcC4"
}
}

View File

@@ -101,7 +101,7 @@ services:
HTTP_PROXY: 'squid:3128'
HTTPS_PROXY: 'squid:3128'
volumes:
- 'cypress_logs:/test/results'
- 'cypress_logs:/results'
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
command: cypress run --browser chrome --config-file=cypress/config/ci.js

View File

@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
GREEN='\E[1;32m'
RESET='\E[0m'
S6_OVERLAY_VERSION=3.2.0.2
S6_OVERLAY_VERSION=3.1.5.0
TARGETPLATFORM=${1:-linux/amd64}
# Determine the correct binary file for the architecture given

View File

@@ -26,7 +26,7 @@ module.exports = {
* Users
*/
showUsers: function () {
const controller = this;
let controller = this;
if (Cache.User.isAdmin()) {
require(['./main', './users/main'], (App, View) => {
controller.navigate('/users');
@@ -93,7 +93,8 @@ module.exports = {
* Dashboard
*/
showDashboard: function () {
const controller = this;
let controller = this;
require(['./main', './dashboard/main'], (App, View) => {
controller.navigate('/');
App.UI.showAppContent(new View());
@@ -105,7 +106,7 @@ module.exports = {
*/
showNginxProxy: function () {
if (Cache.User.isAdmin() || Cache.User.canView('proxy_hosts')) {
const controller = this;
let controller = this;
require(['./main', './nginx/proxy/main'], (App, View) => {
controller.navigate('/nginx/proxy');
@@ -145,7 +146,8 @@ module.exports = {
*/
showNginxRedirection: function () {
if (Cache.User.isAdmin() || Cache.User.canView('redirection_hosts')) {
const controller = this;
let controller = this;
require(['./main', './nginx/redirection/main'], (App, View) => {
controller.navigate('/nginx/redirection');
App.UI.showAppContent(new View());
@@ -184,7 +186,8 @@ module.exports = {
*/
showNginxStream: function () {
if (Cache.User.isAdmin() || Cache.User.canView('streams')) {
const controller = this;
let controller = this;
require(['./main', './nginx/stream/main'], (App, View) => {
controller.navigate('/nginx/stream');
App.UI.showAppContent(new View());
@@ -223,7 +226,8 @@ module.exports = {
*/
showNginxDead: function () {
if (Cache.User.isAdmin() || Cache.User.canView('dead_hosts')) {
const controller = this;
let controller = this;
require(['./main', './nginx/dead/main'], (App, View) => {
controller.navigate('/nginx/404');
App.UI.showAppContent(new View());
@@ -274,7 +278,8 @@ module.exports = {
*/
showNginxAccess: function () {
if (Cache.User.isAdmin() || Cache.User.canView('access_lists')) {
const controller = this;
let controller = this;
require(['./main', './nginx/access/main'], (App, View) => {
controller.navigate('/nginx/access');
App.UI.showAppContent(new View());
@@ -313,7 +318,8 @@ module.exports = {
*/
showNginxCertificates: function () {
if (Cache.User.isAdmin() || Cache.User.canView('certificates')) {
const controller = this;
let controller = this;
require(['./main', './nginx/certificates/main'], (App, View) => {
controller.navigate('/nginx/certificates');
App.UI.showAppContent(new View());
@@ -377,7 +383,7 @@ module.exports = {
* Audit Log
*/
showAuditLog: function () {
const controller = this;
let controller = this;
if (Cache.User.isAdmin()) {
require(['./main', './audit-log/main'], (App, View) => {
controller.navigate('/audit-log');
@@ -405,7 +411,7 @@ module.exports = {
* Settings
*/
showSettings: function () {
const controller = this;
let controller = this;
if (Cache.User.isAdmin()) {
require(['./main', './settings/main'], (App, View) => {
controller.navigate('/settings');

View File

@@ -24,7 +24,7 @@ module.exports = Mn.View.extend({
},
templateContext: function () {
const view = this;
let view = this;
return {
getUserName: function () {
@@ -48,7 +48,8 @@ module.exports = Mn.View.extend({
},
onRender: function () {
const view = this;
let view = this;
if (typeof view.stats.hosts === 'undefined') {
Api.Reports.getHostStats()
.then(response => {
@@ -71,7 +72,8 @@ module.exports = Mn.View.extend({
// calculate the available columns based on permissions for the objects
// and store as a variable
const perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
//let view = this;
let perms = ['proxy_hosts', 'redirection_hosts', 'streams', 'dead_hosts'];
perms.map(perm => {
this.columns += Cache.User.isAdmin() || Cache.User.canView(perm) ? 1 : 0;

View File

@@ -33,13 +33,6 @@
<td class="<%- isExpired() ? 'text-danger' : '' %>">
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
</td>
<td>
<% if (active_domain_names().length > 0) { %>
<span class="status-icon bg-success"></span> <%- i18n('certificates', 'in-use') %>
<% } else { %>
<span class="status-icon bg-danger"></span> <%- i18n('certificates', 'inactive') %>
<% } %>
</td>
<% if (canManage) { %>
<td class="text-right">
<div class="item-action dropdown">
@@ -55,13 +48,6 @@
<div class="dropdown-divider"></div>
<% } %>
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
<% if (active_domain_names().length > 0) { %>
<div class="dropdown-divider"></div>
<span class="dropdown-header"><%- i18n('certificates', 'active-domain_names') %></span>
<% active_domain_names().forEach(function(host) { %>
<a href="https://<%- host %>" class="dropdown-item" target="_blank"><%- host %></a>
<% }); %>
<% } %>
</div>
</div>
</td>

View File

@@ -44,24 +44,14 @@ module.exports = Mn.View.extend({
},
},
templateContext: function () {
return {
templateContext: {
canManage: App.Cache.User.canManage('certificates'),
isExpired: function () {
return moment(this.expires_on).isBefore(moment());
},
dns_providers: dns_providers,
active_domain_names: function () {
const { proxy_hosts = [], redirect_hosts = [], dead_hosts = [] } = this;
return [...proxy_hosts, ...redirect_hosts, ...dead_hosts].reduce((acc, host) => {
acc.push(...(host.domain_names || []));
return acc;
}, []);
}
};
dns_providers: dns_providers
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
}

View File

@@ -3,7 +3,6 @@
<th><%- i18n('str', 'name') %></th>
<th><%- i18n('all-hosts', 'cert-provider') %></th>
<th><%- i18n('str', 'expires') %></th>
<th><%- i18n('str', 'status') %></th>
<% if (canManage) { %>
<th>&nbsp;</th>
<% } %>

View File

@@ -74,7 +74,7 @@ module.exports = Mn.View.extend({
e.preventDefault();
let query = this.ui.query.val();
this.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'], query)
this.fetch(['owner'], query)
.then(response => this.showData(response))
.catch(err => {
this.showError(err);
@@ -89,7 +89,7 @@ module.exports = Mn.View.extend({
onRender: function () {
let view = this;
view.fetch(['owner','proxy_hosts', 'dead_hosts', 'redirection_hosts'])
view.fetch(['owner'])
.then(response => {
if (!view.isDestroyed()) {
if (response && response.length) {

View File

@@ -208,10 +208,7 @@
"reachability-other": "There is a server found at this domain but it returned an unexpected status code {code}. Is it the NPM server? Please make sure your domain points to the IP where your NPM instance is running.",
"download": "Download",
"renew-title": "Renew Let's Encrypt Certificate",
"search": "Search Certificate…",
"in-use" : "In use",
"inactive": "Inactive",
"active-domain_names": "Active domain names"
"search": "Search Certificate…"
},
"access-lists": {
"title": "Access Lists",

View File

@@ -161,11 +161,11 @@
},
"domainoffensive": {
"name": "DomainOffensive (do.de)",
"package_name": "certbot-dns-domainoffensive",
"version": "~=2.0.0",
"package_name": "certbot-dns-do",
"version": "~=0.31.0",
"dependencies": "",
"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
"full_plugin_name": "dns-domainoffensive"
"full_plugin_name": "dns-do"
},
"domeneshop": {
"name": "Domeneshop",
@@ -364,7 +364,7 @@
"package_name": "certbot-dns-mijn-host",
"version": "~=0.0.4",
"dependencies": "",
"credentials": "dns_mijn_host_api_key=0123456789abcdef0123456789abcdef",
"credentials": "dns-mijn-host-credentials = /etc/letsencrypt/mijnhost-credentials.ini",
"full_plugin_name": "dns-mijn-host"
},
"namecheap": {
@@ -534,13 +534,5 @@
"dependencies": "",
"credentials": "edgedns_client_secret = as3d1asd5d1a32sdfsdfs2d1asd5=\nedgedns_host = sdflskjdf-dfsdfsdf-sdfsdfsdf.luna.akamaiapis.net\nedgedns_access_token = kjdsi3-34rfsdfsdf-234234fsdfsdf\nedgedns_client_token = dkfjdf-342fsdfsd-23fsdfsdfsdf",
"full_plugin_name": "edgedns"
},
"zoneedit": {
"name": "ZoneEdit",
"package_name": "certbot-dns-zoneedit",
"version": "~=0.3.2",
"dependencies": "--no-deps dnspython",
"credentials": "dns_zoneedit_user = <login-user-id>\ndns_zoneedit_token = <dyn-authentication-token>",
"full_plugin_name": "dns-zoneedit"
}
}

View File

@@ -1,5 +1,7 @@
FROM cypress/included:14.0.1
COPY --chown=1000 ./test /test
# Disable Cypress CLI colors
ENV FORCE_COLOR=0
ENV NO_COLOR=1
@@ -16,7 +18,6 @@ RUN wget "https://github.com/testssl/testssl.sh/archive/refs/tags/v3.2rc4.tar.gz
&& wget "https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64" -O /bin/mkcert \
&& chmod +x /bin/mkcert
COPY --chown=1000 ./test /test
WORKDIR /test
RUN yarn install && yarn cache clean
ENTRYPOINT []

View File

@@ -6,21 +6,7 @@ describe('Streams', () => {
before(() => {
cy.getToken().then((tok) => {
token = tok;
// Set default site content
cy.task('backendApiPut', {
token: token,
path: '/api/settings/default-site',
data: {
value: 'html',
meta: {
html: '<p>yay it works</p>'
},
},
}).then((data) => {
cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
});
});
// Create a custom cert pair
cy.exec('mkcert -cert-file=/test/cypress/fixtures/website1.pem -key-file=/test/cypress/fixtures/website1.key.pem website1.example.com').then((result) => {
expect(result.code).to.eq(0);
@@ -29,8 +15,6 @@ describe('Streams', () => {
expect(result.code).to.eq(0);
});
});
cy.exec('rm -f /test/results/testssl.json');
});
it('Should be able to create TCP Stream', function() {
@@ -58,9 +42,9 @@ describe('Streams', () => {
expect(data).to.have.property('tcp_forwarding', true);
expect(data).to.have.property('udp_forwarding', false);
cy.exec('curl --noproxy -- http://website1.example.com:1500').then((result) => {
cy.exec('curl --noproxy -I http://website1.example.com:1500').then((result) => {
expect(result.code).to.eq(0);
expect(result.stdout).to.contain('yay it works');
expect(result.stderr).to.contain('HTTP/1.1 200 OK');
});
});
});
@@ -117,9 +101,10 @@ describe('Streams', () => {
expect(data).to.have.property('tcp_forwarding', true);
expect(data).to.have.property('udp_forwarding', true);
cy.exec('curl --noproxy -- http://website1.example.com:1502').then((result) => {
// Check the port is working
cy.exec('curl --noproxy -I http://website1.example.com:1502').then((result) => {
expect(result.code).to.eq(0);
expect(result.stdout).to.contain('yay it works');
expect(result.stderr).to.contain('HTTP/1.1 200 OK');
});
});
});
@@ -179,31 +164,15 @@ describe('Streams', () => {
expect(data).to.have.property('udp_forwarding', false);
expect(data).to.have.property('certificate_id', certID);
// wait 5 mins
cy.wait(300000);
// Check the ssl termination
cy.task('log', '[testssl.sh] Running ...');
cy.exec('/testssl/testssl.sh --quiet --add-ca="$(/bin/mkcert -CAROOT)/rootCA.pem" --jsonfile=/test/results/testssl.json website1.example.com:1503', {
cy.exec('/testssl/testssl.sh --quiet --add-ca="$(/bin/mkcert -CAROOT)" --jsonfile=/test/cypress/results/testssl.json website1.example.com:1503', {
timeout: 120000, // 2 minutes
failOnNonZeroExit: false,
}).then((result) => {
cy.task('log', '[testssl.sh] ' + result.stdout);
const allowedSeverities = ["INFO", "OK", "LOW", "MEDIUM"];
const ignoredIDs = [
'cert_chain_of_trust',
'cert_extlifeSpan',
'cert_revocation',
'overall_grade',
];
cy.readFile('/test/results/testssl.json').then((data) => {
// Parse each array item
for (let i = 0; i < data.length; i++) {
const item = data[i];
if (ignoredIDs.includes(item.id)) {
continue;
}
expect(item.severity).to.be.oneOf(allowedSeverities);
}
});
expect(result.code).to.eq(0);
});
});
});
@@ -211,3 +180,4 @@ describe('Streams', () => {
});
});