github.com/circl-dev/go-swagger@v0.31.0/examples/composed-auth/auth/authorizers.go (about)

     1  package auth
     2  
     3  import (
     4  	"crypto/rsa"
     5  	"io/ioutil"
     6  
     7  	models "github.com/circl-dev/go-swagger/examples/composed-auth/models"
     8  	jwt "github.com/dgrijalva/jwt-go"
     9  	errors "github.com/go-openapi/errors"
    10  )
    11  
    12  const (
    13  	// currently unused: privateKeyPath = "keys/apiKey.prv"
    14  	publicKeyPath = "keys/apiKey.pem"
    15  	issuerName    = "example.com"
    16  )
    17  
    18  var (
    19  	userDb map[string]string
    20  
    21  	// Keys used to sign and verify our tokens
    22  	verifyKey *rsa.PublicKey
    23  	// currently unused: signKey   *rsa.PrivateKey
    24  )
    25  
    26  // roleClaims describes the format of our JWT token's claims
    27  type roleClaims struct {
    28  	Roles []string `json:"roles"`
    29  	jwt.StandardClaims
    30  }
    31  
    32  func init() {
    33  	// emulates the loading of a local users database
    34  	userDb = map[string]string{
    35  		"fred": "scrum",
    36  		"ivan": "terrible",
    37  	}
    38  
    39  	// loads public keys to verify our tokens
    40  	verifyKeyBuf, err := ioutil.ReadFile(publicKeyPath)
    41  	if err != nil {
    42  		panic("Cannot load public key for tokens")
    43  	}
    44  	verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyKeyBuf)
    45  	if err != nil {
    46  		panic("Invalid public key for tokens")
    47  	}
    48  }
    49  
    50  // Customized authorizer methods for our sample API
    51  
    52  // IsRegistered determines if the user is properly registered,
    53  // i.e if a valid username:password pair has been provided
    54  func IsRegistered(user, pass string) (*models.Principal, error) {
    55  	if password, ok := userDb[user]; ok {
    56  		if pass == password {
    57  			return &models.Principal{
    58  				Name: user,
    59  			}, nil
    60  		}
    61  	}
    62  	return nil, errors.New(401, "Unauthorized: not a registered user")
    63  }
    64  
    65  // IsReseller tells if the API key is a JWT signed by us with a claim to be a reseller
    66  func IsReseller(token string) (*models.Principal, error) {
    67  	claims, err := parseAndCheckToken(token)
    68  	if err == nil {
    69  		if claims.Issuer == issuerName && claims.Id != "" {
    70  			isReseller := false
    71  			for _, role := range claims.Roles {
    72  				if role == "reseller" {
    73  					isReseller = true
    74  					break
    75  				}
    76  			}
    77  			if isReseller {
    78  				return &models.Principal{
    79  					Name: claims.Id,
    80  				}, nil
    81  			}
    82  			return nil, errors.New(403, "Forbidden: insufficient API key privileges")
    83  		}
    84  	}
    85  	return nil, errors.New(401, "Unauthorized: invalid API key token: %v", err)
    86  }
    87  
    88  // HasRole tells if the Bearer token is a JWT signed by us with a claim to be
    89  // member of an authorization scope.
    90  // We verify that the claimed role is one of the passed scopes
    91  func HasRole(token string, scopes []string) (*models.Principal, error) {
    92  	claims, err := parseAndCheckToken(token)
    93  	if err == nil {
    94  		if claims.Issuer == issuerName {
    95  			isInScopes := false
    96  			claimedRoles := []string{}
    97  			for _, scope := range scopes {
    98  				for _, role := range claims.Roles {
    99  					if role == scope {
   100  						isInScopes = true
   101  						// we enrich the principal with all claimed roles within scope (hence: not breaking here)
   102  						claimedRoles = append(claimedRoles, role)
   103  					}
   104  				}
   105  			}
   106  			if isInScopes {
   107  				return &models.Principal{
   108  					Name:  claims.Id,
   109  					Roles: claimedRoles,
   110  				}, nil
   111  			}
   112  			return nil, errors.New(403, "Forbidden: insufficient privileges")
   113  		}
   114  	}
   115  	return nil, errors.New(401, "Unauthorized: invalid Bearer token: %v", err)
   116  }
   117  
   118  func parseAndCheckToken(token string) (*roleClaims, error) {
   119  	// the API key is a JWT signed by us with a claim to be a reseller
   120  	parsedToken, err := jwt.ParseWithClaims(token, &roleClaims{}, func(parsedToken *jwt.Token) (interface{}, error) {
   121  		// the key used to validate tokens
   122  		return verifyKey, nil
   123  	})
   124  
   125  	if err == nil {
   126  		if claims, ok := parsedToken.Claims.(*roleClaims); ok && parsedToken.Valid {
   127  			return claims, nil
   128  		}
   129  	}
   130  	return nil, err
   131  
   132  }