Skip to content

Empty getUserContext map implementation sets a Pricing-Token in RenewTokenFilter #54

@pgmarc

Description

@pgmarc

Given the following Pricing2Yaml file:

syntaxVersion: 2.1
saasName: PetClinic SaaS
createdAt: "2025-04-21"
version: "1.0.0"
currency: EUR

features:
  hotel:
    description: Acceso al hotel de mascotas
    valueType: BOOLEAN
    defaultValue: false
    expression: planContext['features']['hotel']
    type: DOMAIN

  adoptions:
    description: Acceso al sistema de adopciones
    valueType: BOOLEAN
    defaultValue: false
    expression: planContext['features']['adoptions']
    type: DOMAIN

  onlineConsultation:
    description: Consultas online con veterinarios
    valueType: BOOLEAN
    defaultValue: false
    expression: planContext['features']['onlineConsultation']
    type: DOMAIN

  pets:
    description: Control de mascotas por usuario
    valueType: BOOLEAN
    defaultValue: true
    expression: userContext['pets'] < planContext['usageLimits']['maxPets']
    type: DOMAIN

  visits:
    description: Control de número de visitas por mes
    valueType: BOOLEAN
    defaultValue: true
    expression: userContext['monthlyVisits'] < planContext['usageLimits']['maxVisits']
    type: DOMAIN

usageLimits:
  maxPets:
    description: Número máximo de mascotas por usuario
    valueType: NUMERIC
    defaultValue: 50
    unit: pet
    type: NON_RENEWABLE
    linkedFeatures: [pets]

  maxVisits:
    description: Número máximo de visitas por mes
    valueType: NUMERIC
    defaultValue: 100
    unit: visit
    type: NON_RENEWABLE
    linkedFeatures: [visits]

plans:
  BASIC:
    description: Plan gratuito sin SLA
    price: 0.0
    unit: user/month
    features:
      hotel: { value: false }
      adoptions: { value: false }
      onlineConsultation: { value: false }
    usageLimits:
      maxPets: { value: 50 }
      maxVisits: { value: 100 }

  GOLD:
    description: Plan con soporte extendido y servicios adicionales
    price: 5.0
    unit: user/month
    features:
      hotel: { value: true }
      adoptions: { value: true }
      onlineConsultation: { value: false }
    usageLimits:
      maxPets: { value: 200 }
      maxVisits: { value: 400 }

  PLATINUM:
    description: Plan premium con todas las funcionalidades y SLA completo
    price: 10.0
    unit: user/month
    features:
      hotel: { value: true }
      adoptions: { value: true }
      onlineConsultation: { value: true }
    usageLimits:
      maxPets: { value: 100000 }  # equivalente a "ilimitado"
      maxVisits: { value: 100000 }

And the follwing dummy implementation of PricingContext interface (Notice the empty map of getUserContext):

package org.springframework.samples.petclinic.configuration;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.samples.petclinic.owner.Owner;
import org.springframework.samples.petclinic.user.User;
import org.springframework.samples.petclinic.user.UserService;
import org.springframework.stereotype.Component;

import io.github.isagroup.PricingContext;

@Component
public class PricingConfiguration extends PricingContext {

    @Autowired
    private UserService userService;

    @Value("${petclinic.app.jwtSecret}") private String secret;
    
    @Override 
    public String getJwtSecret() { 
        return secret; 
    }

    @Override
    public String getConfigFilePath() { 
        return "pricing/pricing.yml"; 
    }

    @Override 
    public Map<String, Object> getUserContext() {
        Map<String, Object> userContext = new HashMap<>();
        // Add the logic to retrieve all the data needed within the user context,
        // such as his current usage of the feature XXX.
        return userContext; 
    }
    
    @Override 
    public String getUserPlan() {
        try{
            // Get the current user from the context
            User user = userService.findCurrentUser();
            Owner owner = userService.findOwnerByUser(user.getId());
            return owner.getClinic().getPlan().toString(); 
        } catch (Exception e) {
            // Handle the case where the user is not found or any other exception
            return "BASIC";
        }
    }
}

Doing any call to some endpoint of an API, take as an example Petclinic endpoint

POST http://example.org/api/v1/pets
Content-Type: application/json
Accept: application/json
uthorization: Bearer {{login.response.body.$.token}}
//Pricing-Token: {{login.response.body.$.pricingToken}}

{
    "id": null,
    "name": "Example",
    "birthDate": "2025-03-27",
    "type": {
        "id": 5,
        "name": "bird"
    },
    "owner": {}
}

The filter RenewTokenFilter surprisingly answers with a Pricing-Token:

Pricing-Token: eyJhbGciOiJIUzUxMiJ9.eyJmZWF0dXJlcyI6eyJwZXRzIjp7ImV2YWwiOnRydWUsInVzZWQiOm51bGwsImxpbWl0IjoxMDAwMDB9LCJhZG9wdGlvbnMiOnsiZXZhbCI6dHJ1ZSwidXNlZCI6bnVsbCwibGltaXQiOm51bGx9LCJ2aXNpdHMiOnsiZXZhbCI6dHJ1ZSwidXNlZCI6bnVsbCwibGltaXQiOjEwMDAwMH0sImhvdGVsIjp7ImV2YWwiOnRydWUsInVzZWQiOm51bGwsImxpbWl0IjpudWxsfSwib25saW5lQ29uc3VsdGF0aW9uIjp7ImV2YWwiOnRydWUsInVzZWQiOm51bGwsImxpbWl0IjpudWxsfX0sInN1YiI6IkRlZmF1bHQiLCJleHAiOjE3NDU1MDIwMTIsInVzZXJDb250ZXh0Ijp7fSwiaWF0IjoxNzQ1NDE1NjEyLCJwbGFuQ29udGV4dCI6eyJuYW1lIjoiUExBVElOVU0iLCJkZXNjcmlwdGlvbiI6IlBsYW4gcHJlbWl1bSBjb24gdG9kYXMgbGFzIGZ1bmNpb25hbGlkYWRlcyB5IFNMQSBjb21wbGV0byIsInByaWNlIjoxMC4wLCJ1bml0IjoidXNlci9tb250aCIsImlzUHJpdmF0ZSI6ZmFsc2UsImZlYXR1cmVzIjp7InBldHMiOnRydWUsImFkb3B0aW9ucyI6dHJ1ZSwidmlzaXRzIjp0cnVlLCJob3RlbCI6dHJ1ZSwib25saW5lQ29uc3VsdGF0aW9uIjp0cnVlfSwidXNhZ2VMaW1pdHMiOnsibWF4VmlzaXRzIjoxMDAwMDAsIm1heFBldHMiOjEwMDAwMH19fQ.UOLBaz7CVGbVG5cIv2FsovCQVsY25M54C9C5asP03dPNHHypX43lrPtbzSId0n3K0WUkZHAtRXnKKqxF0QAIFw

Further investigation needs to be done. This is a remainder for mantainers

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions