Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for OpenFGA Initialization #57

Open
rwinch opened this issue Jun 24, 2024 · 3 comments
Open

Add Support for OpenFGA Initialization #57

rwinch opened this issue Jun 24, 2024 · 3 comments

Comments

@rwinch
Copy link

rwinch commented Jun 24, 2024

Spring Boot provides support for Database initialization such that if schema.sql or data.sql are found on the classpath they are imported into the database.

It would be nice if OpenFGA could add similar support for importing the initial model and data into OpenFGA. An example can be found in fga-spring-examples.

@Akshayp02
Copy link

Hello @rwinch ,

I would like to contribute to this issue. Could you please assign it to me and provide any relevant references or guidance?

Thank you!

@rwinch
Copy link
Author

rwinch commented Oct 4, 2024

Hello! You will probably need to coordinate with @jimmyjames as I'm not a commiter on this project.

@holgerstolzenberg
Copy link

holgerstolzenberg commented Nov 11, 2024

I just tried the starter and it works quite well so far but is in a very early stage.
I have put together my own stores provisioner, I will share the code here, maybe it helps:

@Slf4j
public class StoresClient {

  private final OpenFgaClient openFgaClient;

  public StoresClient(final OpenFgaClient openFgaClient) {
    this.openFgaClient = openFgaClient;
  }

  @NotNull
  public String createStore(final String storeName) {
    checkNotBlank(storeName, "storeName");
    log.info("Provisioning authorization store: {}", storeName);

    final var request = new CreateStoreRequest();
    request.setName(storeName);

    try {
      final var createResponse = openFgaClient.createStore(request).get();
      log.debug("Store created: {} => {}", createResponse.getId(), createResponse.getName());
      return createResponse.getId();
    } catch (final InterruptedException | FgaInvalidParameterException | ExecutionException cause) {
      throw new OpenFgaException(String.format("Error creating store '%s'.", storeName), cause);
    }
  }

  @NotNull
  public ClientListStoresResponse getClientListStoresResponse() {
    try {
      return openFgaClient.listStores().get();
    } catch (final FgaInvalidParameterException | ExecutionException | InterruptedException cause) {
      throw new OpenFgaException("Error listing stores", cause);
    }
  }
}

and

@Slf4j
@Component
public class OpenFgaStoreAutoProvisioner implements ApplicationListener<ApplicationStartedEvent> {
  private final OpenFgaClient openFgaClient;
  private final StoresClient storesClient;
  private final String storeName;

  public OpenFgaStoreAutoProvisioner(
      final OpenFgaClient openFgaClient,
      final StoresClient storesClient,
      final AuthorizationConfigurationProperties properties) {
    this.openFgaClient = openFgaClient;
    this.storesClient = storesClient;
    this.storeName = properties.store().name();
  }

  @Override
  @RetryableCall(label = "openfga-store-provision")
  public void onApplicationEvent(@NotNull final ApplicationStartedEvent event) {
    log.info("Application started event received after: {}", event.getTimeTaken());
    provision();
    log.info("Store auto-provision completed");
  }

  @Override
  public boolean supportsAsyncExecution() {
    return false;
  }

  private void provision() {
    final var listResponse = storesClient.getClientListStoresResponse();

    final var storeId =
        listResponse.getStores().stream()
            .filter(s -> s.getName().equalsIgnoreCase(storeName))
            .map(Store::getId)
            .findFirst()
            .orElseGet(() -> storesClient.createStore(storeName));
    log.info("Store: {} => {}", storeName, storeId);

    openFgaClient.setStoreId(storeId);
    log.info("Store ID applied to client: {}", storeId);
  }
}

The code uses some custom AuthorizationConfigurationProperties, this can easily be exchanged with your (own) implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants