diff --git a/.dockerignore b/.dockerignore index 3765648b1..8b26d6a73 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,5 @@ * !dist !build +!metadata.json +!docker-extension/build diff --git a/api/cli/cli.go b/api/cli/cli.go index f04804cd3..8a2ee9c19 100644 --- a/api/cli/cli.go +++ b/api/cli/cli.go @@ -51,7 +51,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) { SSLKey: kingpin.Flag("sslkey", "Path to the SSL key used to secure the Portainer instance").String(), Rollback: kingpin.Flag("rollback", "Rollback the database store to the previous version").Bool(), SnapshotInterval: kingpin.Flag("snapshot-interval", "Duration between each environment snapshot job").String(), - AdminPassword: kingpin.Flag("admin-password", "Hashed admin password").String(), + AdminPassword: kingpin.Flag("admin-password", "Set admin password with provided hash").String(), AdminPasswordFile: kingpin.Flag("admin-password-file", "Path to the file containing the password for the admin user").String(), Labels: pairs(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l')), Logo: kingpin.Flag("logo", "URL for the logo displayed in the UI").String(), diff --git a/api/crypto/hash.go b/api/crypto/hash.go index 3e52dfbd3..d9b212bf3 100644 --- a/api/crypto/hash.go +++ b/api/crypto/hash.go @@ -9,11 +9,11 @@ type Service struct{} // Hash hashes a string using the bcrypt algorithm func (*Service) Hash(data string) (string, error) { - hash, err := bcrypt.GenerateFromPassword([]byte(data), bcrypt.DefaultCost) + bytes, err := bcrypt.GenerateFromPassword([]byte(data), bcrypt.DefaultCost) if err != nil { - return "", nil + return "", err } - return string(hash), nil + return string(bytes), err } // CompareHashAndData compares a hash to clear data and returns an error if the comparison fails. diff --git a/api/crypto/hash_test.go b/api/crypto/hash_test.go new file mode 100644 index 000000000..bc73be3a5 --- /dev/null +++ b/api/crypto/hash_test.go @@ -0,0 +1,53 @@ +package crypto + +import ( + "testing" +) + +func TestService_Hash(t *testing.T) { + var s = &Service{} + + type args struct { + hash string + data string + } + tests := []struct { + name string + args args + expect bool + }{ + { + name: "Empty", + args: args{ + hash: "", + data: "", + }, + expect: false, + }, + { + name: "Matching", + args: args{ + hash: "$2a$10$6BFGd94oYx8k0bFNO6f33uPUpcpAJyg8UVX.akLe9EthF/ZBTXqcy", + data: "Passw0rd!", + }, + expect: true, + }, + { + name: "Not matching", + args: args{ + hash: "$2a$10$ltKrUZ7492xyutHOb0/XweevU4jyw7QO66rP32jTVOMb3EX3JxA/a", + data: "Passw0rd!", + }, + expect: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + err := s.CompareHashAndData(tt.args.hash, tt.args.data) + if (err != nil) == tt.expect { + t.Errorf("Service.CompareHashAndData() = %v", err) + } + }) + } +} diff --git a/api/filesystem/write.go b/api/filesystem/write.go index 235511933..1e4b714a0 100644 --- a/api/filesystem/write.go +++ b/api/filesystem/write.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" ) +// WriteToFile creates a file in the filesystem storage func WriteToFile(dst string, content []byte) error { if err := os.MkdirAll(filepath.Dir(dst), 0744); err != nil { return errors.Wrapf(err, "failed to create filestructure for the path %q", dst) diff --git a/api/portainer.go b/api/portainer.go index 2f942ea82..ca5711247 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -1344,7 +1344,7 @@ type ( const ( // APIVersion is the version number of the Portainer API - APIVersion = "2.11.0" + APIVersion = "2.11.1" // DBVersion is the version number of the Portainer database DBVersion = 35 // ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax diff --git a/app/config.js b/app/config.js index 2cac8c5dc..d701ee818 100644 --- a/app/config.js +++ b/app/config.js @@ -14,6 +14,7 @@ export function configApp($urlRouterProvider, $httpProvider, localStorageService tokenGetter: /* @ngInject */ function tokenGetter(LocalStorage) { return LocalStorage.getJWT(); }, + whiteListedDomains: ['localhost'], }); $httpProvider.interceptors.push('jwtInterceptor'); diff --git a/app/docker/views/containers/console/containerConsoleController.js b/app/docker/views/containers/console/containerConsoleController.js index ab2e92d6c..940bd30ba 100644 --- a/app/docker/views/containers/console/containerConsoleController.js +++ b/app/docker/views/containers/console/containerConsoleController.js @@ -69,9 +69,9 @@ angular.module('portainer.docker').controller('ContainerConsoleController', [ id: attachId, }; + const base = window.location.origin.startsWith('http') ? `${window.location.origin}${baseHref()}` : baseHref(); var url = - window.location.origin + - baseHref() + + base + 'api/websocket/attach?' + Object.keys(params) .map((k) => k + '=' + params[k]) @@ -110,9 +110,9 @@ angular.module('portainer.docker').controller('ContainerConsoleController', [ id: data.Id, }; + const base = window.location.origin.startsWith('http') ? `${window.location.origin}${baseHref()}` : baseHref(); var url = - window.location.origin + - baseHref() + + base + 'api/websocket/exec?' + Object.keys(params) .map((k) => k + '=' + params[k]) diff --git a/app/global.d.ts b/app/global.d.ts index a8842eefe..3ae967ca1 100644 --- a/app/global.d.ts +++ b/app/global.d.ts @@ -18,3 +18,7 @@ declare module 'axios-progress-bar' { instance?: AxiosInstance ): void; } + +interface Window { + ddExtension: boolean; +} diff --git a/app/index.html b/app/index.html index 920e1af72..8748fde19 100644 --- a/app/index.html +++ b/app/index.html @@ -7,9 +7,16 @@ @@ -62,9 +69,12 @@ -
- +
+ + + + + + + + diff --git a/app/kubernetes/components/kubectl-shell/kubectl-shell.controller.js b/app/kubernetes/components/kubectl-shell/kubectl-shell.controller.js index 2a96fb6d0..3b77d9ba1 100644 --- a/app/kubernetes/components/kubectl-shell/kubectl-shell.controller.js +++ b/app/kubernetes/components/kubectl-shell/kubectl-shell.controller.js @@ -93,11 +93,13 @@ export default class KubectlShellController { const wsProtocol = this.$window.location.protocol === 'https:' ? 'wss://' : 'ws://'; const path = baseHref() + 'api/websocket/kubernetes-shell'; + const base = path.startsWith('http') ? path.replace(/^https?:\/\//i, '') : window.location.host + path; + const queryParams = Object.entries(params) .map(([k, v]) => `${k}=${v}`) .join('&'); - const url = `${wsProtocol}${window.location.host}${path}?${queryParams}`; + const url = `${wsProtocol}${base}?${queryParams}`; Terminal.applyAddon(fit); this.state.shell.socket = new WebSocket(url); this.state.shell.term = new Terminal(); diff --git a/app/kubernetes/views/applications/console/consoleController.js b/app/kubernetes/views/applications/console/consoleController.js index 590bbedcf..f0912d106 100644 --- a/app/kubernetes/views/applications/console/consoleController.js +++ b/app/kubernetes/views/applications/console/consoleController.js @@ -58,9 +58,10 @@ class KubernetesApplicationConsoleController { command: this.state.command, }; + const base = window.location.origin.startsWith('http') ? `${window.location.origin}${baseHref()}` : baseHref(); + let url = - window.location.origin + - baseHref() + + base + 'api/websocket/pod?' + Object.keys(params) .map((k) => k + '=' + params[k]) diff --git a/app/portainer/components/PageHeader/HeaderContent.controller.js b/app/portainer/components/PageHeader/HeaderContent.controller.js index f69beb9f8..7392c6be5 100644 --- a/app/portainer/components/PageHeader/HeaderContent.controller.js +++ b/app/portainer/components/PageHeader/HeaderContent.controller.js @@ -2,7 +2,7 @@ export default class HeaderContentController { /* @ngInject */ constructor(Authentication) { this.Authentication = Authentication; - + this.display = !window.ddExtension; this.username = null; } diff --git a/app/portainer/components/PageHeader/HeaderContent.html b/app/portainer/components/PageHeader/HeaderContent.html index 2b5d569e5..51b1eb69c 100644 --- a/app/portainer/components/PageHeader/HeaderContent.html +++ b/app/portainer/components/PageHeader/HeaderContent.html @@ -1,11 +1,11 @@