Skip to content

APIKeyApproval CRD#45

Merged
eguzki merged 4 commits intodeveloper-portal-rbacfrom
APIKeyApproval-crd
Apr 20, 2026
Merged

APIKeyApproval CRD#45
eguzki merged 4 commits intodeveloper-portal-rbacfrom
APIKeyApproval-crd

Conversation

@eguzki
Copy link
Copy Markdown
Contributor

@eguzki eguzki commented Apr 10, 2026

Depends on #44

Part of #39

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced APIKeyApproval resource enabling API owners to review, approve, or deny API key access requests with reviewer identity and timestamp tracking.
  • Documentation

    • Updated comprehensive documentation including RBAC-based approval workflows, expanded API owner and API consumer guidance, and additional kubectl command examples for request management and approval processes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1274f9ac-6f4b-4e99-a0c8-58e08d32dd74

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A new Kubernetes custom resource APIKeyApproval has been introduced to manage API key approval workflows. The implementation includes API type definitions, CRD schema, RBAC roles (admin/editor/viewer), sample manifests, and documentation updates spanning the developer portal API model.

Changes

Cohort / File(s) Summary
Documentation Updates
AGENTS.md, README.md
Expanded architecture documentation to cover four v1alpha1 resources including APIKeyApproval, updated development commands, generalised API modification instructions, and documented RBAC-based approval workflow with manual and automatic approval flows.
API Type Definitions
api/v1alpha1/apikeyapproval_types.go
Introduced APIKeyApproval root type, APIKeyApprovalSpec with approval decision and reviewer metadata, APIKeyApprovalStatus, and supporting APIKeyRequestReference type with kubebuilder annotations and scheme registration.
Generated Deep Copy Methods
api/v1alpha1/zz_generated.deepcopy.go
Added auto-generated DeepCopyInto, DeepCopy, and DeepCopyObject methods for APIKeyApproval, APIKeyApprovalList, APIKeyApprovalSpec, APIKeyApprovalStatus, and APIKeyRequestReference types.
CRD Definition & Kustomization
config/crd/bases/devportal.kuadrant.io_apikeyapprovals.yaml, config/crd/kustomization.yaml
Added Kubernetes CRD manifest for APIKeyApproval with namespaced scope, v1alpha1 version, spec fields for approval decision and reviewer metadata, and status subresource; registered in kustomization.
RBAC Roles
config/rbac/apikeyapproval_*_role.yaml, config/rbac/kustomization.yaml
Introduced three ClusterRole manifests (admin, editor, viewer) providing granular permissions on apikeyapprovals resource and status subresource; registered in RBAC kustomization.
Sample Manifest & Kustomization
config/samples/devportal_v1alpha1_apikeyapproval.yaml, config/samples/kustomization.yaml
Added sample APIKeyApproval resource demonstrating approval workflow with reference to APIKeyRequest, approval decision, reviewer metadata, and review timestamp; registered in samples kustomization.
Project Configuration
PROJECT
Registered new APIKeyApproval Kubernetes controller resource for devportal group at v1alpha1 version with crdVersion: v1 and namespaced: true.

Possibly related issues

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A hop and a skip through approvals so bright,
New APIKeyApproval resources take flight!
With reviewers and timestamps, decisions so clear,
The rabbit approves—this design brings cheer! 🗝️✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'APIKeyApproval CRD' directly and accurately reflects the main change: introducing a new APIKeyApproval CustomResourceDefinition across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch APIKeyApproval-crd

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@eguzki eguzki force-pushed the 40-implement-apikeyrequest-crd-and-shadow-resource-controller branch from 33e2285 to 20af371 Compare April 16, 2026 09:38
@eguzki eguzki force-pushed the APIKeyApproval-crd branch from 3be1825 to d3f8420 Compare April 16, 2026 09:39
@eguzki eguzki force-pushed the 40-implement-apikeyrequest-crd-and-shadow-resource-controller branch 3 times, most recently from 3dc2081 to a540077 Compare April 16, 2026 13:36
@eguzki eguzki force-pushed the APIKeyApproval-crd branch from d3f8420 to dcee43e Compare April 16, 2026 14:15
Base automatically changed from 40-implement-apikeyrequest-crd-and-shadow-resource-controller to developer-portal-rbac April 17, 2026 13:37
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@eguzki eguzki force-pushed the APIKeyApproval-crd branch from dcee43e to f8701e7 Compare April 17, 2026 13:38
@eguzki eguzki marked this pull request as ready for review April 17, 2026 13:40
@eguzki eguzki requested a review from didierofrivia April 17, 2026 13:40
@R-Lawton
Copy link
Copy Markdown
Contributor

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
config/samples/devportal_v1alpha1_apikeyapproval.yaml (1)

