diff --git a/data/stir/generate_keys.sh b/data/stir/generate_keys.sh new file mode 100755 index 000000000..b6611e010 --- /dev/null +++ b/data/stir/generate_keys.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# generate private the key for ES256 +openssl ecparam -genkey -name prime256v1 -noout -out stir_privatekey.pem + +# generate the public key based on the private key +openssl ec -in stir_privatekey.pem -pubout -out stir_pubkey.pem + +#generate the certificate for the private key +openssl req -new -x509 -key stir_privatekey.pem -out stir_cert.pem -days 3650 -subj "/C=DE/ST=Bavaria/L=Bad Reichenhall/O=ITsysCOM/OU=root/CN=localhost/emailAddress=contact@itsyscom.com" diff --git a/data/stir/stir_cert.pem b/data/stir/stir_cert.pem new file mode 100644 index 000000000..82b4cf1bc --- /dev/null +++ b/data/stir/stir_cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICczCCAhqgAwIBAgIJAMfbYJAZnigjMAoGCCqGSM49BAMCMIGUMQswCQYDVQQG +EwJERTEQMA4GA1UECAwHQmF2YXJpYTEYMBYGA1UEBwwPQmFkIFJlaWNoZW5oYWxs +MREwDwYDVQQKDAhJVHN5c0NPTTENMAsGA1UECwwEcm9vdDESMBAGA1UEAwwJbG9j +YWxob3N0MSMwIQYJKoZIhvcNAQkBFhRjb250YWN0QGl0c3lzY29tLmNvbTAeFw0y +MDA0MTYxMTU4MDBaFw0zMDA0MTQxMTU4MDBaMIGUMQswCQYDVQQGEwJERTEQMA4G +A1UECAwHQmF2YXJpYTEYMBYGA1UEBwwPQmFkIFJlaWNoZW5oYWxsMREwDwYDVQQK +DAhJVHN5c0NPTTENMAsGA1UECwwEcm9vdDESMBAGA1UEAwwJbG9jYWxob3N0MSMw +IQYJKoZIhvcNAQkBFhRjb250YWN0QGl0c3lzY29tLmNvbTBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABI0uM5lqLWKilgdv7J/uL9blKDwENjdmba1LJnsJJdxKwGyK +JF0mUq7nCTz7M5QbWG24XJm1wuwyj/bvT6vxp1ajUzBRMB0GA1UdDgQWBBT6VrVO +TDbfJs5R/JG8xBTe8O8TkTAfBgNVHSMEGDAWgBT6VrVOTDbfJs5R/JG8xBTe8O8T +kTAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0cAMEQCID2rMfvx3Qyvvqr2 +WMz4AgV1Yplp6kI0FDEH2w0GiTjoAiBsIF1TsUAwmmXYW8yaPyCVq7LYK9TUP2bu +oa27qsM7gA== +-----END CERTIFICATE----- diff --git a/data/stir/stir_privatekey.pem b/data/stir/stir_privatekey.pem new file mode 100644 index 000000000..3ab6e4294 --- /dev/null +++ b/data/stir/stir_privatekey.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICcL1+2nj9ylMlTKjSpIGx03gALK0cISciviwudQuvb9oAoGCCqGSM49 +AwEHoUQDQgAEjS4zmWotYqKWB2/sn+4v1uUoPAQ2N2ZtrUsmewkl3ErAbIokXSZS +rucJPPszlBtYbbhcmbXC7DKP9u9Pq/GnVg== +-----END EC PRIVATE KEY----- diff --git a/data/stir/stir_pubkey.pem b/data/stir/stir_pubkey.pem new file mode 100644 index 000000000..213af90f5 --- /dev/null +++ b/data/stir/stir_pubkey.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjS4zmWotYqKWB2/sn+4v1uUoPAQ2 +N2ZtrUsmewkl3ErAbIokXSZSrucJPPszlBtYbbhcmbXC7DKP9u9Pq/GnVg== +-----END PUBLIC KEY----- diff --git a/general_tests/session2_it_test.go b/general_tests/session2_it_test.go index 21ff8fdea..4fbfa4153 100644 --- a/general_tests/session2_it_test.go +++ b/general_tests/session2_it_test.go @@ -47,6 +47,7 @@ var ( testSes2ItLoadFromFolder, testSes2ItInitSession, testSes2ItAsActiveSessions, + testSes2StirAuthorize, testSes2ItStopCgrEngine, } ) @@ -179,3 +180,41 @@ func testSes2ItStopCgrEngine(t *testing.T) { t.Error(err) } } + +func testSes2StirAuthorize(t *testing.T) { + args := &sessions.V1ProcessEventArgs{ + Flags: []string{"*stir_authorize"}, + CGREvent: &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "testSes2StirAuthorize", + Event: map[string]interface{}{ + utils.ToR: utils.VOICE, + utils.OriginID: "testSes2StirAuthorize", + utils.RequestType: utils.META_PREPAID, + utils.Account: "1001", + utils.Subject: "ANY2CNT", + utils.Destination: "1002", + utils.Usage: 10 * time.Minute, + utils.STIRIdentity: "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiL3Vzci9zaGFyZS9jZ3JhdGVzL3N0aXIvc3Rpcl9wdWJrZXkucGVtIn0.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMzg4MDIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.cMEMlFnfyTu8uxfeU4RoZTamA7ifFT9Ibwrvi1_LKwL2xAU6fZ_CSIxKbtyOpNhM_sV03x7CfA_v0T4sHkifzg;info=;ppt=shaken", + }, + }, + } + var rply sessions.V1ProcessEventReply + if err := ses2RPC.Call(utils.SessionSv1ProcessEvent, + args, &rply); err != nil { // no error verificated with success + t.Error(err) + } + // altered originator + args.CGREvent.Event[utils.STIROriginatorTn] = "1005" + if err := ses2RPC.Call(utils.SessionSv1ProcessEvent, + args, &rply); err == nil || err.Error() != "*stir_authorize: wrong originatorTn" { + t.Errorf("Expected error :%q ,receved: %v", "*stir_authorize: wrong originatorTn", err) + } + + // altered identity + args.CGREvent.Event[utils.STIRIdentity] = "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiL3Vzci9zaGFyZS9jZ3JhdGVzL3N0aXIvc3Rpcl9wdWJrZXkucGVtIn0.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMzg4MDIsIm9yaWciOnsidG4iOiIxMDA1In0sIm9yaWdpZCI6IjEyMzQ1NiJ9.cMEMlFnfyTu8uxfeU4RoZTamA7ifFT9Ibwrvi1_LKwL2xAU6fZ_CSIxKbtyOpNhM_sV03x7CfA_v0T4sHkifzg;info=;ppt=shaken" + if err := ses2RPC.Call(utils.SessionSv1ProcessEvent, + args, &rply); err == nil || err.Error() != "*stir_authorize: crypto/ecdsa: verification error" { + t.Errorf("Expected error :%q ,receved: %v", "*stir_authorize: crypto/ecdsa: verification error", err) + } +} diff --git a/packages/debian/changelog b/packages/debian/changelog index f5ee2b1b8..33a70ec80 100644 --- a/packages/debian/changelog +++ b/packages/debian/changelog @@ -50,6 +50,7 @@ cgrates (0.11.0~dev) UNRELEASED; urgency=medium * [AgentS] Add ability to inject data in cache from agents * [Config] Config cache format change to include partitions * [ERs] Add *none EventReader type + * [SessionS] Added support for *stir_authorize -- Alexandru Tripon Wed, 19 Feb 2020 13:25:52 +0200 diff --git a/sessions/libsessions.go b/sessions/libsessions.go index 668ddb832..16b48986f 100644 --- a/sessions/libsessions.go +++ b/sessions/libsessions.go @@ -24,6 +24,7 @@ import ( "strings" "time" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" "github.com/dgrijalva/jwt-go" @@ -205,23 +206,19 @@ func (pi *ProcessedStirIdentity) VerifyPayload(originatorTn, originatorURI, dest if hdrMaxDur >= 0 && time.Now().After(time.Unix(pi.Payload.IAT, 0).Add(hdrMaxDur)) { return errors.New("expired payload") } - if originatorTn != utils.EmptyString { - if originatorTn != pi.Payload.Orig.Tn { - return errors.New("wrong originatorTn") - } - } else { + if originatorURI != utils.EmptyString { if originatorURI != pi.Payload.Orig.URI { return errors.New("wrong originatorURI") } + } else if originatorTn != pi.Payload.Orig.Tn { + return errors.New("wrong originatorTn") } - if destinationTn != utils.EmptyString { - if !utils.SliceHasMember(pi.Payload.Dest.Tn, destinationTn) { - return errors.New("wrong destinationTn") - } - } else { + if destinationURI != utils.EmptyString { if !utils.SliceHasMember(pi.Payload.Dest.URI, destinationURI) { return errors.New("wrong destinationURI") } + } else if !utils.SliceHasMember(pi.Payload.Dest.Tn, destinationTn) { + return errors.New("wrong destinationTn") } return } @@ -258,7 +255,7 @@ func NewIdentity(header *utils.PASSporTHeader, payload *utils.PASSporTPayload, p return } -func authStirShaken(identity, originatorTn, originatorURI, destinationTn, destinationURI string, +func AuthStirShaken(identity, originatorTn, originatorURI, destinationTn, destinationURI string, attest *utils.StringSet, hdrMaxDur time.Duration) (err error) { var pi *ProcessedStirIdentity if pi, err = NewProcessedIdentity(identity); err != nil { @@ -267,8 +264,8 @@ func authStirShaken(identity, originatorTn, originatorURI, destinationTn, destin if !pi.VerifyHeader() { return errors.New("wrong header") } - if err = pi.VerifySignature(time.Second); err != nil { + if err = pi.VerifySignature(config.CgrConfig().GeneralCfg().ReplyTimeout); err != nil { return } - return pi.VerifyPayload(originatorTn, originatorURI, destinationTn, destinationURI, hdrMaxDur, attest) // verificare in lista + return pi.VerifyPayload(originatorTn, originatorURI, destinationTn, destinationURI, hdrMaxDur, attest) } diff --git a/sessions/libsessions_test.go b/sessions/libsessions_test.go index 0642ae37b..d067ebc44 100644 --- a/sessions/libsessions_test.go +++ b/sessions/libsessions_test.go @@ -25,6 +25,7 @@ import ( "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" + "github.com/dgrijalva/jwt-go" ) func TestLibSessionSGetSetCGRID(t *testing.T) { @@ -146,3 +147,165 @@ func TestGetFlagIDs(t *testing.T) { t.Errorf("Expected %s , received: %s", utils.ToJSON(eOut), utils.ToJSON(rcv)) } } + +func TestNewProcessedIdentity(t *testing.T) { + if _, err := NewProcessedIdentity(""); err == nil || + err.Error() != "missing parts of the message header" { + t.Errorf("Expected %q received: %v", "missing parts of the message header", err) + } + if _, err := NewProcessedIdentity(";"); err == nil || + err.Error() != "wrong header format" { + t.Errorf("Expected %q received: %v", "wrong header format", err) + } + + if _, err := NewProcessedIdentity("eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR,5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWc,iOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken"); err == nil { + t.Errorf("Expected error") + } + + if _, err := NewProcessedIdentity("eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWc,iOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken"); err == nil { + t.Errorf("Expected error") + } + + expected := &ProcessedStirIdentity{ + Tokens: []string{"info=", "ppt=shaken"}, + SigningStr: "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9", + Signature: "4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg", + Header: &utils.PASSporTHeader{ + Typ: utils.STIRTyp, + Alg: utils.STIRAlg, + Ppt: utils.STIRPpt, + X5u: "https://www.example.org/cert.cer", + }, + Payload: &utils.PASSporTPayload{ + ATTest: "A", + Dest: utils.PASSporTDestinationsIdentity{ + Tn: []string{"1002"}, + }, + IAT: 1587019822, + Orig: utils.PASSporTOriginsIdentity{ + Tn: "1001", + }, + OrigID: "123456", + }, + } + if rply, err := NewProcessedIdentity("eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rply) { + t.Errorf("Expected: %s, received:%s", utils.ToJSON(expected), utils.ToJSON(rply)) + } +} + +func TestProcessedIdentityVerifyHeader(t *testing.T) { + args := &ProcessedStirIdentity{ + Tokens: []string{"info=", "ppt=shaken", "extra", "alg=ES256"}, + Header: &utils.PASSporTHeader{ + Typ: utils.STIRTyp, + Alg: utils.STIRAlg, + Ppt: utils.STIRPpt, + X5u: "https://www.example.org/cert.cer", + }, + } + if !args.VerifyHeader() { + t.Errorf("Expected the header to be valid") + } + args.Header.Typ = "1" + if args.VerifyHeader() { + t.Errorf("Expected the header to not be valid") + } + + args.Tokens = []string{"info=", "ppt=shaken", "alg=wrongArg"} + if args.VerifyHeader() { + t.Errorf("Expected the header to not be valid") + } + + args.Tokens = []string{"info=", "ppt=wrongExtension"} + if args.VerifyHeader() { + t.Errorf("Expected the header to not be valid") + } + args.Tokens = []string{"info=<", "ppt=shaken"} + if args.VerifyHeader() { + t.Errorf("Expected the header to not be valid") + } +} + +func TestProcessedIdentityVerifyPayload(t *testing.T) { + args := &ProcessedStirIdentity{ + Payload: &utils.PASSporTPayload{ + ATTest: "C", + Dest: utils.PASSporTDestinationsIdentity{ + Tn: []string{"1002"}, + }, + IAT: 1587019822, + Orig: utils.PASSporTOriginsIdentity{ + Tn: "1001", + }, + OrigID: "123456", + }, + } + if err := args.VerifyPayload("1001", "", "1002", "", -1, utils.NewStringSet([]string{utils.META_ANY})); err != nil { + t.Error(err) + } + if err := args.VerifyPayload("1001", "", "1003", "", -1, utils.NewStringSet([]string{utils.META_ANY})); err == nil || + err.Error() != "wrong destinationTn" { + t.Errorf("Expected error: %s,receved %v", "wrong destinationTn", err) + } + if err := args.VerifyPayload("1001", "", "1003", "1002", -1, utils.NewStringSet([]string{utils.META_ANY})); err == nil || + err.Error() != "wrong destinationURI" { + t.Errorf("Expected error: %s,receved %v", "wrong destinationURI", err) + } + if err := args.VerifyPayload("1002", "", "1003", "1002", -1, utils.NewStringSet([]string{utils.META_ANY})); err == nil || + err.Error() != "wrong originatorTn" { + t.Errorf("Expected error: %s,receved %v", "wrong originatorTn", err) + } + if err := args.VerifyPayload("1002", "1001", "1003", "1002", -1, utils.NewStringSet([]string{utils.META_ANY})); err == nil || + err.Error() != "wrong originatorURI" { + t.Errorf("Expected error: %s,receved %v", "wrong originatorURI", err) + } + if err := args.VerifyPayload("1001", "", "1002", "", time.Second, utils.NewStringSet([]string{utils.META_ANY})); err == nil || + err.Error() != "expired payload" { + t.Errorf("Expected error: %s,receved %v", "expired payload", err) + } + if err := args.VerifyPayload("1001", "", "1002", "", time.Second, utils.NewStringSet([]string{"A"})); err == nil || + err.Error() != "wrong attest level" { + t.Errorf("Expected error: %s,receved %v", "wrong attest level", err) + } +} + +func TestAuthStirShaken(t *testing.T) { + if err := AuthStirShaken("", "1001", "", "1002", "", utils.NewStringSet([]string{utils.META_ANY}), -1); err == nil { + t.Error("Expected invalid identity") + } + if err := AuthStirShaken( + "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken", + "1001", "", "1002", "", utils.NewStringSet([]string{utils.META_ANY}), -1); err == nil { + t.Error("Expected invalid identity") + } + engine.Cache.Set(utils.CacheSTIR, "https://www.example.org/cert.cer", nil, + nil, true, utils.NonTransactional) + if err := AuthStirShaken( + "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken", "1001", "", "1002", "", utils.NewStringSet([]string{utils.META_ANY}), -1); err == nil { + t.Error("Expected invalid identity") + } + + pubkeyBuf := []byte(`-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESt8sEh55Yc579vLHjFRWVQO27p4Y +aa+jqv4dwkr/FLEcN1zC76Y/IniI65fId55hVJvN3ORuzUqYEtzD3irmsw== +-----END PUBLIC KEY----- +`) + pubKey, err := jwt.ParseECPublicKeyFromPEM(pubkeyBuf) + if err != nil { + t.Fatal(err) + } + engine.Cache.Set(utils.CacheSTIR, "https://www.example.org/cert.cer", pubKey, + nil, true, utils.NonTransactional) + + if err := AuthStirShaken( + "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken", "1001", "", "1003", "", utils.NewStringSet([]string{utils.META_ANY}), -1); err == nil { + t.Error("Expected invalid identity") + } + + if err := AuthStirShaken( + "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken", "1001", "", "1002", "", utils.NewStringSet([]string{utils.META_ANY}), -1); err != nil { + t.Fatal(err) + } +} diff --git a/sessions/session_test.go b/sessions/session_test.go index 47d3336f2..e415e4ca3 100644 --- a/sessions/session_test.go +++ b/sessions/session_test.go @@ -361,12 +361,10 @@ func TestSessionAsCGREvents(t *testing.T) { CGRID: "RandomCGRID", Tenant: "cgrates.org", EventStart: engine.NewMapEvent(startEv), - SRuns: []*SRun{ - &SRun{ - Event: engine.NewMapEvent(ev), - TotalUsage: time.Duration(2 * time.Second), - }, - }, + SRuns: []*SRun{{ + Event: engine.NewMapEvent(ev), + TotalUsage: time.Duration(2 * time.Second), + }}, } //check for some fields if populated correct cgrEvs, err := s.asCGREvents() @@ -420,44 +418,40 @@ func TestSessionAsExternalSessions(t *testing.T) { Tenant: "cgrates.org", EventStart: engine.NewMapEvent(startEv), DebitInterval: time.Second, - SRuns: []*SRun{ - &SRun{ - Event: engine.NewMapEvent(ev), - TotalUsage: time.Duration(2 * time.Second), - NextAutoDebit: &tTime, - }, - }, + SRuns: []*SRun{{ + Event: engine.NewMapEvent(ev), + TotalUsage: time.Duration(2 * time.Second), + NextAutoDebit: &tTime, + }}, } - exp := []*ExternalSession{ - &ExternalSession{ - CGRID: "RandomCGRID", - RunID: utils.MetaDefault, - ToR: utils.VOICE, - OriginID: "123451", - // OriginHost: s.EventStart.GetStringIgnoreErrors(utils.OriginHost), - Source: utils.SessionS + "_" + "TEST_EVENT", - RequestType: utils.META_PREPAID, - Tenant: "cgrates.org", - Category: "call", - Account: "1001", - Subject: "1001", - Destination: "1004", - SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC), - AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), - Usage: time.Duration(2 * time.Second), - ExtraFields: map[string]string{ - utils.EVENT_NAME: "TEST_EVENT2", - }, - NodeID: "ALL", - DebitInterval: time.Second, - NextAutoDebit: tTime, - // aSs[i].LoopIndex: sr.CD.LoopIndex, - // aSs[i].DurationIndex: sr.CD.DurationIndex, - // aSs[i].MaxRate: sr.CD.MaxRate, - // aSs[i].MaxRateUnit: sr.CD.MaxRateUnit, - // aSs[i].MaxCostSoFar: sr.CD.MaxCostSoFar, + exp := []*ExternalSession{{ + CGRID: "RandomCGRID", + RunID: utils.MetaDefault, + ToR: utils.VOICE, + OriginID: "123451", + // OriginHost: s.EventStart.GetStringIgnoreErrors(utils.OriginHost), + Source: utils.SessionS + "_" + "TEST_EVENT", + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "1004", + SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC), + AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), + Usage: time.Duration(2 * time.Second), + ExtraFields: map[string]string{ + utils.EVENT_NAME: "TEST_EVENT2", }, - } + NodeID: "ALL", + DebitInterval: time.Second, + NextAutoDebit: tTime, + // aSs[i].LoopIndex: sr.CD.LoopIndex, + // aSs[i].DurationIndex: sr.CD.DurationIndex, + // aSs[i].MaxRate: sr.CD.MaxRate, + // aSs[i].MaxRateUnit: sr.CD.MaxRateUnit, + // aSs[i].MaxCostSoFar: sr.CD.MaxCostSoFar, + }} //check for some fields if populated correct rply := s.AsExternalSessions("", "ALL") if !reflect.DeepEqual(exp, rply) { @@ -503,49 +497,45 @@ func TestSessionAsExternalSessions2(t *testing.T) { Tenant: "cgrates.org", EventStart: engine.NewMapEvent(startEv), DebitInterval: time.Second, - SRuns: []*SRun{ - &SRun{ - Event: engine.NewMapEvent(ev), - TotalUsage: time.Duration(2 * time.Second), - CD: &engine.CallDescriptor{ - LoopIndex: 10, - DurationIndex: 3 * time.Second, - MaxRate: 11, - MaxRateUnit: 30 * time.Second, - MaxCostSoFar: 20, - }, - }, + SRuns: []*SRun{{ + Event: engine.NewMapEvent(ev), + TotalUsage: time.Duration(2 * time.Second), + CD: &engine.CallDescriptor{ + LoopIndex: 10, + DurationIndex: 3 * time.Second, + MaxRate: 11, + MaxRateUnit: 30 * time.Second, + MaxCostSoFar: 20, + }}, }, } - exp := []*ExternalSession{ - &ExternalSession{ - CGRID: "RandomCGRID", - RunID: utils.MetaDefault, - ToR: utils.VOICE, - OriginID: "123451", - // OriginHost: s.EventStart.GetStringIgnoreErrors(utils.OriginHost), - Source: utils.SessionS + "_" + "TEST_EVENT", - RequestType: utils.META_PREPAID, - Tenant: "cgrates.org", - Category: "call", - Account: "1001", - Subject: "1001", - Destination: "1004", - SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC), - AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), - Usage: time.Duration(2 * time.Second), - ExtraFields: map[string]string{ - utils.EVENT_NAME: "TEST_EVENT2", - }, - NodeID: "ALL", - DebitInterval: time.Second, - LoopIndex: 10, - DurationIndex: 3 * time.Second, - MaxRate: 11, - MaxRateUnit: 30 * time.Second, - MaxCostSoFar: 20, + exp := []*ExternalSession{{ + CGRID: "RandomCGRID", + RunID: utils.MetaDefault, + ToR: utils.VOICE, + OriginID: "123451", + // OriginHost: s.EventStart.GetStringIgnoreErrors(utils.OriginHost), + Source: utils.SessionS + "_" + "TEST_EVENT", + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "1004", + SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC), + AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), + Usage: time.Duration(2 * time.Second), + ExtraFields: map[string]string{ + utils.EVENT_NAME: "TEST_EVENT2", }, - } + NodeID: "ALL", + DebitInterval: time.Second, + LoopIndex: 10, + DurationIndex: 3 * time.Second, + MaxRate: 11, + MaxRateUnit: 30 * time.Second, + MaxCostSoFar: 20, + }} //check for some fields if populated correct rply := s.AsExternalSessions("", "ALL") if !reflect.DeepEqual(exp, rply) { @@ -593,20 +583,18 @@ func TestSessionAsExternalSessions3(t *testing.T) { Tenant: "cgrates.org", EventStart: engine.NewMapEvent(startEv), DebitInterval: time.Second, - SRuns: []*SRun{ - &SRun{ - Event: engine.NewMapEvent(ev), - TotalUsage: time.Duration(2 * time.Second), - CD: &engine.CallDescriptor{ - LoopIndex: 10, - DurationIndex: 3 * time.Second, - MaxRate: 11, - MaxRateUnit: 30 * time.Second, - MaxCostSoFar: 20, - }, - NextAutoDebit: &tTime, + SRuns: []*SRun{{ + Event: engine.NewMapEvent(ev), + TotalUsage: time.Duration(2 * time.Second), + CD: &engine.CallDescriptor{ + LoopIndex: 10, + DurationIndex: 3 * time.Second, + MaxRate: 11, + MaxRateUnit: 30 * time.Second, + MaxCostSoFar: 20, }, - }, + NextAutoDebit: &tTime, + }}, } exp := &ExternalSession{ CGRID: "RandomCGRID", @@ -663,7 +651,8 @@ func TestSessiontotalUsage(t *testing.T) { EventStart: engine.NewMapEvent(nil), DebitInterval: time.Duration(18), SRuns: []*SRun{ - {Event: engine.NewMapEvent(nil), + { + Event: engine.NewMapEvent(nil), CD: &engine.CallDescriptor{Category: "test"}, EventCost: &engine.EventCost{CGRID: "testCGRID"}, ExtraDuration: time.Duration(1), @@ -671,7 +660,8 @@ func TestSessiontotalUsage(t *testing.T) { TotalUsage: time.Duration(5), NextAutoDebit: &tTime, }, - {Event: engine.NewMapEvent(nil), + { + Event: engine.NewMapEvent(nil), CD: &engine.CallDescriptor{Category: "test2"}, EventCost: &engine.EventCost{CGRID: "testCGRID2"}, ExtraDuration: time.Duration(4), @@ -714,5 +704,73 @@ func TestSessionstopDebitLoops(t *testing.T) { if session.debitStop != nil { t.Errorf("Expecting: nil, received: %s", utils.ToJSON(session.debitStop)) } - +} + +func TestUpdateSRuns(t *testing.T) { + startEv := map[string]interface{}{ + utils.EVENT_NAME: "TEST_EVENT", + utils.ToR: utils.VOICE, + utils.OriginID: "123451", + utils.Account: "1001", + utils.Subject: "1001", + utils.Destination: "1004", + utils.Category: "call", + utils.Tenant: "cgrates.org", + utils.RequestType: utils.META_PREPAID, + utils.SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC), + utils.AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), + utils.Usage: time.Duration(2 * time.Second), + utils.Cost: 12.12, + } + ev := map[string]interface{}{ + utils.EVENT_NAME: "TEST_EVENT2", + utils.ToR: utils.VOICE, + utils.OriginID: "123451", + utils.Account: "1001", + utils.Subject: "1001", + utils.Destination: "1004", + utils.Category: "call", + utils.RunID: utils.MetaDefault, + utils.Tenant: "cgrates.org", + utils.RequestType: utils.META_PREPAID, + utils.SetupTime: time.Date(2016, time.January, 5, 18, 30, 59, 0, time.UTC), + utils.AnswerTime: time.Date(2016, time.January, 5, 18, 31, 05, 0, time.UTC), + utils.Usage: time.Duration(2 * time.Second), + utils.Cost: 12.13, + } + s := &Session{ + CGRID: "RandomCGRID", + Tenant: "cgrates.org", + EventStart: engine.NewMapEvent(startEv), + DebitInterval: time.Second, + SRuns: []*SRun{{ + Event: engine.NewMapEvent(ev), + TotalUsage: time.Duration(2 * time.Second), + CD: &engine.CallDescriptor{ + LoopIndex: 10, + DurationIndex: 3 * time.Second, + MaxRate: 11, + MaxRateUnit: 30 * time.Second, + MaxCostSoFar: 20, + }, + }}, + } + updEv := map[string]interface{}{ + utils.EVENT_NAME: "TEST_EVENT2", + utils.Tenant: "cgrates.org", + utils.RequestType: utils.META_POSTPAID, + } + s.updateSRuns(updEv, utils.NewStringSet(nil)) + if s.SRuns[0].Event[utils.RequestType] != utils.META_PREPAID { + t.Errorf("Expected session to not change") + } + s.UpdateSRuns(updEv, utils.NewStringSet(nil)) + if s.SRuns[0].Event[utils.RequestType] != utils.META_PREPAID { + t.Errorf("Expected session to not change") + } + s.UpdateSRuns(updEv, utils.NewStringSet([]string{utils.RequestType})) + if s.SRuns[0].Event[utils.RequestType] != utils.META_POSTPAID { + t.Errorf("Expected request type to be: %q, received: %q", + utils.META_POSTPAID, s.SRuns[0].Event[utils.RequestType]) + } } diff --git a/sessions/sessions.go b/sessions/sessions.go index 1e1b58fdb..78611ab9e 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -2965,7 +2965,7 @@ func (sS *SessionS) BiRPCv1ProcessEvent(clnt rpcclient.ClientConnector, if stirMaxDur, err = ev.GetDuration(utils.STIRPayloadMaxDuration); err != nil { stirMaxDur = sS.cgrCfg.SessionSCfg().STIRPayloadMaxduration } - if err = authStirShaken(ev.GetStringIgnoreErrors(utils.STIRIdentity), + if err = AuthStirShaken(ev.GetStringIgnoreErrors(utils.STIRIdentity), utils.FirstNonEmpty(ev.GetStringIgnoreErrors(utils.STIROriginatorTn), ev.GetStringIgnoreErrors(utils.Account)), ev.GetStringIgnoreErrors(utils.STIROriginatorURI), utils.FirstNonEmpty(ev.GetStringIgnoreErrors(utils.STIRDestinationTn), ev.GetStringIgnoreErrors(utils.Destination)), diff --git a/sessions/sessions_test.go b/sessions/sessions_test.go index c27a47e39..85fc89e63 100644 --- a/sessions/sessions_test.go +++ b/sessions/sessions_test.go @@ -30,7 +30,6 @@ import ( "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" "github.com/cgrates/rpcclient" - "github.com/dgrijalva/jwt-go" ) var attrs = &engine.AttrSProcessEventReply{ @@ -2209,22 +2208,3 @@ func TestSessionSfilterSessionsCount(t *testing.T) { t.Errorf("Expected %v , received: %s", 2, utils.ToJSON(noSess)) } } - -func TestStirShaken(t *testing.T) { - pubkeyBuf := []byte(`-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESt8sEh55Yc579vLHjFRWVQO27p4Y -aa+jqv4dwkr/FLEcN1zC76Y/IniI65fId55hVJvN3ORuzUqYEtzD3irmsw== ------END PUBLIC KEY----- -`) - pubKey, err := jwt.ParseECPublicKeyFromPEM(pubkeyBuf) - if err != nil { - t.Fatal(err) - } - engine.Cache.Set(utils.CacheSTIR, "https://www.example.org/cert.cer", pubKey, - nil, true, utils.NonTransactional) - - if err := authStirShaken( - "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly93d3cuZXhhbXBsZS5vcmcvY2VydC5jZXIifQ.eyJhdHRlc3QiOiJBIiwiZGVzdCI6eyJ0biI6WyIxMDAyIl19LCJpYXQiOjE1ODcwMTk4MjIsIm9yaWciOnsidG4iOiIxMDAxIn0sIm9yaWdpZCI6IjEyMzQ1NiJ9.4ybtWmgqdkNyJLS9Iv3PuJV8ZxR7yZ_NEBhCpKCEu2WBiTchqwoqoWpI17Q_ALm38tbnpay32t95ZY_LhSgwJg;info=;ppt=shaken", "1001", "", "1002", "", utils.NewStringSet([]string{utils.META_ANY}), -1); err != nil { - t.Fatal(err) - } -} diff --git a/utils/errors.go b/utils/errors.go index 7b5f650f4..895f48a2c 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -264,5 +264,5 @@ func ErrNotConvertibleTF(from, to string) error { // NewSTIRError returns a error with a *stir_authorize prefix func NewSTIRError(reason string) error { - return fmt.Errorf("<%s> %s", MetaSTIRAuthorize, reason) + return fmt.Errorf("%s: %s", MetaSTIRAuthorize, reason) } diff --git a/utils/errors_test.go b/utils/errors_test.go index 0d6c34054..d223e7c03 100644 --- a/utils/errors_test.go +++ b/utils/errors_test.go @@ -230,3 +230,17 @@ func TestErrNotConvertibleTF(t *testing.T) { t.Errorf("Expecting: not convertible : from: test_type1 to:test_type2, received: %+v", rcv) } } + +func TestNewErrChargerS(t *testing.T) { + expected := `CHARGERS_ERROR:NOT_FOUND` + if rcv := NewErrChargerS(ErrNotFound); rcv.Error() != expected { + t.Errorf("Expecting: %q, received: %q", expected, rcv.Error()) + } +} + +func TestNewSTIRError(t *testing.T) { + expected := `*stir_authorize: wrong header` + if rcv := NewSTIRError("wrong header"); rcv.Error() != expected { + t.Errorf("Expecting: %q, received: %q", expected, rcv.Error()) + } +} diff --git a/utils/stir_shaken.go b/utils/stir_shaken.go index a85a8494c..fd5469d06 100644 --- a/utils/stir_shaken.go +++ b/utils/stir_shaken.go @@ -70,8 +70,8 @@ type PASSporTOriginsIdentity struct { } // NewPASSporTPayload returns an new PASSporTPayload with the given origin and destination -func NewPASSporTPayload(attest, originID string, dest PASSporTDestinationsIdentity, orig PASSporTOriginsIdentity) PASSporTPayload { - return PASSporTPayload{ +func NewPASSporTPayload(attest, originID string, dest PASSporTDestinationsIdentity, orig PASSporTOriginsIdentity) *PASSporTPayload { + return &PASSporTPayload{ ATTest: attest, Dest: dest, IAT: time.Now().Unix(), diff --git a/utils/stir_shaken_test.go b/utils/stir_shaken_test.go new file mode 100644 index 000000000..4b56b2bc8 --- /dev/null +++ b/utils/stir_shaken_test.go @@ -0,0 +1,72 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package utils + +import ( + "reflect" + "testing" +) + +func TestNewPASSporTHeader(t *testing.T) { + expected := &PASSporTHeader{ + Alg: STIRAlg, + Ppt: STIRPpt, + Typ: STIRTyp, + X5u: "path/to/certificate", + } + if rply := NewPASSporTHeader("path/to/certificate"); !reflect.DeepEqual(expected, rply) { + t.Errorf("Expected: %s,received: %s", ToJSON(expected), ToJSON(rply)) + } +} + +func TestNewPASSporTDestinationsIdentity(t *testing.T) { + expected := &PASSporTDestinationsIdentity{ + Tn: []string{"1001"}, + URI: []string{"1002@cgrates.org"}, + } + if rply := NewPASSporTDestinationsIdentity([]string{"1001"}, []string{"1002@cgrates.org"}); !reflect.DeepEqual(expected, rply) { + t.Errorf("Expected: %s,received: %s", ToJSON(expected), ToJSON(rply)) + } +} + +func TestNewPASSporTOriginsIdentity(t *testing.T) { + expected := &PASSporTOriginsIdentity{ + Tn: "1001", + } + if rply := NewPASSporTOriginsIdentity("1001", ""); !reflect.DeepEqual(expected, rply) { + t.Errorf("Expected: %s,received: %s", ToJSON(expected), ToJSON(rply)) + } +} + +func TestNewPASSporTPayload(t *testing.T) { + dst := NewPASSporTDestinationsIdentity([]string{"1001"}, nil) + orig := NewPASSporTOriginsIdentity("1002", "") + expected := &PASSporTPayload{ + ATTest: "A", + Dest: *dst, + IAT: 0, + Orig: *orig, + OrigID: "123456", + } + rply := NewPASSporTPayload("A", "123456", *dst, *orig) + rply.IAT = 0 + if !reflect.DeepEqual(expected, rply) { + t.Errorf("Expected: %s,received: %s", ToJSON(expected), ToJSON(rply)) + } +} diff --git a/utils/stir_shaken_utils.go b/utils/stir_shaken_utils.go index 18e3b7669..4bc4f7a4b 100644 --- a/utils/stir_shaken_utils.go +++ b/utils/stir_shaken_utils.go @@ -22,6 +22,7 @@ import ( "crypto/ecdsa" "encoding/json" "fmt" + "io" "io/ioutil" "net/http" "os" @@ -31,55 +32,67 @@ import ( "github.com/dgrijalva/jwt-go" ) -// NewECDSAPrvKey creates a private key from the path -func NewECDSAPrvKey(prvKeyPath string, timeout time.Duration) (prvKey *ecdsa.PrivateKey, err error) { +// NewECDSAPrvKeyFromReader creates a private key from io.Reader +func NewECDSAPrvKeyFromReader(reader io.Reader) (prvKey *ecdsa.PrivateKey, err error) { var prvkeyBuf []byte - if prvkeyBuf, err = GetDataAtPath(prvKeyPath, timeout); err != nil { + if prvkeyBuf, err = ioutil.ReadAll(reader); err != nil { return } return jwt.ParseECPrivateKeyFromPEM(prvkeyBuf) } -// NewECDSAPubKey returns a public key from the path -func NewECDSAPubKey(pubKeyPath string, timeout time.Duration) (pubKey *ecdsa.PublicKey, err error) { +// NewECDSAPubKeyFromReader returns a public key from io.Reader +func NewECDSAPubKeyFromReader(reader io.Reader) (pubKey *ecdsa.PublicKey, err error) { var pubkeyBuf []byte - if pubkeyBuf, err = GetDataAtPath(pubKeyPath, timeout); err != nil { + if pubkeyBuf, err = ioutil.ReadAll(reader); err != nil { return } return jwt.ParseECPublicKeyFromPEM(pubkeyBuf) } -// getURLFile returns the file from URL -func getURLFile(urlVal string, timeout time.Duration) (body []byte, err error) { +// NewECDSAPrvKey creates a private key from the path +func NewECDSAPrvKey(prvKeyPath string, timeout time.Duration) (prvKey *ecdsa.PrivateKey, err error) { + var prvKeyBuf io.ReadCloser + if prvKeyBuf, err = GetReaderFromPath(prvKeyPath, timeout); err != nil { + return + } + prvKey, err = NewECDSAPrvKeyFromReader(prvKeyBuf) + prvKeyBuf.Close() + return +} + +// NewECDSAPubKey returns a public key from the path +func NewECDSAPubKey(pubKeyPath string, timeout time.Duration) (pubKey *ecdsa.PublicKey, err error) { + var pubKeyBuf io.ReadCloser + if pubKeyBuf, err = GetReaderFromPath(pubKeyPath, timeout); err != nil { + return + } + pubKey, err = NewECDSAPubKeyFromReader(pubKeyBuf) + pubKeyBuf.Close() + return +} + +// GetReaderFromPath returns the reader at the given path +func GetReaderFromPath(path string, timeout time.Duration) (r io.ReadCloser, err error) { + if !IsURL(path) { + return os.Open(path) + } httpClient := http.Client{ Timeout: timeout, } var resp *http.Response - if resp, err = httpClient.Get(urlVal); err != nil { + if resp, err = httpClient.Get(path); err != nil { return } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { + resp.Body.Close() err = fmt.Errorf("http status error: %v", resp.StatusCode) return } - return ioutil.ReadAll(resp.Body) -} - -func GetDataAtPath(path string, timeout time.Duration) (body []byte, err error) { - if IsURL(path) { - return getURLFile(path, timeout) - } - var file *os.File - if file, err = os.Open(path); err != nil { - return - } - body, err = ioutil.ReadAll(file) - file.Close() - return + return resp.Body, nil } +// EncodeBase64JSON encodes the structure in json and then the string in base64 func EncodeBase64JSON(val interface{}) (enc string, err error) { var b []byte if b, err = json.Marshal(val); err != nil { @@ -89,6 +102,7 @@ func EncodeBase64JSON(val interface{}) (enc string, err error) { return } +// DecodeBase64JSON decodes the base64 json string in the given interface func DecodeBase64JSON(data string, val interface{}) (err error) { var b []byte if b, err = jwt.DecodeSegment(data); err != nil { @@ -97,6 +111,7 @@ func DecodeBase64JSON(data string, val interface{}) (err error) { return json.Unmarshal(b, val) } +// RemoveWhiteSpaces removes white spaces from string func RemoveWhiteSpaces(str string) string { rout := make([]rune, 0, len(str)) for _, r := range str { diff --git a/utils/stir_shaken_utils_test.go b/utils/stir_shaken_utils_test.go new file mode 100644 index 000000000..4216230eb --- /dev/null +++ b/utils/stir_shaken_utils_test.go @@ -0,0 +1,93 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package utils + +import ( + "bytes" + "math" + "reflect" + "testing" +) + +func TestRemoveWhiteSpaces(t *testing.T) { + strWithWS := ` A String + With White Spaces` + expected := `AStringWithWhiteSpaces` + if rply := RemoveWhiteSpaces(strWithWS); rply != expected { + t.Errorf("Expected: %q, received: %q", expected, rply) + } +} + +func TestEncodeBase64JSON(t *testing.T) { + var args interface{} + args = math.NaN() + if _, err := EncodeBase64JSON(args); err == nil { + t.Errorf("Expected error") + } + args = map[string]interface{}{"Q": 1} + expected := `eyJRIjoxfQ` + if rply, err := EncodeBase64JSON(args); err != nil { + t.Error(err) + } else if rply != expected { + t.Errorf("Expected: %q,received: %q", expected, rply) + } +} + +func TestDecodeBase64JSON(t *testing.T) { + args := `eyJRIjoxfQ` + var rply1 string + if err := DecodeBase64JSON(args, &rply1); err == nil { + t.Errorf("Expected error") + } + var rply2 map[string]interface{} + expected := map[string]interface{}{"Q": 1.} + if err := DecodeBase64JSON(args, &rply2); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rply2) { + t.Errorf("Expected: %s,received: %s", ToJSON(expected), ToJSON(rply2)) + } + args = `eyJRIjoxfQ,` + if err := DecodeBase64JSON(args, &rply2); err == nil { + t.Errorf("Expected error") + } +} + +type testErrReader struct{} + +func (testErrReader) Read([]byte) (int, error) { return 0, ErrNotFound } + +func TestNewECDSAPrvKeyFromReader(t *testing.T) { + if _, err := NewECDSAPrvKeyFromReader(new(testErrReader)); err == nil { + t.Errorf("Expected error") + } + r := bytes.NewBuffer([]byte("invalid certificate")) + if _, err := NewECDSAPrvKeyFromReader(r); err == nil { + t.Errorf("Expected error") + } +} + +func TestNewECDSAPubKeyFromReader(t *testing.T) { + if _, err := NewECDSAPubKeyFromReader(new(testErrReader)); err == nil { + t.Errorf("Expected error") + } + r := bytes.NewBuffer([]byte("invalid certificate")) + if _, err := NewECDSAPubKeyFromReader(r); err == nil { + t.Errorf("Expected error") + } +}