tauri-apps / tauri

Build smaller, faster, and more secure desktop and mobile applications with a web frontend.
https://tauri.app
Apache License 2.0
83.66k stars 2.51k forks source link

Invalid capability identifier gives non-helpful error message + why capability identifier cannot contain digits after first char? #8820

Closed thepyper closed 8 months ago

thepyper commented 8 months ago

Discussed in https://github.com/tauri-apps/tauri/discussions/8813

The original question was not precise, but after some debugging resolved in an issue in: https://github.com/tauri-apps/tauri/blob/dev/core/tauri-utils/src/acl/identifier.rs Here many possible errors are defined for identifier parsing error, but they're not exposed to user when something wrong happens in capability file parsing.

Also I'd point out in documentation that plugin name is required to have only alpha characters in it's name.. or remove that constrain from plugin name (unless there's some reason I don't know of course..)

Originally posted by **thepyper** February 7, 2024 Hi everybody! I've updated tauri from 2.0.0-alpha.21 to 2.0.0-beta.2, and I figured out that ACL is now denying my app to work :) I've added capabilities file (in src-tauri/capabilities) to allow all built-in plugins, that solved part of issues (many errors about mouse movement) but I cannot figure out how to allow my own plugin to work.. it worked before, now all it's commands are denied in my TypeScript part with errors like ". not allowed. Plugin did not define its manifest" Let's say my plugin is called "doggy" and has a "bark" command, how should I allow my main window (or even better any window) to interact with that command? Thanks!!
amrbashir commented 8 months ago

Thank you, the documentation for beta is still uncomplete and this is how we find about our shortcomings.

I think removing the alpha only limitation should be plausible, I believe alphanumeric is fine. cc @chippers @lucasfernog

lucasfernog commented 8 months ago

I don't see a reason why alphanumeric shouldn't be allowed.

martpie commented 7 months ago

I've added capabilities file (in src-tauri/capabilities) to allow all built-in plugins

Can you share how you did that? :) I am facing a similar issue trying to attach commands to local plugins, but the Capability schema is rather cryptic.

thepyper commented 7 months ago

Actually I copied from some example in documentation (can't remember exactly which, but from somewhere in v2 doc or maybe in examples from tauri git)... so, I created a "capabilities" sub-folder in the "src-tauri" folder, and created a file named "main.json" (but could be anything.json, files are matched on a .json and .toml basis I found somewhere in tauri code) with that content:

{
  "$schema": "./schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "path:default",
    "event:default",
    "window:default",
    "app:default",
    "resources:default",
    "menu:default",
    "tray:default",
    "window:allow-set-title"
  ]
}

On that same file I added also capabilities of my own plugin.. probably a better way could be creating different files with some logic, now I'm just collapsing all capability management into that single file. If you have multiwindow app probably you need to add them to "windows" list...

Hope that helps!

martpie commented 7 months ago

Thank you for your reply!

On that same file I added also capabilities of my own plugin.

It's this bit I'm mostly interested in. Did you place your plugin within the your app crate? or in a separate lib/crate folder at the root?

- my-plugin
- src-tauri

// or

- src-tauri
    - plugins
        - my-plugin

I've documented my current issue here just in case https://github.com/tauri-apps/tauri/issues/8962

thepyper commented 7 months ago

Beware I don't know if my way is correct/preferred way, but I did like that (works with tauri v2.0.0-beta1): 1) I have a separate crate with a library of my own, named cy3 2) Inside that crate I defined the plugin (so plugin has no dedicated crate, but lives in a separate crate from my app; don't think that is a requirement it's just I have a separate library) 3) In that same crate I added the build script and things to build up permissions automatically. More specific:

I have a file in my cy3 crate, in cy3/back/tauri_plugin_scada.rs containing plugin interface, like:

... some more use for me ...
use tauri::{
    plugin::{Builder, TauriPlugin},
    Runtime,
};
... some use from my crate...

#[tauri::command]
fn initialize() {}

#[tauri::command]
async fn add_alias(
    state: tauri::State<'_, ScadaManager>,
    alias: String,
    path: String,
) -> Result<(), Error> {
    state.add_alias(alias, path).await
}

... some more command functions defined ...