3-8: Add an explicit namespace in the sample manifest.

To reflect the intended namespace-scoped approval flow, include metadata.namespace so the example demonstrates correct placement of APIKeyApproval alongside related resources.

Based on learnings: APIKeyApproval namespace must match the APIProduct namespace and RBAC is namespace-based.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/samples/devportal_v1alpha1_apikeyapproval.yaml` around lines 3 - 8,
Add an explicit metadata.namespace entry to the sample manifest (the resource
with metadata.name: apikeyapproval-sample) so the APIKeyApproval example is
namespace-scoped; set metadata.namespace to the intended namespace (e.g.,
developer-portal) and ensure the APIKeyApproval’s namespace matches the
APIProduct namespace and the namespace used in your RBAC rules so the approval
flow and permissions work as shown.
api/v1alpha1/apikeyapproval_types.go (2)

54-58: APIKeyApprovalStatus is an empty placeholder — add conditions and observedGeneration.

With the status subresource enabled but no fields defined, the controller has no durable way to record whether the approval has been processed, propagated to the referenced APIKey, or failed. This also diverges from the conditions-based lifecycle documented in AGENTS.md line 27 and from the APIKeyRequestStatus pattern already used in this package (zz_generated.deepcopy.go lines 289-298, which carries []metav1.Condition).

♻️ Suggested status shape
 // APIKeyApprovalStatus defines the observed state of APIKeyApproval.
 type APIKeyApprovalStatus struct {
-	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
-	// Important: Run "make" to regenerate code after modifying this file
+	// ObservedGeneration reflects the generation most recently reconciled.
+	// +optional
+	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
+
+	// Conditions represent the latest observations of the approval's state
+	// (e.g. Processed, Propagated, Failed).
+	// +optional
+	// +patchMergeKey=type
+	// +patchStrategy=merge
+	// +listType=map
+	// +listMapKey=type
+	Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
 }

Remember to run make manifests generate after this change.

As per coding guidelines: "Run make manifests generate after modifying API types to regenerate CRDs and DeepCopy methods".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/v1alpha1/apikeyapproval_types.go` around lines 54 - 58,
APIKeyApprovalStatus is currently empty; add a conditions slice and
observedGeneration to persist status: add Conditions []metav1.Condition
`json:"conditions,omitempty"` (with +optional comment) and ObservedGeneration
int64 `json:"observedGeneration,omitempty"` (with +optional) to the
APIKeyApprovalStatus struct so the controller can record lifecycle state;
reference metav1.Condition for the Conditions type and ensure matching json tags
and optional comments, then run "make manifests generate" to regenerate CRDs and
deepcopy code (zz_generated.deepcopy.go).

23-25: Consider renaming for same-namespace convention.

