Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: ci

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
buf:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: bufbuild/buf-action@v1
with:
token: ${{ secrets.BUF_TOKEN }}
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}

go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- uses: bufbuild/buf-action@v1
with:
setup_only: true
- name: Check generated code
run: |
buf generate
git diff --exit-code
- run: go test ./...
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,57 @@
# exframe-proto
# exframe-proto

Protobuf extensions used by the exFrame Go framework.

## exframe.auth.v1

`exframe/auth/v1/auth.proto` declares the method option `(exframe.auth.v1.method)`, the access policy of an RPC method:

- `scopes`: access is granted when the caller has any of the scopes;
- `public`: requests that fail authorization are let through and counted.

A method without the option is closed for every caller.

```proto
import "exframe/auth/v1/auth.proto";

service ContactService {
rpc GetUserPhoneV1(GetUserPhoneV1Request) returns (GetUserPhoneV1Response) {
option (exframe.auth.v1.method) = {
scopes: ["contact-service.phone.read", "contact-service.phone.read-write"]
};
}
}
```

| `public` | `scopes` | Caller without a required scope | Caller with a required scope |
|---|---|---|---|
| false | set | denied | allowed |
| false | empty | denied | denied |
| true | set | allowed and counted | allowed |
| true | empty | allowed | allowed |

## Usage

buf: add the module to `deps` in `buf.yaml` and run `buf dep update`.

```yaml
deps:
- buf.build/exness/exframe-proto
```

Go: the generated code imports `github.com/exness/exframe-proto/exframe/auth/v1`, no `go_package` override is needed.

```sh
go get github.com/exness/exframe-proto
```

## Development

```sh
buf lint
buf format -d --exit-code
buf generate
go test ./...
```

The extension number `52008` and the field numbers of `MethodAuth` never change.
5 changes: 5 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: v2
plugins:
- local: ["go", "tool", "protoc-gen-go"]
out: .
opt: paths=source_relative
10 changes: 10 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: v2
modules:
- path: .
name: buf.build/exness/exframe-proto
lint:
use:
- STANDARD
breaking:
use:
- FILE
160 changes: 160 additions & 0 deletions exframe/auth/v1/auth.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions exframe/auth/v1/auth.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
syntax = "proto3";

package exframe.auth.v1;

import "google/protobuf/descriptor.proto";

option go_package = "github.com/exness/exframe-proto/exframe/auth/v1;authv1";

// MethodAuth is the access policy of an RPC method.
message MethodAuth {
// Access is granted when the caller has any of the scopes.
repeated string scopes = 1;
// Requests that fail authorization are let through and counted.
bool public = 2;
}

extend google.protobuf.MethodOptions {
// A method without the option is closed for every caller.
MethodAuth method = 52008;
}
57 changes: 57 additions & 0 deletions exframe/auth/v1/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package authv1

import (
"testing"

"google.golang.org/protobuf/reflect/protoreflect"
)

func TestExtension(t *testing.T) {
t.Parallel()

ext := E_Method.TypeDescriptor()
if got := ext.Number(); got != 52008 {
t.Fatalf("extension number = %d, want 52008", got)
}
if got := ext.FullName(); got != "exframe.auth.v1.method" {
t.Fatalf("extension name = %q, want exframe.auth.v1.method", got)
}
if got := ext.ContainingMessage().FullName(); got != "google.protobuf.MethodOptions" {
t.Fatalf("extended message = %q, want google.protobuf.MethodOptions", got)
}
if got := ext.Message().FullName(); got != "exframe.auth.v1.MethodAuth" {
t.Fatalf("extension type = %q, want exframe.auth.v1.MethodAuth", got)
}
}

func TestMethodAuthFields(t *testing.T) {
t.Parallel()

fields := (&MethodAuth{}).ProtoReflect().Descriptor().Fields()
tests := []struct {
name string
number protoreflect.FieldNumber
kind protoreflect.Kind
list bool
}{
{name: "scopes", number: 1, kind: protoreflect.StringKind, list: true},
{name: "public", number: 2, kind: protoreflect.BoolKind},
}
if fields.Len() != len(tests) {
t.Fatalf("fields = %d, want %d", fields.Len(), len(tests))
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

f := fields.ByName(protoreflect.Name(tt.name))
if f == nil {
t.Fatalf("field %s not found", tt.name)
}
if f.Number() != tt.number || f.Kind() != tt.kind || f.IsList() != tt.list {
t.Fatalf("field %s = (%d, %s, list %t), want (%d, %s, list %t)",
tt.name, f.Number(), f.Kind(), f.IsList(), tt.number, tt.kind, tt.list)
}
})
}
}
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/exness/exframe-proto

go 1.24

require google.golang.org/protobuf v1.36.12

tool google.golang.org/protobuf/cmd/protoc-gen-go
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
Loading