pub fn init<R: Runtime>() -> TauriPlugin<R> {
    Builder::new("scada")
        .invoke_handler(tauri::generate_handler![
            initialize,
            add_alias,
... some more command functions listed...
        ])
        .build()
}

Note the name of plugin is "scada" as you see in init function.

Then I added a "build.rs" file in cy3/build.rs containing:

pub fn main() {
    tauri_plugin::Builder::new(&[
        "add_alias",
... some more command names listed (named exactly as the functions of previous file)...
    ])
    .build();
}

Then some important bits are in cy3/Cargo.toml:

[package]
name    = "cy3"
... some more usual version / edition things ...
# This gives name to tauri plugin, don't know how
links   = "scada"

[dependencies]
... my own dependencies not related to tauri ...
tauri               = "2.0.0-beta"

[build-dependencies]
tauri-plugin        = { version = "2.0.0-beta", features = ["build"] }

Here you see you need to add the build dependency (that is required in build.rs) and that bit of black magic: the "links" keyword. Actually the supposed use for this key (see rust book) is related to linking libraries to your project, but tauri use that keyword to pass the plugin name information here and there (if I understood well, it's for the build script pass).

So that's all you need on your plugin side. When you build it, you'll see the permission directory (cy3/permissions) being created magically (actually the build.rs file does it, dig tauri source a bit to see);

Then, in the application you want to use the plugin: 1) You import your crate as a dependency in Cargo.toml 2) You add permission to use your commands in the "capabilities" directory

Specifically in my case, my app is named usv5, so in usv5/src-tauri/Cargo.toml:

... various things ...

[build-dependencies]
tauri-build = { version = "2.0.0-beta", features = [] }

[dependencies]
tauri = { version = "2.0.0-beta", features = [] }
... more dependencies ...
cy3 = { path = "../../cy3" }

[features]
# this feature is used for production builds or when `devPath` points to the filesystem
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]

In usv5/package.json:

{
  "name": "usv5",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "tauri": "tauri"
  },
  "dependencies": {
    "@tauri-apps/api": "^2.0.0-beta.0",
    "@tauri-apps/plugin-fs": "^2.0.0-beta.0",
    "handlebars": "^4.7.8"
  },
  "devDependencies": {
    "@tauri-apps/cli": "^2.0.0-beta.1",
    "@typescript-eslint/eslint-plugin": "^6.4.0",
    "eslint": "^8.56.0",
    "eslint-config-standard-with-typescript": "^43.0.1",
    "eslint-plugin-import": "^2.25.2",
    "eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
    "eslint-plugin-promise": "^6.0.0",
    "typescript": "^5.0.2",
    "vite": "^5.0.0"
  }
}

(nothing exactly related to plugin there, only tauri deps)

In usv5/src-tauri/tauri.conf.json:

{
  "build": {
    "beforeBuildCommand": "pnpm build",
    "beforeDevCommand": "pnpm dev",
    "devUrl": "http://localhost:1420",
    "frontendDist": "../dist"
  },
  "productName": "usv5",
  "version": "0.0.0",
  "plugins": {
    "shell": {
      "open": true
    }
  },
  "app": {
    "security": {
      "csp": null
    },
    "windows": [
      {
        "fullscreen": false,
        "height": 600,
        "resizable": true,
        "title": "usv5",
        "width": 800
      }
    ]
  },
  "identifier": "com.unioncontrolli.usv5",
  "bundle": {
    "active": true,
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ],
    "targets": "all"
  }
}

(nothing related to plugin there, but I had to manually edit that thing because things changed in-between alpha and beta)

In the usv5/capabilities/main.json file I added all of my plugin capabilities in the "permissions" array, as:

...
    "scada:allow-add-alias",
...

If you want to debug capabilities names, a simple way is to add in that main.json some wrong capabilities, like

    "this:does-not-exist",

and in build you'll get a debug where all existent capabilities are listed. That may help to get the exact name you have.

Note that my plugin name is "scada", not "cy3", so is not related to my library crate name.

To call your plugin then you use in your js/ts scripts:

import { invoke } from "@tauri-apps/api/core";

... whatever ...

        return await invoke('plugin:scada|add_alias',
            {
                alias: alias,
                path: path
            }
        )

Hope this helps! (and not forgetting anything...)