APIKeyRequestReference holds only Name and is implicitly same-namespace. The package already uses LocalAPIProductReference (see api/v1alpha1/zz_generated.deepcopy.go line 620) to encode that semantic in the name. Renaming to LocalAPIKeyRequestReference would make the scoping rule self-documenting and consistent with the existing reference types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/v1alpha1/apikeyapproval_types.go` around lines 23 - 25, Rename the struct
type APIKeyRequestReference to LocalAPIKeyRequestReference to make
same-namespace semantics explicit: change the type declaration name (type
APIKeyRequestReference -> type LocalAPIKeyRequestReference) while keeping the
existing field Name and json tag, then update all usages/imports that reference
APIKeyRequestReference (e.g., in other types, method signatures, and tests) to
the new LocalAPIKeyRequestReference identifier and regenerate/update any
generated files (deepcopy/zz_generated) so the new name is reflected
consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/v1alpha1/apikeyapproval_types.go`:
- Around line 37-43: The ReviewedBy and ReviewedAt spec fields are
client-supplied and spoofable; move them out of spec and into status (e.g., add
ReviewedBy and ReviewedAt to APIKeyApprovalStatus and remove the Required tags
from the spec) and have the controller or an admission webhook populate them
from the authenticated admission userInfo and
metav1.ObjectMeta.CreationTimestamp (or if you must keep optional informational
fields in spec, make them optional and implement a validating webhook that
asserts spec.ReviewedBy == admission.userInfo.username and spec.ReviewedAt is
within an allowed skew of ObjectMeta.CreationTimestamp). Update references to
ReviewedBy/ReviewedAt in the controller, webhook, and CRD markers to use the new
status fields and ensure the admission/controller writes the trusted values
rather than relying on client input.

In `@config/rbac/apikeyapproval_admin_role.yaml`:
- Around line 20-21: The RBAC role currently uses a wildcard verb entry ('verbs:
- '*'') which grants excessive permissions; update the
apikeyapproval_admin_role.yaml to replace the wildcard with an explicit,
least-privilege list (e.g., get, list, watch, create, update, patch, delete —
only include the verbs actually required by the apikey approval admin helper)
under the verbs key and remove '*' so the role grants only the necessary
operations.

In `@PROJECT`:
- Line 43: PROJECT marks APIKeyApproval as controller-managed but cmd/main.go
lacks wiring; either add the reconciler setup call or change the PROJECT entry
to controller: false. To fix, open cmd/main.go and register the reconciler by
creating/using APIKeyApprovalReconciler and calling its SetupWithManager(mgr)
(consistent with how APIProductReconciler, APIKeyReconciler,
APIKeyRequestReconciler, and APIKeyRequestStatusReconciler are registered), or
edit the PROJECT file line for APIKeyApproval to set controller: false so
metadata matches the missing SetupWithManager wiring.

---

Nitpick comments:
In `@api/v1alpha1/apikeyapproval_types.go`:
- Around line 54-58: APIKeyApprovalStatus is currently empty; add a conditions
slice and observedGeneration to persist status: add Conditions
[]metav1.Condition `json:"conditions,omitempty"` (with +optional comment) and
ObservedGeneration int64 `json:"observedGeneration,omitempty"` (with +optional)
to the APIKeyApprovalStatus struct so the controller can record lifecycle state;
reference metav1.Condition for the Conditions type and ensure matching json tags
and optional comments, then run "make manifests generate" to regenerate CRDs and
deepcopy code (zz_generated.deepcopy.go).
- Around line 23-25: Rename the struct type APIKeyRequestReference to
LocalAPIKeyRequestReference to make same-namespace semantics explicit: change
the type declaration name (type APIKeyRequestReference -> type
LocalAPIKeyRequestReference) while keeping the existing field Name and json tag,
then update all usages/imports that reference APIKeyRequestReference (e.g., in
other types, method signatures, and tests) to the new
LocalAPIKeyRequestReference identifier and regenerate/update any generated files
(deepcopy/zz_generated) so the new name is reflected consistently.

In `@config/samples/devportal_v1alpha1_apikeyapproval.yaml`:
- Around line 3-8: Add an explicit metadata.namespace entry to the sample
manifest (the resource with metadata.name: apikeyapproval-sample) so the
APIKeyApproval example is namespace-scoped; set metadata.namespace to the
intended namespace (e.g., developer-portal) and ensure the APIKeyApproval’s
namespace matches the APIProduct namespace and the namespace used in your RBAC
rules so the approval flow and permissions work as shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cc30c720-3f29-4117-9a49-2472e2c7c3fd

📥 Commits

Reviewing files that changed from the base of the PR and between ae8475c and f8701e7.

📒 Files selected for processing (13)
  • AGENTS.md
  • PROJECT
  • README.md
  • api/v1alpha1/apikeyapproval_types.go
  • api/v1alpha1/zz_generated.deepcopy.go
  • config/crd/bases/devportal.kuadrant.io_apikeyapprovals.yaml
  • config/crd/kustomization.yaml
  • config/rbac/apikeyapproval_admin_role.yaml
  • config/rbac/apikeyapproval_editor_role.yaml
  • config/rbac/apikeyapproval_viewer_role.yaml
  • config/rbac/kustomization.yaml
  • config/samples/devportal_v1alpha1_apikeyapproval.yaml
  • config/samples/kustomization.yaml

Comment thread api/v1alpha1/apikeyapproval_types.go
Comment thread config/rbac/apikeyapproval_admin_role.yaml
Comment thread PROJECT
@R-Lawton
Copy link
Copy Markdown
Contributor

I think it would be helpful to add a api reference for this crd in https://github.com/Kuadrant/developer-portal-controller/tree/main/docs/references

Copy link
Copy Markdown
Contributor

@R-Lawton R-Lawton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great, two things: one comment from code rabbit about using astrix for rbac only a nit so ill leave that up to you. Also adding a api reference doc. Your linters are failing too

}

