diff --git a/go.mod b/go.mod index a4361c1..f84a743 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/urfave/cli v1.22.5 golang.org/x/crypto v0.14.0 + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 gopkg.in/yaml.v2 v2.4.0 ) @@ -44,7 +45,6 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/internal/context/context.go b/internal/context/context.go index 76cf5a6..4a1bc8d 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -50,6 +50,8 @@ type UDMContext struct { SubscriptionOfSharedDataChange sync.Map // subscriptionID as key SuciProfiles []suci.SuciProfile EeSubscriptionIDGenerator *idgenerator.IDGenerator + ClientMap sync.Map + TokenMap sync.Map } type UdmUeContext struct { diff --git a/internal/sbi/consumer/nf_accesstoken.go b/internal/sbi/consumer/nf_accesstoken.go new file mode 100644 index 0000000..00b9754 --- /dev/null +++ b/internal/sbi/consumer/nf_accesstoken.go @@ -0,0 +1,88 @@ +package consumer + +import ( + "context" + "time" + + udm_context "github.com/free5gc/udm/internal/context" + + "github.com/free5gc/udm/internal/logger" + "github.com/free5gc/udm/pkg/factory" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Nnrf_AccessToken" + "github.com/free5gc/openapi/models" + + "github.com/antihax/optional" + "golang.org/x/oauth2" +) + +func GetTokenCtx(scope, targetNF string) (context.Context, *models.ProblemDetails, error) { + if factory.UdmConfig.GetOAuth() { + tok, pd, err := sendAccTokenReq(scope, targetNF) + if err != nil { + return nil, pd, err + } + return context.WithValue(context.Background(), + openapi.ContextOAuth2, tok), pd, nil + } + return context.TODO(), nil, nil +} + +func sendAccTokenReq(scope, targetNF string) (oauth2.TokenSource, *models.ProblemDetails, error) { + logger.ConsumerLog.Infof("Send Access Token Request") + var client *Nnrf_AccessToken.APIClient + udmSelf := udm_context.Getself() + // Set client and set url + configuration := Nnrf_AccessToken.NewConfiguration() + configuration.SetBasePath(udmSelf.NrfUri) + if val, ok := udmSelf.ClientMap.Load(configuration); ok { + client = val.(*Nnrf_AccessToken.APIClient) + } else { + client = Nnrf_AccessToken.NewAPIClient(configuration) + udmSelf.ClientMap.Store(configuration, client) + } + + var tok models.AccessTokenRsp + + if val, ok := udmSelf.TokenMap.Load(scope); ok { + tok = val.(models.AccessTokenRsp) + if int32(time.Now().Unix()) < tok.ExpiresIn { + logger.ConsumerLog.Infof("Token is not expired") + token := &oauth2.Token{ + AccessToken: tok.AccessToken, + TokenType: tok.TokenType, + Expiry: time.Unix(int64(tok.ExpiresIn), 0), + } + return oauth2.StaticTokenSource(token), nil, nil + } + } + + tok, res, err := client.AccessTokenRequestApi.AccessTokenRequest(context.Background(), "client_credentials", + udmSelf.NfId, scope, &Nnrf_AccessToken.AccessTokenRequestParamOpts{ + NfType: optional.NewInterface(models.NfType_UDM), + TargetNfType: optional.NewInterface(targetNF), + }) + if err == nil { + udmSelf.TokenMap.Store(scope, tok) + token := &oauth2.Token{ + AccessToken: tok.AccessToken, + TokenType: tok.TokenType, + Expiry: time.Unix(int64(tok.ExpiresIn), 0), + } + return oauth2.StaticTokenSource(token), nil, err + } else if res != nil { + defer func() { + if resCloseErr := res.Body.Close(); resCloseErr != nil { + logger.ConsumerLog.Errorf("AccessTokenRequestApi response body cannot close: %+v", resCloseErr) + } + }() + if res.Status != err.Error() { + return nil, nil, err + } + problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) + return nil, &problem, err + } else { + return nil, nil, openapi.ReportError("server no response") + } +} diff --git a/internal/sbi/consumer/nf_management.go b/internal/sbi/consumer/nf_management.go index 6c3fc1e..acc9cd7 100644 --- a/internal/sbi/consumer/nf_management.go +++ b/internal/sbi/consumer/nf_management.go @@ -81,6 +81,11 @@ func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfil func SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err error) { logger.ConsumerLog.Infof("Send Deregister NFInstance") + ctx, pd, err := GetTokenCtx("nnrf-nfm", "NRF") + if err != nil { + return pd, err + } + udmSelf := udm_context.Getself() // Set client and set url configuration := Nnrf_NFManagement.NewConfiguration() @@ -89,7 +94,7 @@ func SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err erro var res *http.Response - res, err = client.NFInstanceIDDocumentApi.DeregisterNFInstance(context.Background(), udmSelf.NfId) + res, err = client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, udmSelf.NfId) if err == nil { return } else if res != nil { diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 0b67bf7..26be250 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -129,6 +129,13 @@ type Sbi struct { BindingIPv4 string `yaml:"bindingIPv4,omitempty" valid:"host,required"` // IP used to run the server in the node. Port int `yaml:"port,omitempty" valid:"port,required"` Tls *Tls `yaml:"tls,omitempty" valid:"optional"` + OAuth bool `yaml:"oauth,omitempty" valid:"optional"` +} + +func (c *Config) GetOAuth() bool { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.OAuth } func (s *Sbi) validate() (bool, error) {