github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/services/authorizations/encoding.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package authorizations
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/base64"
    23  	"github.com/aacfactory/avro"
    24  	"github.com/aacfactory/errors"
    25  	"github.com/aacfactory/fns/commons/signatures"
    26  	"github.com/aacfactory/fns/context"
    27  	"github.com/aacfactory/fns/services"
    28  	"strings"
    29  )
    30  
    31  type TokenEncoder interface {
    32  	services.Component
    33  	Encode(ctx context.Context, param Authorization) (token Token, err error)
    34  	Decode(ctx context.Context, token Token) (result Authorization, err error)
    35  }
    36  
    37  type defaultTokenEncoderConfig struct {
    38  	Key string `json:"key" avro:"key"`
    39  }
    40  
    41  func DefaultTokenEncoder() TokenEncoder {
    42  	return &defaultTokenEncoder{}
    43  }
    44  
    45  type defaultTokenEncoder struct {
    46  	signature signatures.Signature
    47  }
    48  
    49  func (encoder *defaultTokenEncoder) Name() (name string) {
    50  	return "authorizations:encoder:default"
    51  }
    52  
    53  func (encoder *defaultTokenEncoder) Construct(options services.Options) (err error) {
    54  	config := defaultTokenEncoderConfig{}
    55  	configErr := options.Config.As(&config)
    56  	if configErr != nil {
    57  		err = errors.Warning("authorizations: build default token encoder failed").WithMeta("encoder", encoder.Name()).WithCause(configErr)
    58  		return
    59  	}
    60  	key := strings.TrimSpace(config.Key)
    61  	if key == "" {
    62  		err = errors.Warning("authorizations: build default token encoder failed").WithMeta("encoder", encoder.Name()).WithCause(errors.Warning("key is require"))
    63  		return
    64  	}
    65  	encoder.signature = signatures.HMAC([]byte(key))
    66  	return
    67  }
    68  
    69  func (encoder *defaultTokenEncoder) Shutdown(_ context.Context) {
    70  	return
    71  }
    72  
    73  func (encoder *defaultTokenEncoder) Encode(_ context.Context, param Authorization) (token Token, err error) {
    74  	p, encodeErr := avro.Marshal(param)
    75  	if encodeErr != nil {
    76  		err = errors.Warning("authorizations: encode token failed").WithMeta("encoder", encoder.Name()).WithCause(encodeErr)
    77  		return
    78  	}
    79  	pb := make([]byte, base64.URLEncoding.EncodedLen(len(p)))
    80  	base64.URLEncoding.Encode(pb, p)
    81  	pbl := len(pb)
    82  
    83  	s := encoder.signature.Sign(p)
    84  	sb := make([]byte, base64.URLEncoding.EncodedLen(len(s)))
    85  	base64.URLEncoding.Encode(sb, s)
    86  	sbl := len(sb)
    87  
    88  	token = make(Token, 4+pbl+1+sbl)
    89  	token[0] = 'F'
    90  	token[1] = 'n'
    91  	token[2] = 's'
    92  	token[3] = ' '
    93  
    94  	copy(token[4:4+pbl], pb)
    95  	token[4+pbl] = '.'
    96  	copy(token[4+pbl+1:], sb)
    97  
    98  	return
    99  }
   100  
   101  func (encoder *defaultTokenEncoder) Decode(_ context.Context, token Token) (result Authorization, err error) {
   102  	if len(token) < 4 {
   103  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(errors.Warning("token is invalid"))
   104  		return
   105  	}
   106  	after, found := bytes.CutPrefix(token, []byte{'F', 'n', 's', ' '})
   107  	if !found {
   108  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(errors.Warning("token is invalid"))
   109  		return
   110  	}
   111  	pos := bytes.IndexByte(after, '.')
   112  	if pos < 1 {
   113  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(errors.Warning("token is invalid"))
   114  		return
   115  	}
   116  	p, pErr := base64.URLEncoding.DecodeString(string(after[0:pos]))
   117  	if pErr != nil {
   118  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(pErr)
   119  		return
   120  	}
   121  	s, sErr := base64.URLEncoding.DecodeString(string(after[pos+1:]))
   122  	if sErr != nil {
   123  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(sErr)
   124  		return
   125  	}
   126  	if !encoder.signature.Verify(p, s) {
   127  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(errors.Warning("token is invalid"))
   128  		return
   129  	}
   130  	decodeErr := avro.Unmarshal(p, &result)
   131  	if decodeErr != nil {
   132  		err = errors.Warning("authorizations: decode token failed").WithMeta("encoder", encoder.Name()).WithCause(decodeErr)
   133  		return
   134  	}
   135  	return
   136  }