// APIKeyApprovalSpec defines the desired state of APIKeyApproval.
type APIKeyApprovalSpec struct {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't be blocking this, but I'd suggest we simplify the amount of objects around the API Key feature to the APIKey in the consumer's namespace and just one object in the owner's namespace, something like APIKeyManagement/APIKeyRequest/APIKeyControl/etc as I commented in #46 (comment)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the APIKeyRequest CRD, without it, the owner would need permission out of their own namespace to read APIKey objects. That is considered a security issue.

Regarding the APIKeyApproval CRD, that type represents a registry of decisions made by the owner in the owner's namespace. The decision is made by the owner that does not have permission over APIKey resources in the consumer namespace.

Happy to discuss all about it. The design document is the source of this implementation. If this implementation needs to be changed, let's go first to the design doc

@eguzki
Copy link
Copy Markdown
Contributor Author

eguzki commented Apr 20, 2026

looks great, two things: one comment from code rabbit about using astrix for rbac only a nit so ill leave that up to you. Also adding a api reference doc. Your linters are failing too

Regarding the linters failing. That's ok, for now (targeting feature branch). I had to comment some code out and that left some functions not being called from anywhere in the code. Hence the complain.

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented Apr 20, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
@eguzki eguzki force-pushed the APIKeyApproval-crd branch from 70415df to 0a19710 Compare April 20, 2026 09:55
@eguzki
Copy link
Copy Markdown
Contributor Author

eguzki commented Apr 20, 2026

@R-Lawton added api reference for this crd. BTW I realized the apikey api reference was outdated, so fixed it as well.

Copy link
Copy Markdown
Contributor

@R-Lawton R-Lawton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one or two things i noticed

Comment thread docs/references/apikeyapproval.md Outdated

APIKeyApproval **must** reference an existing APIKeyRequest via `apiKeyRequestRef`. The APIKeyRequest represents a developer's request for API access and contains information about the requested APIProduct, plan tier, use case, and requester details.

When an APIKeyApproval is created with `approved: true`, the controller processes the approval and updates the corresponding APIKey resource to transition it from `Pending` to `Approved` phase, triggering the creation of the API key secret.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to use the term condition rather then phase since we removed it from the crd

Comment thread docs/references/apikeyapproval.md Outdated

When an APIKeyApproval is created with `approved: true`, the controller processes the approval and updates the corresponding APIKey resource to transition it from `Pending` to `Approved` phase, triggering the creation of the API key secret.

When an APIKeyApproval is created with `approved: false`, the controller updates the corresponding APIKey resource to transition it to `Rejected` phase, and no secret is created.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think rejected should be denied?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think its used in a few places

eguzki added 2 commits April 20, 2026 18:28
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
Copy link
Copy Markdown
Contributor

@R-Lawton R-Lawton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm nice one

@eguzki eguzki merged commit e77143a into developer-portal-rbac Apr 20, 2026
16 of 18 checks passed
@eguzki eguzki deleted the APIKeyApproval-crd branch April 20, 2026 16:42
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

Successfully merging this pull request may close these issues.

3 participants