...

Source file src/github.com/lestrrat-go/jwx/jwt/openid/birthdate.go

Documentation: github.com/lestrrat-go/jwx/jwt/openid

     1  package openid
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"regexp"
     8  	"strconv"
     9  
    10  	"github.com/lestrrat-go/jwx/internal/json"
    11  
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // https://openid.net/specs/openid-connect-core-1_0.html
    16  //
    17  // End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format.
    18  // The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY
    19  // format is allowed. Note that depending on the underlying platform's date related function,
    20  // providing just year can result in varying month and day, so the implementers need to
    21  // take this factor into account to correctly process the dates.
    22  
    23  type BirthdateClaim struct {
    24  	year  *int
    25  	month *int
    26  	day   *int
    27  }
    28  
    29  func (b BirthdateClaim) Year() int {
    30  	if b.year == nil {
    31  		return 0
    32  	}
    33  	return *(b.year)
    34  }
    35  
    36  func (b BirthdateClaim) Month() int {
    37  	if b.month == nil {
    38  		return 0
    39  	}
    40  	return *(b.month)
    41  }
    42  
    43  func (b BirthdateClaim) Day() int {
    44  	if b.day == nil {
    45  		return 0
    46  	}
    47  	return *(b.day)
    48  }
    49  
    50  func (b *BirthdateClaim) UnmarshalJSON(data []byte) error {
    51  	var s string
    52  	if err := json.Unmarshal(data, &s); err != nil {
    53  		return errors.Wrap(err, `failed to unmarshal JSON string for birthdate claim`)
    54  	}
    55  
    56  	if err := b.Accept(s); err != nil {
    57  		return errors.Wrap(err, `failed to accept JSON value for birthdate claim`)
    58  	}
    59  	return nil
    60  }
    61  
    62  func tointptr(v int64) *int {
    63  	i := int(v)
    64  	return &i
    65  }
    66  
    67  var birthdateRx = regexp.MustCompile(`^(\d{4})-(\d{2})-(\d{2})$`)
    68  
    69  // Accepts a value read from JSON, and converts it to a BirthdateClaim.
    70  // This method DOES NOT verify the correctness of a date.
    71  // Consumers should check for validity of dates such as Apr 31 et al
    72  func (b *BirthdateClaim) Accept(v interface{}) error {
    73  	b.year = nil
    74  	b.month = nil
    75  	b.day = nil
    76  	switch v := v.(type) {
    77  	case *BirthdateClaim:
    78  		if ptr := v.year; ptr != nil {
    79  			year := *ptr
    80  			b.year = &year
    81  		}
    82  		if ptr := v.month; ptr != nil {
    83  			month := *ptr
    84  			b.month = &month
    85  		}
    86  		if ptr := v.day; ptr != nil {
    87  			day := *ptr
    88  			b.day = &day
    89  		}
    90  		return nil
    91  	case string:
    92  		// yeah, yeah, regexp is slow. PR's welcome
    93  		indices := birthdateRx.FindStringSubmatchIndex(v)
    94  		if indices == nil {
    95  			return errors.New(`invalid pattern for birthdate`)
    96  		}
    97  		var tmp BirthdateClaim
    98  
    99  		// Okay, this really isn't kosher, but we're doing this for
   100  		// the coverage game... Because birthdateRx already checked that
   101  		// the string contains 3 strings with consecutive decimal values
   102  		// we can assume that strconv.ParseInt always succeeds.
   103  		// strconv.ParseInt (and strconv.ParseUint that it uses internally)
   104  		// only returns range errors, so we should be safe.
   105  		year, _ := strconv.ParseInt(v[indices[2]:indices[3]], 10, 64)
   106  		if year <= 0 {
   107  			return errors.New(`failed to parse birthdate year`)
   108  		}
   109  		tmp.year = tointptr(year)
   110  
   111  		month, _ := strconv.ParseInt(v[indices[4]:indices[5]], 10, 64)
   112  		if month <= 0 {
   113  			return errors.New(`failed to parse birthdate month`)
   114  		}
   115  		tmp.month = tointptr(month)
   116  
   117  		day, _ := strconv.ParseInt(v[indices[6]:indices[7]], 10, 64)
   118  		if day <= 0 {
   119  			return errors.New(`failed to parse birthdate day`)
   120  		}
   121  		tmp.day = tointptr(day)
   122  
   123  		*b = tmp
   124  		return nil
   125  	default:
   126  		return errors.Errorf(`invalid type for birthdate: %T`, v)
   127  	}
   128  }
   129  
   130  func (b BirthdateClaim) encode(dst io.Writer) {
   131  	fmt.Fprintf(dst, "%04d-%02d-%02d", b.Year(), b.Month(), b.Day())
   132  }
   133  
   134  func (b BirthdateClaim) String() string {
   135  	var buf bytes.Buffer
   136  	b.encode(&buf)
   137  	return buf.String()
   138  }
   139  
   140  func (b BirthdateClaim) MarshalText() ([]byte, error) {
   141  	var buf bytes.Buffer
   142  	b.encode(&buf)
   143  	return buf.Bytes(), nil
   144  }
   145  

View as plain text