Compare commits
18 Commits
fix/EE-611
...
feat/INT-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
219a209f13 | ||
|
|
ecab23e168 | ||
|
|
534adc7ad3 | ||
|
|
31daea2801 | ||
|
|
607e26d0e7 | ||
|
|
8b26ab1185 | ||
|
|
0368f6f678 | ||
|
|
1584999d50 | ||
|
|
832fb88ce2 | ||
|
|
b9fed9a133 | ||
|
|
afccfc1d83 | ||
|
|
d3b429227d | ||
|
|
ebc3c2b2ed | ||
|
|
7801cabb01 | ||
|
|
27c0f6d3bb | ||
|
|
020c2efa42 | ||
|
|
5a5b8911e1 | ||
|
|
4ab39538df |
@@ -8,17 +8,17 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/portainer/libhelm"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/bolt"
|
||||
"github.com/portainer/portainer/api/chisel"
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
|
||||
"github.com/portainer/libhelm"
|
||||
"github.com/portainer/portainer/api/exec"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
"github.com/portainer/portainer/api/git"
|
||||
"github.com/portainer/portainer/api/hostmanagement/intel/openamt"
|
||||
"github.com/portainer/portainer/api/http"
|
||||
"github.com/portainer/portainer/api/http/client"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
@@ -468,6 +468,8 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
|
||||
gitService := initGitService()
|
||||
|
||||
openAMTService := openamt.NewService()
|
||||
|
||||
cryptoService := initCryptoService()
|
||||
|
||||
digitalSignatureService := initDigitalSignatureService()
|
||||
@@ -623,6 +625,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
LDAPService: ldapService,
|
||||
OAuthService: oauthService,
|
||||
GitService: gitService,
|
||||
OpenAMTService: openAMTService,
|
||||
ProxyManager: proxyManager,
|
||||
KubernetesTokenCacheManager: kubernetesTokenCacheManager,
|
||||
KubeConfigService: kubeConfigService,
|
||||
|
||||
@@ -37,7 +37,6 @@ require (
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/swaggo/swag v1.7.3
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
@@ -47,4 +46,5 @@ require (
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
k8s.io/client-go v0.22.2
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
|
||||
)
|
||||
|
||||
29
api/go.sum
29
api/go.sum
@@ -41,8 +41,6 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
@@ -64,9 +62,7 @@ github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
@@ -320,19 +316,11 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
|
||||
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
@@ -456,8 +444,6 @@ github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/ansi v0.0.0-20170202005112-f496b27cd669 h1:l5rH/CnVVu+HPxjtxjM90nHrm4nov3j3RF9/62UjgLs=
|
||||
github.com/jpillora/ansi v0.0.0-20170202005112-f496b27cd669/go.mod h1:kOeLNvjNBGSV3uYtFjvb72+fnZCMFJF1XDvRIjdom0g=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||
@@ -503,8 +489,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@@ -648,7 +632,6 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
@@ -691,8 +674,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/swaggo/swag v1.7.3 h1:ucB7irEdRrhjmW+Z1Ss4GjO68oPKQFjSgOR8BCAvcbU=
|
||||
github.com/swaggo/swag v1.7.3/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3kNqI=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
@@ -706,7 +687,6 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
@@ -790,7 +770,6 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -827,7 +806,6 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
|
||||
@@ -908,7 +886,6 @@ golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -926,7 +903,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -973,8 +949,6 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1078,7 +1052,6 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -1149,3 +1122,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZa
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
|
||||
|
||||
53
api/hostmanagement/intel/openamt/authorization.go
Normal file
53
api/hostmanagement/intel/openamt/authorization.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package openamt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type authenticationResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func (service *Service) executeAuthenticationRequest(configuration portainer.OpenAMTConfiguration) (*authenticationResponse, error) {
|
||||
loginURL := fmt.Sprintf("https://%v/mps/login/api/v1/authorize", configuration.MPSURL)
|
||||
|
||||
payload := map[string]string{
|
||||
"username": configuration.Credentials.MPSUser,
|
||||
"password": configuration.Credentials.MPSPassword,
|
||||
}
|
||||
jsonValue, _ := json.Marshal(payload)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, loginURL, bytes.NewBuffer(jsonValue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
response, err := service.httpsClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseBody, readErr := ioutil.ReadAll(response.Body)
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
errorResponse := parseError(responseBody)
|
||||
if errorResponse != nil {
|
||||
return nil, errorResponse
|
||||
}
|
||||
|
||||
var token authenticationResponse
|
||||
err = json.Unmarshal(responseBody, &token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &token, nil
|
||||
}
|
||||
119
api/hostmanagement/intel/openamt/configCIRA.go
Normal file
119
api/hostmanagement/intel/openamt/configCIRA.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package openamt
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type CIRAConfig struct {
|
||||
ConfigName string `json:"configName"`
|
||||
MPSServerAddress string `json:"mpsServerAddress"`
|
||||
ServerAddressFormat int `json:"serverAddressFormat"`
|
||||
CommonName string `json:"commonName"`
|
||||
MPSPort int `json:"mpsPort"`
|
||||
Username string `json:"username"`
|
||||
MPSRootCertificate string `json:"mpsRootCertificate"`
|
||||
RegeneratePassword bool `json:"regeneratePassword"`
|
||||
AuthMethod int `json:"authMethod"`
|
||||
}
|
||||
|
||||
func (service *Service) createOrUpdateCIRAConfig(configuration portainer.OpenAMTConfiguration, configName string) (*CIRAConfig, error) {
|
||||
ciraConfig, err := service.getCIRAConfig(configuration, configName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
method := http.MethodPost
|
||||
if ciraConfig != nil {
|
||||
method = http.MethodPatch
|
||||
}
|
||||
|
||||
ciraConfig, err = service.saveCIRAConfig(method, configuration, configName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ciraConfig, nil
|
||||
}
|
||||
|
||||
func (service *Service) getCIRAConfig(configuration portainer.OpenAMTConfiguration, configName string) (*CIRAConfig, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/ciraconfigs/%v", configuration.MPSURL, configName)
|
||||
|
||||
responseBody, err := service.executeGetRequest(url, configuration.Credentials.MPSToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if responseBody == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result CIRAConfig
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (service *Service) saveCIRAConfig(method string, configuration portainer.OpenAMTConfiguration, configName string) (*CIRAConfig, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/ciraconfigs", configuration.MPSURL)
|
||||
|
||||
certificate, err := service.getCIRACertificate(configuration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := CIRAConfig{
|
||||
ConfigName: configName,
|
||||
MPSServerAddress: configuration.MPSURL,
|
||||
ServerAddressFormat: 3,
|
||||
CommonName: configuration.MPSURL,
|
||||
MPSPort: 4433,
|
||||
Username: "admin",
|
||||
MPSRootCertificate: certificate,
|
||||
RegeneratePassword: false,
|
||||
AuthMethod: 2,
|
||||
}
|
||||
payload, _ := json.Marshal(config)
|
||||
|
||||
responseBody, err := service.executeSaveRequest(method, url, configuration.Credentials.MPSToken, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result CIRAConfig
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (service *Service) getCIRACertificate(configuration portainer.OpenAMTConfiguration) (string, error) {
|
||||
loginURL := fmt.Sprintf("https://%v/mps/api/v1/ciracert", configuration.MPSURL)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, loginURL, nil)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", configuration.Credentials.MPSToken))
|
||||
|
||||
response, err := service.httpsClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return "", errors.New(fmt.Sprintf("unexpected status code %v", response.Status))
|
||||
}
|
||||
|
||||
certificate, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
block, _ := pem.Decode(certificate)
|
||||
return base64.StdEncoding.EncodeToString(block.Bytes), nil
|
||||
}
|
||||
81
api/hostmanagement/intel/openamt/configDomain.go
Normal file
81
api/hostmanagement/intel/openamt/configDomain.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package openamt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type (
|
||||
Domain struct {
|
||||
DomainName string `json:"profileName"`
|
||||
DomainSuffix string `json:"domainSuffix"`
|
||||
ProvisioningCert string `json:"provisioningCert"`
|
||||
ProvisioningCertPassword string `json:"provisioningCertPassword"`
|
||||
ProvisioningCertStorageFormat string `json:"provisioningCertStorageFormat"`
|
||||
}
|
||||
)
|
||||
|
||||
func (service *Service) createOrUpdateDomain(configuration portainer.OpenAMTConfiguration, domainName string) (*Domain, error) {
|
||||
domain, err := service.getDomain(configuration, domainName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
method := http.MethodPost
|
||||
if domain != nil {
|
||||
method = http.MethodPatch
|
||||
}
|
||||
|
||||
domain, err = service.saveDomain(method, configuration, domainName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func (service *Service) getDomain(configuration portainer.OpenAMTConfiguration, domainName string) (*Domain, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/domains/%v", configuration.MPSURL, domainName)
|
||||
|
||||
responseBody, err := service.executeGetRequest(url, configuration.Credentials.MPSToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if responseBody == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result Domain
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (service *Service) saveDomain(method string, configuration portainer.OpenAMTConfiguration, domainName string) (*Domain, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/domains", configuration.MPSURL)
|
||||
|
||||
profile := Domain{
|
||||
DomainName: domainName,
|
||||
DomainSuffix: configuration.DomainConfiguration.DomainName,
|
||||
ProvisioningCert: configuration.DomainConfiguration.CertFileText,
|
||||
ProvisioningCertPassword: configuration.DomainConfiguration.CertPassword,
|
||||
ProvisioningCertStorageFormat: "string",
|
||||
}
|
||||
payload, _ := json.Marshal(profile)
|
||||
|
||||
responseBody, err := service.executeSaveRequest(method, url, configuration.Credentials.MPSToken, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result Domain
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
100
api/hostmanagement/intel/openamt/configProfile.go
Normal file
100
api/hostmanagement/intel/openamt/configProfile.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package openamt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type (
|
||||
Profile struct {
|
||||
ProfileName string `json:"profileName"`
|
||||
Activation string `json:"activation"`
|
||||
CIRAConfigName *string `json:"ciraConfigName"`
|
||||
GenerateRandomPassword bool `json:"generateRandomPassword"`
|
||||
GenerateRandomMEBxPassword bool `json:"generateRandomMEBxPassword"`
|
||||
Tags []string `json:"tags"`
|
||||
DHCPEnabled bool `json:"dhcpEnabled"`
|
||||
TenantId string `json:"tenantId"`
|
||||
WIFIConfigs []ProfileWifiConfig `json:"wifiConfigs"`
|
||||
}
|
||||
|
||||
ProfileWifiConfig struct {
|
||||
Priority int `json:"priority"`
|
||||
ProfileName string `json:"profileName"`
|
||||
}
|
||||
)
|
||||
|
||||
func (service *Service) createOrUpdateAMTProfile(configuration portainer.OpenAMTConfiguration, profileName string, ciraConfigName string, wirelessConfig string) (*Profile, error) {
|
||||
profile, err := service.getAMTProfile(configuration, profileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
method := http.MethodPost
|
||||
if profile != nil {
|
||||
method = http.MethodPatch
|
||||
}
|
||||
|
||||
profile, err = service.saveAMTProfile(method, configuration, profileName, ciraConfigName, wirelessConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
func (service *Service) getAMTProfile(configuration portainer.OpenAMTConfiguration, profileName string) (*Profile, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/profiles/%v", configuration.MPSURL, profileName)
|
||||
|
||||
responseBody, err := service.executeGetRequest(url, configuration.Credentials.MPSToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if responseBody == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result Profile
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (service *Service) saveAMTProfile(method string, configuration portainer.OpenAMTConfiguration, profileName string, ciraConfigName string, wirelessConfig string) (*Profile, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/profiles", configuration.MPSURL)
|
||||
|
||||
profile := Profile{
|
||||
ProfileName: profileName,
|
||||
Activation: "acmactivate",
|
||||
GenerateRandomPassword: true,
|
||||
GenerateRandomMEBxPassword: true,
|
||||
CIRAConfigName: &ciraConfigName,
|
||||
Tags: []string{},
|
||||
DHCPEnabled: true,
|
||||
}
|
||||
if wirelessConfig != "" {
|
||||
profile.WIFIConfigs = []ProfileWifiConfig{
|
||||
{
|
||||
Priority: 1,
|
||||
ProfileName: DefaultWirelessConfigName,
|
||||
},
|
||||
}
|
||||
}
|
||||
payload, _ := json.Marshal(profile)
|
||||
|
||||
responseBody, err := service.executeSaveRequest(method, url, configuration.Credentials.MPSToken, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result Profile
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
91
api/hostmanagement/intel/openamt/configWireless.go
Normal file
91
api/hostmanagement/intel/openamt/configWireless.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package openamt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type (
|
||||
WirelessProfile struct {
|
||||
ProfileName string `json:"profileName"`
|
||||
AuthenticationMethod int `json:"authenticationMethod"`
|
||||
EncryptionMethod int `json:"encryptionMethod"`
|
||||
SSID string `json:"ssid"`
|
||||
PSKPassphrase string `json:"pskPassphrase"`
|
||||
}
|
||||
)
|
||||
|
||||
func (service *Service) createOrUpdateWirelessConfig(configuration portainer.OpenAMTConfiguration, wirelessConfigName string) (*WirelessProfile, error) {
|
||||
wirelessConfig, err := service.getWirelessConfig(configuration, wirelessConfigName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
method := http.MethodPost
|
||||
if wirelessConfig != nil {
|
||||
method = http.MethodPatch
|
||||
}
|
||||
|
||||
wirelessConfig, err = service.saveWirelessConfig(method, configuration, wirelessConfigName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wirelessConfig, nil
|
||||
}
|
||||
|
||||
func (service *Service) getWirelessConfig(configuration portainer.OpenAMTConfiguration, configName string) (*WirelessProfile, error) {
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/wirelessconfigs/%v", configuration.MPSURL, configName)
|
||||
|
||||
responseBody, err := service.executeGetRequest(url, configuration.Credentials.MPSToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if responseBody == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result WirelessProfile
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (service *Service) saveWirelessConfig(method string, configuration portainer.OpenAMTConfiguration, configName string) (*WirelessProfile, error) {
|
||||
parsedAuthenticationMethod, err := strconv.Atoi(configuration.WirelessConfiguration.AuthenticationMethod)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing wireless authentication method: %v", err.Error())
|
||||
}
|
||||
parsedEncryptionMethod, err := strconv.Atoi(configuration.WirelessConfiguration.EncryptionMethod)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing wireless encryption method: %v", err.Error())
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("https://%v/rps/api/v1/admin/wirelessconfigs", configuration.MPSURL)
|
||||
|
||||
config := WirelessProfile{
|
||||
ProfileName: configName,
|
||||
AuthenticationMethod: parsedAuthenticationMethod,
|
||||
EncryptionMethod: parsedEncryptionMethod,
|
||||
SSID: configuration.WirelessConfiguration.SSID,
|
||||
PSKPassphrase: configuration.WirelessConfiguration.PskPass,
|
||||
}
|
||||
payload, _ := json.Marshal(config)
|
||||
|
||||
responseBody, err := service.executeSaveRequest(method, url, configuration.Credentials.MPSToken, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result WirelessProfile
|
||||
err = json.Unmarshal(responseBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
148
api/hostmanagement/intel/openamt/openamt.go
Normal file
148
api/hostmanagement/intel/openamt/openamt.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package openamt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultCIRAConfigName = "ciraConfigDefault"
|
||||
DefaultWirelessConfigName = "wirelessProfileDefault"
|
||||
DefaultProfileName = "profileAMTDefault"
|
||||
DefaultDomainName = "domainDefault"
|
||||
)
|
||||
|
||||
// Service represents a service for managing an OpenAMT server.
|
||||
type Service struct {
|
||||
httpsClient *http.Client
|
||||
}
|
||||
|
||||
// NewService initializes a new service.
|
||||
func NewService() *Service {
|
||||
return &Service{
|
||||
httpsClient: &http.Client{
|
||||
Timeout: time.Second * time.Duration(5),
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type openAMTError struct {
|
||||
ErrorMsg string `json:"message"`
|
||||
Errors []struct {
|
||||
ErrorMsg string `json:"msg"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
func parseError(responseBody []byte) error {
|
||||
var errorResponse openAMTError
|
||||
err := json.Unmarshal(responseBody, &errorResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(errorResponse.Errors) > 0 {
|
||||
return errors.New(errorResponse.Errors[0].ErrorMsg)
|
||||
}
|
||||
if errorResponse.ErrorMsg != "" {
|
||||
return errors.New(errorResponse.ErrorMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) ConfigureDefault(configuration portainer.OpenAMTConfiguration) error {
|
||||
token, err := service.executeAuthenticationRequest(configuration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configuration.Credentials.MPSToken = token.Token
|
||||
|
||||
ciraConfig, err := service.createOrUpdateCIRAConfig(configuration, DefaultCIRAConfigName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wirelessConfigName := ""
|
||||
if configuration.WirelessConfiguration.UseWirelessConfig {
|
||||
wirelessConfig, err := service.createOrUpdateWirelessConfig(configuration, DefaultWirelessConfigName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wirelessConfigName = wirelessConfig.ProfileName
|
||||
}
|
||||
|
||||
_, err = service.createOrUpdateAMTProfile(configuration, DefaultProfileName, ciraConfig.ConfigName, wirelessConfigName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = service.createOrUpdateDomain(configuration, DefaultDomainName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) executeSaveRequest(method string, url string, token string, payload []byte) ([]byte, error) {
|
||||
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
|
||||
|
||||
response, err := service.httpsClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseBody, readErr := ioutil.ReadAll(response.Body)
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
|
||||
if response.StatusCode < 200 || response.StatusCode > 300 {
|
||||
errorResponse := parseError(responseBody)
|
||||
if errorResponse != nil {
|
||||
return nil, errorResponse
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("unexpected status code %v", response.Status))
|
||||
}
|
||||
|
||||
return responseBody, nil
|
||||
}
|
||||
|
||||
func (service *Service) executeGetRequest(url string, token string) ([]byte, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
|
||||
|
||||
response, err := service.httpsClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseBody, readErr := ioutil.ReadAll(response.Body)
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
|
||||
if response.StatusCode < 200 || response.StatusCode > 300 {
|
||||
if response.StatusCode == http.StatusNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
errorResponse := parseError(responseBody)
|
||||
if errorResponse != nil {
|
||||
return nil, errorResponse
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("unexpected status code %v", response.Status))
|
||||
}
|
||||
|
||||
return responseBody, nil
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/portainer/portainer/api/http/handler/endpoints"
|
||||
"github.com/portainer/portainer/api/http/handler/file"
|
||||
"github.com/portainer/portainer/api/http/handler/helm"
|
||||
"github.com/portainer/portainer/api/http/handler/hostmanagement/intel"
|
||||
"github.com/portainer/portainer/api/http/handler/kubernetes"
|
||||
"github.com/portainer/portainer/api/http/handler/ldap"
|
||||
"github.com/portainer/portainer/api/http/handler/motd"
|
||||
@@ -62,6 +63,7 @@ type Handler struct {
|
||||
RoleHandler *roles.Handler
|
||||
SettingsHandler *settings.Handler
|
||||
SSLHandler *ssl.Handler
|
||||
IntelHandler *intel.Handler
|
||||
StackHandler *stacks.Handler
|
||||
StatusHandler *status.Handler
|
||||
StorybookHandler *storybook.Handler
|
||||
@@ -221,6 +223,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
http.StripPrefix("/api", h.UserHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/ssl"):
|
||||
http.StripPrefix("/api", h.SSLHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/open-amt"):
|
||||
http.StripPrefix("/api", h.IntelHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/teams"):
|
||||
http.StripPrefix("/api", h.TeamHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/team_memberships"):
|
||||
|
||||
40
api/http/handler/hostmanagement/intel/handler.go
Normal file
40
api/http/handler/hostmanagement/intel/handler.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package intel
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
)
|
||||
|
||||
// Handler is the HTTP handler used to handle Intel operations.
|
||||
type Handler struct {
|
||||
*mux.Router
|
||||
OpenAMTService portainer.OpenAMTService
|
||||
DataStore portainer.DataStore
|
||||
ProxyManager *proxy.Manager
|
||||
}
|
||||
|
||||
// NewHandler returns a new Handler
|
||||
func NewHandler(bouncer *security.RequestBouncer, dataStore portainer.DataStore) (*Handler, error) {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
}
|
||||
|
||||
settings, err := dataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
featureEnabled, _ := settings.FeatureFlagSettings[portainer.FeatOpenAMT]
|
||||
if featureEnabled {
|
||||
h.Handle("/open-amt", bouncer.AdminAccess(httperror.LoggerHandler(h.openAMTConfigureDefault))).Methods(http.MethodPost)
|
||||
h.PathPrefix("/open-amt/mps").Handler(httperror.LoggerHandler(h.mpsProxy))
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
32
api/http/handler/hostmanagement/intel/mps_proxy.go
Normal file
32
api/http/handler/hostmanagement/intel/mps_proxy.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package intel
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
)
|
||||
|
||||
const openamt_proxy = "openamt"
|
||||
|
||||
func (handler *Handler) mpsProxy(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
proxy := handler.ProxyManager.GetGenericProxy(openamt_proxy)
|
||||
if proxy == nil {
|
||||
settings, err := handler.DataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create proxy", err}
|
||||
}
|
||||
|
||||
mpsServer := settings.OpenAMTConfiguration.MPSURL
|
||||
|
||||
log.Println("mpsServer=", mpsServer)
|
||||
|
||||
proxy, err = handler.ProxyManager.CreateGenericProxy(openamt_proxy, "https://"+mpsServer)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create proxy", err}
|
||||
}
|
||||
}
|
||||
|
||||
http.StripPrefix("/open-amt/mps", proxy).ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
225
api/http/handler/hostmanagement/intel/openamt.go
Normal file
225
api/http/handler/hostmanagement/intel/openamt.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package intel
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"software.sslmate.com/src/go-pkcs12"
|
||||
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
"github.com/portainer/libhttp/request"
|
||||
"github.com/portainer/libhttp/response"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type openAMTConfigureDefaultPayload struct {
|
||||
EnableOpenAMT bool
|
||||
MPSURL string
|
||||
MPSUser string
|
||||
MPSPassword string
|
||||
CertFileText string
|
||||
CertPassword string
|
||||
DomainName string
|
||||
UseWirelessConfig bool
|
||||
WifiAuthenticationMethod string
|
||||
WifiEncryptionMethod string
|
||||
WifiSSID string
|
||||
WifiPskPass string
|
||||
}
|
||||
|
||||
func (payload *openAMTConfigureDefaultPayload) Validate(r *http.Request) error {
|
||||
if payload.EnableOpenAMT {
|
||||
if payload.MPSURL == "" {
|
||||
return errors.New("MPS Url must be provided")
|
||||
}
|
||||
if payload.MPSUser == "" {
|
||||
return errors.New("MPS User must be provided")
|
||||
}
|
||||
if payload.MPSPassword == "" {
|
||||
return errors.New("MPS Password must be provided")
|
||||
}
|
||||
if payload.DomainName == "" {
|
||||
return errors.New("domain name must be provided")
|
||||
}
|
||||
if payload.CertFileText == "" {
|
||||
return errors.New("certificate file must be provided")
|
||||
}
|
||||
if payload.CertPassword == "" {
|
||||
return errors.New("certificate password must be provided")
|
||||
}
|
||||
if payload.UseWirelessConfig {
|
||||
if payload.WifiAuthenticationMethod == "" {
|
||||
return errors.New("wireless authentication method must be provided")
|
||||
}
|
||||
if payload.WifiEncryptionMethod == "" {
|
||||
return errors.New("wireless encryption method must be provided")
|
||||
}
|
||||
if payload.WifiSSID == "" {
|
||||
return errors.New("wireless config SSID must be provided")
|
||||
}
|
||||
if payload.WifiPskPass == "" {
|
||||
return errors.New("wireless config PSK passphrase must be provided")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// @id OpenAMTConfigureDefault
|
||||
// @summary Enable OpenAMT capabilities
|
||||
// @description Enable OpenAMT capabilities
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @accept json
|
||||
// @produce json
|
||||
// @param body body openAMTConfigureDefaultPayload true "OpenAMT Settings"
|
||||
// @success 204 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied to access settings"
|
||||
// @failure 500 "Server error"
|
||||
// @router /open-amt [post]
|
||||
func (handler *Handler) openAMTConfigureDefault(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload openAMTConfigureDefaultPayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Invalid request payload")
|
||||
return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid request payload", Err: err}
|
||||
}
|
||||
|
||||
if payload.EnableOpenAMT {
|
||||
certificateErr := validateCertificate(payload.CertFileText, payload.CertPassword)
|
||||
if certificateErr != nil {
|
||||
return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Error validating certificate", Err: certificateErr}
|
||||
}
|
||||
|
||||
logrus.Printf("Payload: %#v", payload)
|
||||
|
||||
err = handler.enableOpenAMT(payload)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Error enabling OpenAMT", Err: err}
|
||||
}
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
||||
|
||||
err = handler.disableOpenAMT()
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Error disabling OpenAMT", Err: err}
|
||||
}
|
||||
return response.Empty(w)
|
||||
}
|
||||
|
||||
func validateCertificate(certificateRaw string, certificatePassword string) error {
|
||||
certificateData, err := base64.StdEncoding.Strict().DecodeString(certificateRaw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, certificate, _, err := pkcs12.DecodeChain(certificateData, certificatePassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if certificate == nil {
|
||||
return errors.New("certificate could not be decoded")
|
||||
}
|
||||
|
||||
issuer := certificate.Issuer.CommonName
|
||||
if !isValidIssuer(issuer) {
|
||||
return fmt.Errorf("certificate issuer is invalid: %v", issuer)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidIssuer(issuer string) bool {
|
||||
formattedIssuer := strings.ToLower(strings.ReplaceAll(issuer, " ", ""))
|
||||
return strings.Contains(formattedIssuer, "comodo") ||
|
||||
strings.Contains(formattedIssuer, "digicert") ||
|
||||
strings.Contains(formattedIssuer, "entrust") ||
|
||||
strings.Contains(formattedIssuer, "godaddy")
|
||||
}
|
||||
|
||||
func (handler *Handler) enableOpenAMT(configurationPayload openAMTConfigureDefaultPayload) error {
|
||||
configuration := portainer.OpenAMTConfiguration{
|
||||
Enabled: true,
|
||||
MPSURL: configurationPayload.MPSURL,
|
||||
Credentials: portainer.MPSCredentials{
|
||||
MPSUser: configurationPayload.MPSUser,
|
||||
MPSPassword: configurationPayload.MPSPassword,
|
||||
},
|
||||
DomainConfiguration: portainer.DomainConfiguration{
|
||||
CertFileText: configurationPayload.CertFileText,
|
||||
CertPassword: configurationPayload.CertPassword,
|
||||
DomainName: configurationPayload.DomainName,
|
||||
},
|
||||
WirelessConfiguration: portainer.WirelessConfiguration{
|
||||
UseWirelessConfig: configurationPayload.UseWirelessConfig,
|
||||
AuthenticationMethod: configurationPayload.WifiAuthenticationMethod,
|
||||
EncryptionMethod: configurationPayload.WifiEncryptionMethod,
|
||||
SSID: configurationPayload.WifiSSID,
|
||||
PskPass: configurationPayload.WifiPskPass,
|
||||
},
|
||||
}
|
||||
|
||||
err := handler.OpenAMTService.ConfigureDefault(configuration)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("error configuring OpenAMT server")
|
||||
return err
|
||||
}
|
||||
|
||||
err = handler.saveConfiguration(configuration)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("error updating OpenAMT configurations")
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Info("OpenAMT successfully enabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *Handler) saveConfiguration(configuration portainer.OpenAMTConfiguration) error {
|
||||
settings, err := handler.DataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configuration.Credentials.MPSPassword = ""
|
||||
configuration.Credentials.MPSToken = ""
|
||||
configuration.DomainConfiguration.CertFileText = ""
|
||||
configuration.DomainConfiguration.CertPassword = ""
|
||||
configuration.WirelessConfiguration.PskPass = ""
|
||||
|
||||
logrus.Info("saving...")
|
||||
|
||||
settings.OpenAMTConfiguration = configuration
|
||||
err = handler.DataStore.Settings().UpdateSettings(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *Handler) disableOpenAMT() error {
|
||||
settings, err := handler.DataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.OpenAMTConfiguration.Enabled = false
|
||||
|
||||
err = handler.DataStore.Settings().UpdateSettings(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Info("OpenAMT successfully disabled")
|
||||
return nil
|
||||
}
|
||||
@@ -63,6 +63,17 @@ func (factory *ProxyFactory) NewEndpointProxy(endpoint *portainer.Endpoint) (htt
|
||||
return factory.newDockerProxy(endpoint)
|
||||
}
|
||||
|
||||
// NewGenericProxy returns a generic reverse proxy with no special handling
|
||||
func (factory *ProxyFactory) NewGenericProxy(uri string) (http.Handler, error) {
|
||||
origin, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newSingleHostReverseProxyWithHostHeader(origin), nil
|
||||
//return httputil.NewSingleHostReverseProxy(origin), nil
|
||||
}
|
||||
|
||||
// NewGitlabProxy returns a new HTTP proxy to a Gitlab API server
|
||||
func (factory *ProxyFactory) NewGitlabProxy(gitlabAPIUri string) (http.Handler, error) {
|
||||
return newGitlabProxy(gitlabAPIUri)
|
||||
|
||||
@@ -22,6 +22,7 @@ type (
|
||||
proxyFactory *factory.ProxyFactory
|
||||
endpointProxies cmap.ConcurrentMap
|
||||
legacyExtensionProxies cmap.ConcurrentMap
|
||||
genericProxies cmap.ConcurrentMap
|
||||
k8sClientFactory *cli.ClientFactory
|
||||
}
|
||||
)
|
||||
@@ -31,6 +32,7 @@ func NewManager(dataStore portainer.DataStore, signatureService portainer.Digita
|
||||
return &Manager{
|
||||
endpointProxies: cmap.New(),
|
||||
legacyExtensionProxies: cmap.New(),
|
||||
genericProxies: cmap.New(),
|
||||
k8sClientFactory: kubernetesClientFactory,
|
||||
proxyFactory: factory.NewProxyFactory(dataStore, signatureService, tunnelService, clientFactory, kubernetesClientFactory, kubernetesTokenCacheManager),
|
||||
}
|
||||
@@ -92,6 +94,27 @@ func (manager *Manager) GetLegacyExtensionProxy(key string) http.Handler {
|
||||
return proxy.(http.Handler)
|
||||
}
|
||||
|
||||
// CreateGenericProxy creates a new HTTP universal reverse proxy with a given key
|
||||
func (manager *Manager) CreateGenericProxy(key, uri string) (http.Handler, error) {
|
||||
proxy, err := manager.proxyFactory.NewGenericProxy(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager.genericProxies.Set(key, proxy)
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// GetGenericProxy returns the universal proxy associated with the given key
|
||||
func (manager *Manager) GetGenericProxy(key string) http.Handler {
|
||||
proxy, ok := manager.genericProxies.Get(key)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return proxy.(http.Handler)
|
||||
}
|
||||
|
||||
// CreateGitlabProxy creates a new HTTP reverse proxy that can be used to send requests to the Gitlab API
|
||||
func (manager *Manager) CreateGitlabProxy(url string) (http.Handler, error) {
|
||||
return manager.proxyFactory.NewGitlabProxy(url)
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/portainer/portainer/api/http/handler/endpoints"
|
||||
"github.com/portainer/portainer/api/http/handler/file"
|
||||
"github.com/portainer/portainer/api/http/handler/helm"
|
||||
"github.com/portainer/portainer/api/http/handler/hostmanagement/intel"
|
||||
kubehandler "github.com/portainer/portainer/api/http/handler/kubernetes"
|
||||
"github.com/portainer/portainer/api/http/handler/ldap"
|
||||
"github.com/portainer/portainer/api/http/handler/motd"
|
||||
@@ -75,6 +76,7 @@ type Server struct {
|
||||
FileService portainer.FileService
|
||||
DataStore portainer.DataStore
|
||||
GitService portainer.GitService
|
||||
OpenAMTService portainer.OpenAMTService
|
||||
JWTService portainer.JWTService
|
||||
LDAPService portainer.LDAPService
|
||||
OAuthService portainer.OAuthService
|
||||
@@ -203,6 +205,14 @@ func (server *Server) Start() error {
|
||||
var sslHandler = sslhandler.NewHandler(requestBouncer)
|
||||
sslHandler.SSLService = server.SSLService
|
||||
|
||||
intelHandler, err := intel.NewHandler(requestBouncer, server.DataStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
intelHandler.OpenAMTService = server.OpenAMTService
|
||||
intelHandler.DataStore = server.DataStore
|
||||
intelHandler.ProxyManager = server.ProxyManager
|
||||
|
||||
var stackHandler = stacks.NewHandler(requestBouncer)
|
||||
stackHandler.DataStore = server.DataStore
|
||||
stackHandler.DockerClientFactory = server.DockerClientFactory
|
||||
@@ -268,6 +278,7 @@ func (server *Server) Start() error {
|
||||
HelmTemplatesHandler: helmTemplatesHandler,
|
||||
KubernetesHandler: kubernetesHandler,
|
||||
MOTDHandler: motdHandler,
|
||||
IntelHandler: intelHandler,
|
||||
RegistryHandler: registryHandler,
|
||||
ResourceControlHandler: resourceControlHandler,
|
||||
SettingsHandler: settingsHandler,
|
||||
|
||||
@@ -40,6 +40,35 @@ type (
|
||||
AuthenticationKey string `json:"AuthenticationKey" example:"cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="`
|
||||
}
|
||||
|
||||
// OpenAMTConfiguration represents the credentials and configurations used to connect to an OpenAMT MPS server
|
||||
OpenAMTConfiguration struct {
|
||||
Enabled bool `json:"Enabled"`
|
||||
MPSURL string `json:"MPSURL"`
|
||||
Credentials MPSCredentials `json:"Credentials"`
|
||||
DomainConfiguration DomainConfiguration `json:"DomainConfiguration"`
|
||||
WirelessConfiguration WirelessConfiguration `json:"WirelessConfiguration"`
|
||||
}
|
||||
|
||||
MPSCredentials struct {
|
||||
MPSUser string `json:"MPSUser"`
|
||||
MPSPassword string `json:"MPSPassword"`
|
||||
MPSToken string `json:"MPSToken"` // retrieved from API
|
||||
}
|
||||
|
||||
DomainConfiguration struct {
|
||||
CertFileText string `json:"CertFileText"`
|
||||
CertPassword string `json:"CertPassword"`
|
||||
DomainName string `json:"DomainName"`
|
||||
}
|
||||
|
||||
WirelessConfiguration struct {
|
||||
UseWirelessConfig bool `json:"UseWirelessConfig"`
|
||||
AuthenticationMethod string `json:"AuthenticationMethod"`
|
||||
EncryptionMethod string `json:"EncryptionMethod"`
|
||||
SSID string `json:"SSID"`
|
||||
PskPass string `json:"PskPass"`
|
||||
}
|
||||
|
||||
// CLIFlags represents the available flags on the CLI
|
||||
CLIFlags struct {
|
||||
Addr *string
|
||||
@@ -708,6 +737,7 @@ type (
|
||||
AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod" example:"1"`
|
||||
LDAPSettings LDAPSettings `json:"LDAPSettings" example:""`
|
||||
OAuthSettings OAuthSettings `json:"OAuthSettings" example:""`
|
||||
OpenAMTConfiguration OpenAMTConfiguration `json:"OpenAMTConfiguration" example:""`
|
||||
FeatureFlagSettings map[Feature]bool `json:"FeatureFlagSettings" example:""`
|
||||
// The interval in which environment(endpoint) snapshots are created
|
||||
SnapshotInterval string `json:"SnapshotInterval" example:"5m"`
|
||||
@@ -1257,6 +1287,11 @@ type (
|
||||
LatestCommitID(repositoryURL, referenceName, username, password string) (string, error)
|
||||
}
|
||||
|
||||
// OpenAMTService represents a service for managing OpenAMT
|
||||
OpenAMTService interface {
|
||||
ConfigureDefault(configuration OpenAMTConfiguration) error
|
||||
}
|
||||
|
||||
// HelmUserRepositoryService represents a service to manage HelmUserRepositories
|
||||
HelmUserRepositoryService interface {
|
||||
HelmUserRepositoryByUserID(userID UserID) ([]HelmUserRepository, error)
|
||||
|
||||
@@ -4,6 +4,7 @@ export function SettingsViewModel(data) {
|
||||
this.AuthenticationMethod = data.AuthenticationMethod;
|
||||
this.LDAPSettings = data.LDAPSettings;
|
||||
this.OAuthSettings = new OAuthSettingsViewModel(data.OAuthSettings);
|
||||
this.OpenAMTConfiguration = data.OpenAMTConfiguration;
|
||||
this.SnapshotInterval = data.SnapshotInterval;
|
||||
this.TemplatesURL = data.TemplatesURL;
|
||||
this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval;
|
||||
|
||||
16
app/portainer/rest/openAMT.js
Normal file
16
app/portainer/rest/openAMT.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import angular from 'angular';
|
||||
|
||||
const API_ENDPOINT_OPEN_AMT = 'api/open-amt';
|
||||
|
||||
angular.module('portainer.app').factory('OpenAMT', OpenAMTFactory);
|
||||
|
||||
/* @ngInject */
|
||||
function OpenAMTFactory($resource) {
|
||||
return $resource(
|
||||
API_ENDPOINT_OPEN_AMT,
|
||||
{},
|
||||
{
|
||||
submit: { method: 'POST' },
|
||||
}
|
||||
);
|
||||
}
|
||||
14
app/portainer/services/api/openAMTService.js
Normal file
14
app/portainer/services/api/openAMTService.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import angular from 'angular';
|
||||
|
||||
angular.module('portainer.app').service('OpenAMTService', OpenAMTServiceFactory);
|
||||
|
||||
/* @ngInject */
|
||||
function OpenAMTServiceFactory(OpenAMT) {
|
||||
return {
|
||||
submit,
|
||||
};
|
||||
|
||||
function submit(formValues) {
|
||||
return OpenAMT.submit(formValues).$promise;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import { sslCertificate } from './ssl-certificate';
|
||||
import { openAMT } from './open-amt';
|
||||
|
||||
export default angular.module('portainer.settings.general', []).component('sslCertificateSettings', sslCertificate).name;
|
||||
export default angular.module('portainer.settings.general', []).component('sslCertificateSettings', sslCertificate).component('openAmtSettings', openAMT).name;
|
||||
|
||||
6
app/portainer/settings/general/open-amt/index.js
Normal file
6
app/portainer/settings/general/open-amt/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import controller from './open-amt.controller.js';
|
||||
|
||||
export const openAMT = {
|
||||
templateUrl: './open-amt.html',
|
||||
controller,
|
||||
};
|
||||
102
app/portainer/settings/general/open-amt/open-amt.controller.js
Normal file
102
app/portainer/settings/general/open-amt/open-amt.controller.js
Normal file
@@ -0,0 +1,102 @@
|
||||
class OpenAmtController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, OpenAMTService, SettingsService, Notifications) {
|
||||
Object.assign(this, { $async, $state, OpenAMTService, SettingsService, Notifications });
|
||||
|
||||
this.originalValues = {};
|
||||
this.formValues = {
|
||||
enableOpenAMT: false,
|
||||
mpsURL: '',
|
||||
mpsUser: '',
|
||||
mpsPassword: '',
|
||||
domainName: '',
|
||||
certFile: null,
|
||||
certPassword: '',
|
||||
useWirelessConfig: false,
|
||||
wifiAuthenticationMethod: '4',
|
||||
wifiEncryptionMethod: '3',
|
||||
wifiSsid: '',
|
||||
wifiPskPass: '',
|
||||
};
|
||||
|
||||
this.state = {
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
this.save = this.save.bind(this);
|
||||
}
|
||||
|
||||
isFormChanged() {
|
||||
return Object.entries(this.originalValues).some(([key, value]) => value !== this.formValues[key]);
|
||||
}
|
||||
|
||||
isFormValid() {
|
||||
return !this.formValues.enableOpenAMT || this.formValues.certFile != null;
|
||||
}
|
||||
|
||||
async readFile() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const file = this.formValues.certFile;
|
||||
if (file) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.fileName = file.name;
|
||||
fileReader.onload = (e) => {
|
||||
const base64 = e.target.result;
|
||||
// remove prefix of "data:application/x-pkcs12;base64," returned by "readAsDataURL()"
|
||||
const index = base64.indexOf('base64,');
|
||||
const cert = base64.substring(index + 7, base64.length);
|
||||
resolve(cert);
|
||||
};
|
||||
fileReader.onerror = () => {
|
||||
reject(new Error('error reading provisioning certificate file'));
|
||||
};
|
||||
fileReader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async save() {
|
||||
return this.$async(async () => {
|
||||
this.state.actionInProgress = true;
|
||||
try {
|
||||
this.formValues.certFileText = this.formValues.certFile ? await this.readFile(this.formValues.certFile) : null;
|
||||
await this.OpenAMTService.submit(this.formValues);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
this.Notifications.success(`OpenAMT successfully ${this.formValues.enableOpenAMT ? 'enabled' : 'disabled'}`);
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Failed applying changes');
|
||||
}
|
||||
this.state.actionInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
async $onInit() {
|
||||
return this.$async(async () => {
|
||||
try {
|
||||
const data = await this.SettingsService.settings();
|
||||
const config = data.OpenAMTConfiguration;
|
||||
|
||||
this.formValues = {
|
||||
...this.formValues,
|
||||
enableOpenAMT: config.Enabled,
|
||||
mpsURL: config.MPSURL,
|
||||
mpsUser: config.Credentials.MPSUser,
|
||||
domainName: config.DomainConfiguration.DomainName,
|
||||
useWirelessConfig: config.WirelessConfiguration.UseWirelessConfig,
|
||||
wifiAuthenticationMethod: config.WirelessConfiguration.AuthenticationMethod,
|
||||
wifiEncryptionMethod: config.WirelessConfiguration.EncryptionMethod,
|
||||
wifiSsid: config.WirelessConfiguration.SSID,
|
||||
};
|
||||
|
||||
this.originalValues = {
|
||||
...this.formValues,
|
||||
};
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Failed loading settings');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenAmtController;
|
||||
259
app/portainer/settings/general/open-amt/open-amt.html
Normal file
259
app/portainer/settings/general/open-amt/open-amt.html
Normal file
@@ -0,0 +1,259 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-laptop" title-text="Intel OpenAMT"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" name="openAMTForm">
|
||||
<por-switch-field ng-model="$ctrl.formValues.enableOpenAMT" label="Enable edge OpenAMT"></por-switch-field>
|
||||
<span class="small">
|
||||
<p class="text-muted" style="margin-top: 10px;">
|
||||
<i class="fa fa-exclamation-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
When enabled, this will allow Portainer to interact with an OpenAMT MPS API.
|
||||
</p>
|
||||
</span>
|
||||
|
||||
<div ng-show="$ctrl.formValues.enableOpenAMT">
|
||||
<hr />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mps_url" class="col-sm-3 control-label text-left">
|
||||
MPS URL
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.mpsURL"
|
||||
id="mps_url"
|
||||
name="mps_url"
|
||||
placeholder="Enter the MPS URL"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="openAMTForm.mps_url.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="openAMTForm.mps_url.$error">
|
||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mps_user" class="col-sm-3 control-label text-left">
|
||||
MPS User
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.mpsUser"
|
||||
id="mps_user"
|
||||
name="mps_user"
|
||||
placeholder="Enter the MPS User"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="openAMTForm.mps_user.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="openAMTForm.mps_user.$error">
|
||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mps_user" class="col-sm-3 control-label text-left">
|
||||
MPS Password
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="Needs to be 8-32 characters including one uppercase, one lowercase letters, one base-10 digit and one special character."
|
||||
></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.mpsPassword"
|
||||
id="mps_password"
|
||||
name="mps_password"
|
||||
placeholder="Enter the MPS Password"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="openAMTForm.mps_password.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="openAMTForm.mps_password.$error">
|
||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="form-group">
|
||||
<label for="domain_name" class="col-sm-3 control-label text-left">
|
||||
Domain Name
|
||||
<portainer-tooltip position="bottom" message="Enter the FQDN that is associated with the provisioning certificate (i.e amtdomain.com)"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.domainName"
|
||||
id="domain_name"
|
||||
name="domain_name"
|
||||
placeholder="Enter the Domain Name"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="openAMTForm.domain_name.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="openAMTForm.domain_name.$error">
|
||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="certificate_file" class="col-sm-3 control-label text-left">
|
||||
Provisioning Certificate File (.pfx)
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="Supported CAs are Comodo, DigiCert, Entrust and GoDaddy. The certificate must contain the private key."
|
||||
></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<button style="margin-left: 0px !important;" class="btn btn-sm btn-primary" ngf-select ng-model="$ctrl.formValues.certFile" ngf-pattern=".pfx" name="certFile">
|
||||
Upload file
|
||||
</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ $ctrl.formValues.certFile.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!$ctrl.formValues.certFile" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="openAMTForm.certFile.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="openAMTForm.certFile.$error">
|
||||
<p ng-message="pattern"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> File type is invalid.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="certificate_password" class="col-sm-3 control-label text-left">
|
||||
Provisioning Certificate Password
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="Needs to be 8-32 characters including one uppercase, one lowercase letters, one base-10 digit and one special character."
|
||||
></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.certPassword"
|
||||
id="certificate_password"
|
||||
name="certificate_password"
|
||||
placeholder="**********"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12" ng-show="openAMTForm.certificate_password.$invalid">
|
||||
<div class="small text-warning">
|
||||
<div ng-messages="openAMTForm.certificate_password.$error">
|
||||
<p ng-message="required"> <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<por-switch-field ng-model="$ctrl.formValues.useWirelessConfig" label="Wireless Configuration"></por-switch-field>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="$ctrl.formValues.useWirelessConfig">
|
||||
<div class="form-group">
|
||||
<label for="wifi_auth_method" class="col-sm-3 control-label text-left">
|
||||
Authentication Method
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" ng-model="$ctrl.formValues.wifiAuthenticationMethod">
|
||||
<option value="4">WPA PSK</option>
|
||||
<option value="6">WPA2 PSK</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wifi_encrypt_method" class="col-sm-3 control-label text-left">
|
||||
Encryption Method
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" ng-model="$ctrl.formValues.wifiEncryptionMethod" id="wifi_encrypt_method">
|
||||
<option value="3">TKIP</option>
|
||||
<option value="4">CCMP</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wifi_ssid" class="col-sm-3 control-label text-left">
|
||||
SSID
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.wifiSsid"
|
||||
id="wifi_ssid"
|
||||
placeholder="SSIID"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT && $ctrl.formValues.useWirelessConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wifi_pass" class="col-sm-3 control-label text-left">
|
||||
PSK Passphrase
|
||||
<portainer-tooltip position="bottom" message="PSK Passphrase length should be greater than or equal to 8 and less than or equal to 63"></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
ng-model="$ctrl.formValues.wifiPskPass"
|
||||
id="wifi_pass"
|
||||
placeholder="******"
|
||||
ng-required="$ctrl.formValues.enableOpenAMT && $ctrl.formValues.useWirelessConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="$ctrl.state.actionInProgress || !openAMTForm.$valid || !$ctrl.isFormChanged() || !$ctrl.isFormValid()"
|
||||
ng-click="$ctrl.save()"
|
||||
button-spinner="$ctrl.state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="$ctrl.state.actionInProgress">Save Settings</span>
|
||||
<span ng-show="$ctrl.state.actionInProgress">In progress...</span>
|
||||
</button>
|
||||
<span class="text-danger" ng-if="state.formValidationError" style="margin-left: 5px;">{{ state.formValidationError }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,6 +182,8 @@
|
||||
|
||||
<ssl-certificate-settings></ssl-certificate-settings>
|
||||
|
||||
<open-amt-settings ng-show="settings.FeatureFlagSettings && settings.FeatureFlagSettings['open-amt']"></open-amt-settings>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<rd-widget>
|
||||
|
||||
21
yarn.lock
21
yarn.lock
@@ -1838,7 +1838,7 @@
|
||||
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz"
|
||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||
|
||||
"@eslint/eslintrc@^0.4.3":
|
||||
"@eslint/eslintrc@^0.4.2":
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
|
||||
integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
|
||||
@@ -1863,20 +1863,6 @@
|
||||
resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz"
|
||||
integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
|
||||
|
||||
"@humanwhocodes/config-array@^0.5.0":
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
|
||||
integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
|
||||
dependencies:
|
||||
"@humanwhocodes/object-schema" "^1.2.0"
|
||||
debug "^4.1.1"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
"@humanwhocodes/object-schema@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
|
||||
integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz"
|
||||
@@ -3957,7 +3943,6 @@ angular-moment-picker@^0.10.2:
|
||||
dependencies:
|
||||
angular-mocks "1.6.1"
|
||||
angular-sanitize "1.6.1"
|
||||
lodash-es "^4.17.15"
|
||||
|
||||
angular-resource@1.8.0:
|
||||
version "1.8.0"
|
||||
@@ -6368,7 +6353,7 @@ cssesc@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
cssnano-preset-default@^4.0.8:
|
||||
cssnano-preset-default@^4.0.7:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff"
|
||||
integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==
|
||||
@@ -16358,7 +16343,7 @@ string-width@^3.0.0, string-width@^3.1.0:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^5.1.0"
|
||||
|
||||
string-width@^4.0.0, string-width@^4.2.3:
|
||||
string-width@^4.0.0:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
|
||||
Reference in New Issue
Block a user