Added integration and unit tests
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package saml
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_TemplateProvider(t *testing.T) {
|
||||
var (
|
||||
tcc = []struct {
|
||||
name string
|
||||
url string
|
||||
expect templateProvider
|
||||
}{
|
||||
{
|
||||
name: "Default SAML provider",
|
||||
url: "http://example.tld",
|
||||
expect: templateProvider{
|
||||
Label: "Default SAML provider",
|
||||
Handle: "saml/init",
|
||||
Icon: "key",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
url: "http://example.tld",
|
||||
expect: templateProvider{
|
||||
Label: "http://example.tld",
|
||||
Handle: "saml/init",
|
||||
Icon: "key",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
req.Equal(tc.expect, TemplateProvider(tc.url, tc.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package saml
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSAMLEmailPayload = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
defaultSamlTestEmailPayload = "urn:oasis:names:tc:SAML:attribute:subject-id"
|
||||
)
|
||||
|
||||
func Test_guessIdentifier(t *testing.T) {
|
||||
var (
|
||||
sp = SamlSPService{
|
||||
IDPUserMeta: &IdpIdentityPayload{
|
||||
Name: "name_field",
|
||||
Handle: "handle_field",
|
||||
Identifier: "identifier_field",
|
||||
},
|
||||
}
|
||||
|
||||
tcc = []struct {
|
||||
name string
|
||||
payload map[string][]string
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "default email",
|
||||
payload: map[string][]string{"email": {"test@example.com"}},
|
||||
expect: "test@example.com",
|
||||
},
|
||||
{
|
||||
name: "samltest.id email payload",
|
||||
payload: map[string][]string{defaultSamlTestEmailPayload: {"test@example.com"}},
|
||||
expect: "test@example.com",
|
||||
},
|
||||
{
|
||||
name: "default SAML emailAddress payload",
|
||||
payload: map[string][]string{defaultSAMLEmailPayload: {"test@example.com"}},
|
||||
expect: "test@example.com",
|
||||
},
|
||||
{
|
||||
name: "emailAddress payload",
|
||||
payload: map[string][]string{"emailAddress": {"test@example.com"}},
|
||||
expect: "test@example.com",
|
||||
},
|
||||
{
|
||||
name: "missing email payload",
|
||||
payload: map[string][]string{"non-existing-identifier": {"test@example.com"}},
|
||||
expect: "",
|
||||
},
|
||||
{
|
||||
name: "default payload as set in settings",
|
||||
payload: map[string][]string{"identifier_field": {"test@example.com"}},
|
||||
expect: "test@example.com",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
req.Equal(tc.expect, sp.GuessIdentifier(tc.payload))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza-server/pkg/id"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
@@ -12,8 +15,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Mock auth service with nil for current time, dummy provider validator and mock db
|
||||
@@ -80,23 +81,23 @@ func TestAuth_External(t *testing.T) {
|
||||
|
||||
cases = []struct {
|
||||
name string
|
||||
profile goth.User
|
||||
profile types.ExternalAuthUser
|
||||
user *types.User
|
||||
err error
|
||||
}{
|
||||
{
|
||||
"matching by user email",
|
||||
goth.User{UserID: freshProfileID(), Provider: "-", Email: validUser.Email},
|
||||
types.ExternalAuthUser{goth.User{UserID: freshProfileID(), Provider: "-", Email: validUser.Email}},
|
||||
validUser,
|
||||
nil},
|
||||
{
|
||||
"unknown profile",
|
||||
goth.User{UserID: freshProfileID(), Provider: "-", Email: "fresh-from-foo@test.cortezaproject.org"},
|
||||
types.ExternalAuthUser{goth.User{UserID: freshProfileID(), Provider: "-", Email: "fresh-from-foo@test.cortezaproject.org"}},
|
||||
&types.User{Email: "fresh-from-foo@test.cortezaproject.org"},
|
||||
nil},
|
||||
{
|
||||
"profile match by provider ID",
|
||||
goth.User{UserID: fooCredentials.Credentials, Provider: fooCredentials.Kind, Email: "valid+2nd+email@test.cortezaproject.org"},
|
||||
types.ExternalAuthUser{goth.User{UserID: fooCredentials.Credentials, Provider: fooCredentials.Kind, Email: "valid+2nd+email@test.cortezaproject.org"}},
|
||||
validUser,
|
||||
nil},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/handlers"
|
||||
"github.com/cortezaproject/corteza-server/auth/saml"
|
||||
"github.com/cortezaproject/corteza-server/system/service"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
s "github.com/crewjam/saml"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/steinfletcher/apitest"
|
||||
)
|
||||
|
||||
func loadSAMLService(ctx context.Context) (srvc *saml.SamlSPService, err error) {
|
||||
links := handlers.GetLinks()
|
||||
|
||||
certManager := saml.NewCertManager(&saml.CertStoreLoader{})
|
||||
|
||||
cert, err := certManager.Parse(
|
||||
readStaticFile("static/spCert.cert"),
|
||||
readStaticFile("static/spCert.key"))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
idpUrl, err := url.Parse("")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// idp metadata needs to be loaded before
|
||||
// the internal samlsp package
|
||||
md, err := samlsp.ParseMetadata(readStaticFile("static/idp_metadata.xml"))
|
||||
ru, err := url.Parse("http://localhost:8084")
|
||||
|
||||
rootURL := &url.URL{
|
||||
Scheme: ru.Scheme,
|
||||
User: ru.User,
|
||||
Host: ru.Host,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
srvc, err = saml.NewSamlSPService(saml.SamlSPArgs{
|
||||
AcsURL: links.SamlCallback,
|
||||
MetaURL: links.SamlMetadata,
|
||||
SloURL: links.SamlLogout,
|
||||
|
||||
IdpURL: *idpUrl,
|
||||
Host: *rootURL,
|
||||
|
||||
Cert: cert,
|
||||
IdpMeta: md,
|
||||
})
|
||||
|
||||
srvc.Handler().ServiceProvider.AllowIDPInitiated = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestAuthExternalSAMLSuccess(t *testing.T) {
|
||||
var (
|
||||
h = newHelper(t)
|
||||
|
||||
cookieSessionIDPtoSP = apitest.
|
||||
NewCookie("saml_tCu5PV6EgxcvUAa9e57uJ2g-bTkqnNkyyHHaOu15yEfZjgWKt02AtXGe").
|
||||
Value(string(readStaticFile("static/idp_to_sp.cookie")))
|
||||
|
||||
cookieTokenIDPtoSPAfterLogin = apitest.
|
||||
NewCookie("token").
|
||||
Value(string(readStaticFile("static/idp_to_sp_token.cookie")))
|
||||
)
|
||||
|
||||
s.MaxClockSkew = time.Hour
|
||||
s.MaxIssueDelay = time.Hour
|
||||
|
||||
jwt.TimeFunc = func() time.Time {
|
||||
tm, _ := time.Parse("2006-01-2 15:04:05", "2021-05-17 09:17:10")
|
||||
return tm
|
||||
}
|
||||
|
||||
s.TimeNow = func() time.Time {
|
||||
tm, _ := time.Parse("2006-01-2 15:04:05", "2021-05-17 09:17:10")
|
||||
return tm
|
||||
}
|
||||
|
||||
// first step, there is no session cookie, redirect to idp
|
||||
// in this case, host from parsed metadata
|
||||
t.Log("start login process")
|
||||
|
||||
h.apiInit().
|
||||
Get(handlers.GetLinks().SamlInit).
|
||||
Expect(t).
|
||||
Assert(func(res *http.Response, req *http.Request) error {
|
||||
loc, _ := res.Location()
|
||||
|
||||
h.assertBody("SSO Error: saml: session not present", res.Body)
|
||||
h.a.NotEmpty(loc.Query().Get("RelayState"))
|
||||
h.a.NotEmpty(loc.Query().Get("SAMLRequest"))
|
||||
h.a.Equal(http.StatusFound, res.StatusCode)
|
||||
return nil
|
||||
}).
|
||||
End()
|
||||
|
||||
cookies := []*apitest.Cookie{}
|
||||
|
||||
// coming back from idp, posting to sp-related endpoint
|
||||
// mocking session cookie and saml response
|
||||
// if everything is ok, redirect back to SAML init
|
||||
t.Log("post from idp to sp")
|
||||
|
||||
h.apiInit().
|
||||
Post(handlers.GetLinks().SamlCallback).
|
||||
Header("Content-Type", "application/x-www-form-urlencoded").
|
||||
FormData("RelayState", "tCu5PV6EgxcvUAa9e57uJ2g-bTkqnNkyyHHaOu15yEfZjgWKt02AtXGe").
|
||||
Cookies(cookieSessionIDPtoSP).
|
||||
FormData("SAMLResponse", string(readStaticFile("static/idp_to_sp.post"))).
|
||||
Expect(t).
|
||||
Assert(func(res *http.Response, req *http.Request) error {
|
||||
loc, _ := res.Location()
|
||||
|
||||
h.a.NotNil(getSessionCookie("token", res.Cookies()...))
|
||||
h.a.Equal(http.StatusFound, res.StatusCode)
|
||||
h.a.Equal(handlers.GetLinks().SamlInit, loc.String())
|
||||
|
||||
cookies = append(cookies, cookieSessionIDPtoSP)
|
||||
return nil
|
||||
}).
|
||||
End()
|
||||
|
||||
// idp sends a token session cookie also
|
||||
cookies = append(cookies, cookieTokenIDPtoSPAfterLogin)
|
||||
|
||||
// once everything is set and the external authentication via
|
||||
// internal Corteza services is done, redirect to default path (profile)
|
||||
t.Log("redirect to profile after session is created")
|
||||
|
||||
h.apiInit().
|
||||
Get(handlers.GetLinks().SamlInit).
|
||||
Cookies(cookies...).
|
||||
Expect(t).
|
||||
Assert(func(res *http.Response, req *http.Request) error {
|
||||
loc, _ := res.Location()
|
||||
|
||||
ss, _, err := service.DefaultStore.SearchAuthSessions(context.Background(), types.AuthSessionFilter{})
|
||||
h.a.NoError(err)
|
||||
h.a.Len(ss, 1)
|
||||
|
||||
h.a.NotNil(getSessionCookie("session", res.Cookies()...))
|
||||
h.a.Equal(http.StatusSeeOther, res.StatusCode)
|
||||
h.a.Equal(handlers.GetLinks().Profile, loc.String())
|
||||
return nil
|
||||
}).
|
||||
End()
|
||||
}
|
||||
|
||||
func getSessionCookie(name string, cc ...*http.Cookie) (found *http.Cookie) {
|
||||
for _, c := range cc {
|
||||
if c.Name == name {
|
||||
found = c
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -2,15 +2,19 @@ package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/app"
|
||||
handlers "github.com/cortezaproject/corteza-server/auth/handlers"
|
||||
"github.com/cortezaproject/corteza-server/auth/request"
|
||||
"github.com/cortezaproject/corteza-server/auth/saml"
|
||||
"github.com/cortezaproject/corteza-server/pkg/api/server"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/eventbus"
|
||||
"github.com/cortezaproject/corteza-server/pkg/id"
|
||||
label "github.com/cortezaproject/corteza-server/pkg/label"
|
||||
ltype "github.com/cortezaproject/corteza-server/pkg/label/types"
|
||||
@@ -32,21 +36,25 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//go:embed static
|
||||
var mockData embed.FS
|
||||
|
||||
type (
|
||||
helper struct {
|
||||
t *testing.T
|
||||
a *require.Assertions
|
||||
t *testing.T
|
||||
a *require.Assertions
|
||||
sp *saml.SamlSPService
|
||||
|
||||
cUser *types.User
|
||||
roleID uint64
|
||||
data embed.FS
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
testApp *app.CortezaApp
|
||||
r chi.Router
|
||||
|
||||
eventBus = eventbus.New()
|
||||
hh *handlers.AuthHandlers
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -64,10 +72,14 @@ func rs(a ...int) string {
|
||||
}
|
||||
|
||||
func InitTestApp() {
|
||||
var sm *request.SessionManager
|
||||
|
||||
if testApp == nil {
|
||||
ctx := cli.Context()
|
||||
|
||||
testApp = helpers.NewIntegrationTestApp(ctx, func(app *app.CortezaApp) (err error) {
|
||||
service.CurrentSettings.Auth.External.Enabled = true
|
||||
|
||||
rbac.SetGlobal(rbac.NewTestService(zap.NewNop(), app.Store))
|
||||
service.DefaultObjectStore, err = plain.NewWithAfero(afero.NewMemMapFs(), "test")
|
||||
if err != nil {
|
||||
@@ -79,16 +91,28 @@ func InitTestApp() {
|
||||
return err
|
||||
}
|
||||
|
||||
eventbus.Set(eventBus)
|
||||
sm = request.NewSessionManager(service.DefaultStore, app.Opt.Auth, service.DefaultLogger)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
sp, _ := loadSAMLService(context.Background())
|
||||
|
||||
hh = &handlers.AuthHandlers{
|
||||
SamlSPService: *sp,
|
||||
Log: zap.NewNop(),
|
||||
AuthService: service.DefaultAuth,
|
||||
SessionManager: sm,
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
hh.MountHttpRoutes(r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +129,7 @@ func newHelper(t *testing.T) helper {
|
||||
cUser: &types.User{
|
||||
ID: id.Next(),
|
||||
},
|
||||
data: mockData,
|
||||
}
|
||||
|
||||
h.cUser.SetRoles([]uint64{h.roleID})
|
||||
@@ -174,6 +199,17 @@ func (h helper) setLabel(res label.LabeledResource, name, value string) {
|
||||
}))
|
||||
}
|
||||
|
||||
func (h helper) assertBody(e string, s io.ReadCloser) {
|
||||
b, err := io.ReadAll(s)
|
||||
h.a.NoError(err)
|
||||
h.a.Equal(e, string(b))
|
||||
}
|
||||
|
||||
func (h helper) clearTemplates() {
|
||||
h.noError(store.TruncateTemplates(context.Background(), service.DefaultStore))
|
||||
}
|
||||
|
||||
func readStaticFile(f string) []byte {
|
||||
c, _ := mockData.ReadFile(f)
|
||||
return c
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODQiLCJleHAiOjE2MjEyNDMwODIsImlhdCI6MTYyMTI0Mjk5MiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg0IiwibmJmIjoxNjIxMjQyOTkyLCJzdWIiOiJ0Q3U1UFY2RWd4Y3ZVQWE5ZTU3dUoyZy1iVGtxbk5reXlISGFPdTE1eUVmWmpnV0t0MDJBdFhHZSIsImlkIjoiaWQtN2JmZmIyNmQ2YmVlNGI1YmRmODZmOGRjNGRhMmNmYzExN2Q4OWQwZiIsInVyaSI6Ii9hdXRoL2V4dGVybmFsL3NhbWwvaW5pdCIsInNhbWwtYXV0aG4tcmVxdWVzdCI6dHJ1ZX0.CU_jrc5gx6JhafzbekO-7VXLJU6jzDd-R4QyrtQIZN3jyqIZtYB466KiTFZnyYeEWEjK7GW18eHuzZFHmDpcQ9weOtvu9u0Z7UUDm3YQoG-6XgUeQKTV2i1uPzq1ZlT8iiMBUsn0kdKL2F18U4jl4Fss0_Ysdc3OqoEJ73xcu0P721ZSsg-vEwyooe1WMSosunN_HEmWOU2aC61uQwNFSRk5_JotdUEytko1Jzn9JeOnLllf8izr7z7JWEnBqN8845IV_zqiScrAptpAZRocAeMAbFPFPmj5_lL2SzKC4GF4lkoOIZ5vWRBVdfzIxvD21rVZK5rRlSdYUmT0txFoQw
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODQiLCJleHAiOjE2MjEyNDY2MzAsImlhdCI6MTYyMTI0MzAzMCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg0IiwibmJmIjoxNjIxMjQzMDMwLCJzdWIiOiJBQWR6WldOeVpYUXhFMmR5SFBqRStoTVhQQW5uSUNpbUt2c21XeC9YMW9MaEFOV1dGbU0wWElMS01xa1dBWjFrVldiR0J0ZjhFS0xsb2wvakM2ZlJMa25Cc3dZRDlWNUxHQUErcDFxZHBON2w2MGI3ZHYvMFcwa25VRm1SSm8vWFlTYmRkTVFweUtBMWlCNVFrejZERDVsS0Ixa2VTMzBiYWpuT29OTT0iLCJhdHRyIjp7IlNlc3Npb25JbmRleCI6WyJfYzA5YWFiNDRhNTUyOGVhNzM2NTdkNTNjYTRmODA4NDMiXSwiZGlzcGxheU5hbWUiOlsiUmljayBTYW5jaGV6Il0sImVkdVBlcnNvbkVudGl0bGVtZW50IjpbInVybjptYWNlOmRpcjplbnRpdGxlbWVudDpjb21tb24tbGliLXRlcm1zIl0sImdpdmVuTmFtZSI6WyJSaWNrIl0sIm1haWwiOlsicnNhbmNoZXpAc2FtbHRlc3QuaWQiXSwicm9sZSI6WyJtYW5hZ2VyQHNhbWx0ZXN0LmlkIl0sInNuIjpbIlNhbmNoZXoiXSwidGVsZXBob25lTnVtYmVyIjpbIisxLTU1NS01NTUtNTUxNSJdLCJ1aWQiOlsicmljayJdLCJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDphdHRyaWJ1dGU6c3ViamVjdC1pZCI6WyJyc2FuY2hlekBzYW1sdGVzdC5pZCJdfSwic2FtbC1zZXNzaW9uIjp0cnVlfQ.bNYxkq2dnISDr4x0Wg9pBXcOsSpKEvf5Bb_1vfZDLax6yH2Jjn0Dwjn7LMI6rRcz31YdMkhxrc9sISDD4Peotc1q_QNmJ92xCCzDq9jZM7ptRpKHZJIWkgwUDmmsLjCWj_YiuOwsPP6Zt70AJXNUtOcBg04Dw5ncpcR1cRqf6rTP7rAdNjQ_Euitln1gHK4Zz-fH0GgtS_pYHIzKTRarxiEktMvQGiaNce5e-1DmMOS3L9_6-lSlTa4sYG9BVG4icFf3QnUXqc3DCgGQr7waMuZNA35jqSJ1MSwhLVSHV5gb_l3HxoHKwZhue5d874apTk8cX00wlDO0Q9Z1z2R9Pg
|
||||
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAgmgAwIBAgIUB34XNoIgt3k4tLbC40GDnLuvYAkwDQYJKoZIhvcNAQEL
|
||||
BQAwIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1wbGUuY29tMB4XDTIxMDQyMjE3
|
||||
MzIzOVoXDTIyMDQyMjE3MzIzOVowIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1w
|
||||
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp9jElUsjM3We
|
||||
UHA9CjGQLoQJlhNOqkv2oKZBvTpr8bAQF40vP9qGiwrZqRe0se1xCTmlVM/HGrct
|
||||
C0I6aL7+WTamNEvwPimoWsk1V7zYY7cFW+JEmL/E379R+9giQLBxIvvg7hSd7icl
|
||||
ucwG5V6WPenQiyneEegTsEEgKqagIAgSV9zYjNQrQes73WHBj8VCZhTsVEnVO+/V
|
||||
mCJaEwwovZWP0loLebR29/I4gr/LmfQfrIA+j116o5zp44vl1yW98xq5snkgb4YB
|
||||
fm6pTHDl2rn4pM3avL+sK1usz7HQwmKrW6cpLtKVc+Sbjf3MyLUuHxRmkPoY2J56
|
||||
uJ1OWChoYwIDAQABo1MwUTAdBgNVHQ4EFgQUTjg/FEKaUjpqzm3KzCg+WMUsm2ow
|
||||
HwYDVR0jBBgwFoAUTjg/FEKaUjpqzm3KzCg+WMUsm2owDwYDVR0TAQH/BAUwAwEB
|
||||
/zANBgkqhkiG9w0BAQsFAAOCAQEALXtIWymIgZDMKHlVzMWq/Id/jP2aI0hpvZgS
|
||||
2Lv7UDdyF1bj1MDTOtDkERoTVh/Xbhao5ls+GVhxvudjnZ8irdtlQRJNWTgj58Zi
|
||||
zagO3StQ2duwZPyvt9jHgVJ8Jm9w8JVgsNP4Notl5Gj7dMGhxnm4aA2oHScJoC5g
|
||||
u2ukGAAxgUpjNA7j9C/7iVLzijFzhztq7XTmbnJmuFPR50rqHJcTTYMVDc8Pb73c
|
||||
mjDZDz9U91v+7j5oDLV/8khvLsHxi85/GA1+j7z77wWzrIwwNjY4YfbhF0HJZXQz
|
||||
Xf3EHGxtSC01hJyfBjyXh5H8+NbyAYF8+d8y+uFu+GHhLS+Yag==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCn2MSVSyMzdZ5Q
|
||||
cD0KMZAuhAmWE06qS/agpkG9OmvxsBAXjS8/2oaLCtmpF7Sx7XEJOaVUz8caty0L
|
||||
Qjpovv5ZNqY0S/A+KahayTVXvNhjtwVb4kSYv8Tfv1H72CJAsHEi++DuFJ3uJyW5
|
||||
zAblXpY96dCLKd4R6BOwQSAqpqAgCBJX3NiM1CtB6zvdYcGPxUJmFOxUSdU779WY
|
||||
IloTDCi9lY/SWgt5tHb38jiCv8uZ9B+sgD6PXXqjnOnji+XXJb3zGrmyeSBvhgF+
|
||||
bqlMcOXaufikzdq8v6wrW6zPsdDCYqtbpyku0pVz5JuN/czItS4fFGaQ+hjYnnq4
|
||||
nU5YKGhjAgMBAAECggEAPlzd/ZJjS9VhswVgyI7NwVqxrR8TVVbQFbRwLHyuaqg9
|
||||
8mI0sgbhgnvPj3INYyaTnxfaA/8HPTfd9pbu2MhN/Ju/eSLV6mLT+JdVyHmT9Mil
|
||||
pxQU5KQr4+5T6bzOTTbBcnwfgJYMb9X/wF68GTDhpbNgFrTBm+mclxo7d11dlUiQ
|
||||
bdA2KBhIM54kKcWEO90AP2FiC/cRSbv8afHVb0Fxy1SBAGVDSscUaEwgyHkRf6H7
|
||||
Mmt4xhfBwLZZk5pVa97pZsas7L0sm4BtyYSs4elJesuo3DTpezZSzWHNreJcCL00
|
||||
v7pm9ZWv9PYeUcQqSw6Ws2fhsydiN93o8s/fPpF0IQKBgQDQqvKPjGo3HLRnBmpc
|
||||
nhUVQgjdh/QobJCGBjdxR/C3NkcatNLONddhefqovfb2jITdHmRKW+7XgRq/q457
|
||||
mZ2JPvvryiWcVqf6D91tSoFYrVg5nU1zr6nTga1BpTH00/eNEXxjMAAguPj3m7Sw
|
||||
3BP7L8yZZI1pOOjzVzuISEDlGwKBgQDN62ZXzRqorn5t+mDywTAJ2qAyZzoDAtUP
|
||||
CjANOKBCECqE9cOW1qBDJcaEF7G+xyB22CMciOELjGXWw6ZwFWjc23tDCEkoHoia
|
||||
5IWvwNs5ncdPCuOm3aay9w2dx9Idy+Eq5Vpe4XDlnZvEfzjwXX3gbQ5YS9kbNE2r
|
||||
XtmgD/BmWQKBgBNKqrBA0BUWT0tzGWRErThQ6ZbpmdYe62Gos3mCqCuYFgzPCOpN
|
||||
qgL2DwmIvotexG3ZAHardzJvWjS8PKkKs7jbnNjY0I9ap58D1nnjOIAlTpHNDDsU
|
||||
04OdapI2Hp8+9ZUSN8jHyEs+Lq5ds9/iCOrhKW5JEJXY0BinSPa5j15fAoGBAM2I
|
||||
dqCQollXwe34CaiD13UeeOOWUTsMKqlWW9v2d085X5dSzyTRmSksnVbfZ5SqoOa+
|
||||
mV0z6pxiSIvywUACvqYjlIa10H9w6pzgF+fzMV3y9CsbDVtSxb7ABSFFf54qD9eH
|
||||
EYq+rrchd4bMDYMtbiUB9V2AZ3VV4Wh5xfKTtjoRAoGAec/nBmX/oHiG/3T2LEmo
|
||||
bTT+/eXc4LUzgHvFG6HW40PI8T2TfMTUGQH+90zkraryUF5PNoUF58tooDAcMWXm
|
||||
DPEHFHeWn8T4obfuUjTw1mwj7Xjzr40HDitjIQa0bvBVwdYyEgiEw1CaeaUpq9Z8
|
||||
ElGWoRHT87pfoZjbdPz7a4Y=
|
||||
-----END PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user