# Using Plugin Permissions

import { Steps } from '@astrojs/starlight/components';
import ShowSolution from '@components/ShowSolution.astro'
import Cta from '@fragments/cta.mdx';

The goal of this exercise is to get a better understanding on how
plugin permissions can be enabled or disabled, where they are described
and how to use default permissions of plugins.

At the end you will have the ability to find and use permissions of
arbitrary plugins and understand how to custom tailor existing permissions.
You will have an example Tauri application where a plugin and plugin specific
permissions are used.

<Steps>

1. ### Create Tauri Application

    Create your Tauri application.
    In our example we will facilitate [`create-tauri-app`](https://github.com/tauri-apps/create-tauri-app):

    <Cta />

    We will proceed in this step-by-step explanation with `pnpm` but you can choose another
    package manager and replace it in the commands accordingly.


    <ShowSolution>
    ```
    pnpm create tauri-app
    ```

    ```
    ✔ Project name · plugin-permission-demo
    ✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, bun)
    ✔ Choose your package manager · pnpm
    ✔ Choose your UI template · Vanilla
    ✔ Choose your UI flavor · TypeScript

    Template created! To get started run:
    cd plugin-permission-demo
    pnpm install
    pnpm tauri dev
    ```
    </ShowSolution>

2. ### Add the `file-system` Plugin to Your Application

    To search for existing plugins you can use multiple resources.

    The most straight forward way would be to check out if your plugin is already
    in the [Plugins](/plugin/) section of the documentation and therefore part of Tauri's
    maintained plugin set.
    The Filesystem plugin is part of the Tauri plugin workspace and you can add it to
    your project by following the [instructions](/plugin/file-system/#setup).

    If the plugin is part of the community effort you can most likely find it
    on [crates.io](https://crates.io/search?q=tauri-plugin-) when searching for `tauri-plugin-<your plugin name>`.

    <ShowSolution>
    If it is an existing plugin from our workspace you can use the automated way:

    ```
    pnpm tauri add fs
    ```

    If you have found it on [crates.io](https://crates.io/crates/tauri-plugin-fs)
    you need to manually add it as a dependency and modify the Tauri builder
    to initialize the plugin:

    ```sh
    cargo add tauri-plugin-fs
    ```

    Modify `lib.rs` to initialize the plugin:

    ```rust title="src-tauri/src/lib.rs" ins={4}
    #[cfg_attr(mobile, tauri::mobile_entry_point)]
    fn run() {
      tauri::Builder::default()
        .plugin(tauri_plugin_fs::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
    }
    ```
    </ShowSolution>

3. ### Understand the Default Permissions of the `fs` Plugin
    
    Each plugin has a `default` permission set, which contains
    all permissions and scopes to use the plugin out of the box
    with a reasonable minimal feature set.
    
    In the case of official maintained plugins you can find a
    rendered description in the documentation
    (eg. [fs default](/plugin/file-system/#default-permission)).

    In case you are figuring this out for a community plugin you
    need to check out the source code of the plugin.
    This should be defined in `your-plugin/permissions/default.toml`.

    <ShowSolution>
    ```
    "$schema" = "schemas/schema.json"

    [default]
    description = """
    # Tauri `fs` default permissions

    This configuration file defines the default permissions granted
    to the filesystem.

    ### Granted Permissions

    This default permission set enables all read-related commands and
    allows access to the `$APP` folder and sub directories created in it.
    The location of the `$APP` folder depends on the operating system,
    where the application is run.

    In general the `$APP` folder needs to be manually created
    by the application at runtime, before accessing files or folders
    in it is possible.

    ### Denied Permissions

    This default permission set prevents access to critical components
    of the Tauri application by default.
    On Windows the webview data folder access is denied.

    """
    permissions = ["read-all", "scope-app-recursive", "deny-default"]

    ```
    </ShowSolution>

  4. ### Find the Right Permissions
      
      This step is all about finding the permissions you need to
      for your commands to be exposed to the frontend with the minimal
      access to your system.

      The `fs` plugin has autogenerated permissions which will disable
      or enable individual commands and allow or disable global scopes.

      These can be found in the [documentation](/plugin/file-system/#permission-table)
      or in the source code of the plugin (`fs/permissions/autogenerated`).

      Let us assume we want to enable writing to a text file `test.txt`
      located in the users `$HOME` folder.

      For this we would search in the autogenerated permissions for a
      permission to enable writing to text files like `allow-write-text-file`
      and then for a scope which would allow us to access the `$HOME/test.txt`
      file.

      We need to add these to our `capabilities` section in our
      `src-tauri/tauri.conf.json` or in a file in the `src-tauri/capabilities/` folder.
      By default there is already a capability in `src-tauri/capabilities/default.json` we 
      can modify.

      <ShowSolution>

      ```json title="src-tauri/capabilities/default.json" del={18} ins={19}
      {
        "$schema": "../gen/schemas/desktop-schema.json",
        "identifier": "default",
        "description": "Capability for the main window",
        "windows": [
          "main"
        ],
        "permissions": [
          "path:default",
          "event:default",
          "window:default",
          "app:default",
          "image:default",
          "resources:default",
          "menu:default",
          "tray:default",
          "shell:allow-open",
          "fs:default",
          "fs:allow-write-text-file",
        ]
      }
      ```

      </ShowSolution>

      Since there are only autogenerated scopes in the `fs` plugin to
      access the full `$HOME` folder, we need to configure our own scope.
      This scope should be only enabled for the `write-text-file` command
      and should only expose our `test.txt` file.

      <ShowSolution>
      ```json title="src-tauri/capabilities/default.json" del={18} ins={19-22}
         {
        "$schema": "../gen/schemas/desktop-schema.json",
        "identifier": "default",
        "description": "Capability for the main window",
        "windows": [
          "main"
        ],
        "permissions": [
          "path:default",
          "event:default",
          "window:default",
          "app:default",
          "image:default",
          "resources:default",
          "menu:default",
          "tray:default",
          "shell:allow-open",
          "fs:allow-write-text-file",
          {
            "identifier": "fs:allow-write-text-file",
            "allow": [{ "path": "$HOME/test.txt" }]
          },
        ]
      }
      ```
      </ShowSolution>
    5. ### Test Permissions in Practice

        After we have added the necessary permission we want to
        confirm that our application can access the file and write
        it's content.

        <ShowSolution>
        We can use this snippet in our application to write to the file:

        ```ts title="src/main.ts"
        import { writeTextFile, BaseDirectory } from '@tauri-apps/plugin-fs';

        let greetInputEl: HTMLInputElement | null;

        async function write(message: string) {
            await writeTextFile('test.txt', message, { baseDir: BaseDirectory.Home });
        }

        window.addEventListener("DOMContentLoaded", () => {
          greetInputEl = document.querySelector("#greet-input");
          document.querySelector("#greet-form")?.addEventListener("submit", (e) => {
            e.preventDefault();
            if (!greetInputEl )
              return;

            write(greetInputEl.value == "" ? "No input provided": greetInputEl.value);

          });
        });

        ```

        Replacing the `src/main.ts` with this snippet means we do not need to modify the default `index.html`,
        when using the plain Vanilla+Typescript app.
        Entering any input into the input field of the running app will be
        written to the file on submit.

        Let's test now in practice:

        ```
        pnpm run tauri dev
        ```

        After writing into the input and clicking "Submit",
        we can check via our terminal emulator or by manually opening the
        file in your home folder.

        ```
        cat $HOME/test.txt
        ```

        You should be presented with your input and finished learning about using permissions from plugins in Tauri applications.
        🥳


        If you encountered this error:

        ```sh
        [Error] Unhandled Promise Rejection: fs.write_text_file not allowed. Permissions associated with this command: fs:allow-app-write, fs:allow-app-write-recursive, fs:allow-appcache-write, fs:allow-appcache-write-recursive, fs:allow-appconf...
        (anonymous function) (main.ts:5)
        ```
        Then you very likely did not properly follow the [previous instructions](#find-the-right-permissions).
        </ShowSolution>

 </Steps>