From 9cb09a88a64941122ee0cbc8328a767186de9704 Mon Sep 17 00:00:00 2001
From: Kian Parvin <kian.parvin@canonical.com>
Date: Thu, 8 Aug 2024 15:49:20 +0200
Subject: [PATCH] feat: add generic access resource

- Added a generic resource access type that will form the basis for jaas access resources.
- Added validators for fields including username, model id and group id validators.
- Added the jaas_access_jaas_model.go file which contains info on how to perform access management to models using the new generic type.
---
 go.mod                                        |  19 +-
 go.sum                                        |  62 +++----
 internal/provider/resource_access_generic.go  | 167 ++++++++++++++++++
 .../provider/resource_access_jaas_model.go    |  69 ++++++++
 internal/provider/validator_jaas_group_id.go  |  44 +++++
 internal/provider/validator_model_id.go       |  45 +++++
 internal/provider/validator_username.go       |  45 +++++
 7 files changed, 412 insertions(+), 39 deletions(-)
 create mode 100644 internal/provider/resource_access_generic.go
 create mode 100644 internal/provider/resource_access_jaas_model.go
 create mode 100644 internal/provider/validator_jaas_group_id.go
 create mode 100644 internal/provider/validator_model_id.go
 create mode 100644 internal/provider/validator_username.go

diff --git a/go.mod b/go.mod
index 29572f5b..46d7745c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,25 +1,26 @@
 module github.com/juju/terraform-provider-juju
 
-go 1.21
+go 1.22.3
 
 require (
 	github.com/bflad/tfproviderlint v0.30.0
 	github.com/hashicorp/terraform-plugin-docs v0.19.4
 	// v3.5.1
-	github.com/juju/juju v0.0.0-20240524040137-95c267441801
+	github.com/juju/juju v0.0.0-20240724081236-63d460f9ee6c
 
 )
 
 require (
+	github.com/canonical/jimm-go-sdk/v3 v3.0.2
 	github.com/dustin/go-humanize v1.0.1
 	github.com/hashicorp/terraform-plugin-framework v1.9.0
 	github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
 	github.com/hashicorp/terraform-plugin-go v0.23.0
 	github.com/hashicorp/terraform-plugin-log v0.9.0
 	github.com/hashicorp/terraform-plugin-testing v1.9.0
-	github.com/juju/charm/v12 v12.0.2
+	github.com/juju/charm/v12 v12.1.0
 	github.com/juju/clock v1.1.1
-	github.com/juju/cmd/v3 v3.0.14
+	github.com/juju/cmd/v3 v3.0.15
 	github.com/juju/collections v1.0.4
 	github.com/juju/errors v1.0.0
 	github.com/juju/names/v4 v4.0.0-20220207005702-9c6532a52823
@@ -129,7 +130,7 @@ require (
 	github.com/juju/mgo/v3 v3.0.4 // indirect
 	github.com/juju/mutex/v2 v2.0.0 // indirect
 	github.com/juju/os/v2 v2.2.5 // indirect
-	github.com/juju/packaging/v3 v3.0.0 // indirect
+	github.com/juju/packaging/v3 v3.1.0 // indirect
 	github.com/juju/persistent-cookiejar v1.0.0 // indirect
 	github.com/juju/proxy v1.0.0 // indirect
 	github.com/juju/pubsub/v2 v2.0.0 // indirect
@@ -198,15 +199,15 @@ require (
 	go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
 	golang.org/x/crypto v0.25.0 // indirect
 	golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
-	golang.org/x/mod v0.17.0 // indirect
-	golang.org/x/net v0.25.0 // indirect
-	golang.org/x/oauth2 v0.17.0 // indirect
+	golang.org/x/mod v0.18.0 // indirect
+	golang.org/x/net v0.27.0 // indirect
+	golang.org/x/oauth2 v0.21.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
 	golang.org/x/sys v0.22.0 // indirect
 	golang.org/x/term v0.22.0 // indirect
 	golang.org/x/text v0.16.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
+	golang.org/x/tools v0.22.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
 	google.golang.org/grpc v1.63.2 // indirect
diff --git a/go.sum b/go.sum
index 77b3ab2b..dd19d337 100644
--- a/go.sum
+++ b/go.sum
@@ -1,12 +1,12 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
 dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2 h1:qiir/pptnHqp6hV8QwV+IExYIf6cPsXBfUDUXQ27t2Y=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2/go.mod h1:jVRrRDLCOuif95HDYC23ADTMlvahB7tMdl519m9Iyjc=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2 v2.0.0 h1:xxe4naFUPYEW1W6C8yWrfFNmyZLnEbO+CsbsSF83wDo=
@@ -19,12 +19,12 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo=
-github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
-github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 h1:DRiANoJTiW6obBQe3SqZizkuV1PEgfiiGivmVocDy64=
+github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0/go.mod h1:qLIye2hwb/ZouqhpSD9Zn3SJipvpEnz1Ywl3VUk9Y0s=
 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
 github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@@ -97,6 +97,8 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ
 github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
 github.com/canonical/go-dqlite v1.21.0 h1:4gLDdV2GF+vg0yv9Ff+mfZZNQ1JGhnQ3GnS2GeZPHfA=
 github.com/canonical/go-dqlite v1.21.0/go.mod h1:Uvy943N8R4CFUAs59A1NVaziWY9nJ686lScY7ywurfg=
+github.com/canonical/jimm-go-sdk/v3 v3.0.2 h1:gorlIyP/5BhMswKdvwpenE+lEYuV8SBr131Ll0ON4dg=
+github.com/canonical/jimm-go-sdk/v3 v3.0.2/go.mod h1:ZgqrXiD6eTuc8tVaH7pwVkKWgnQVwnXfSWOIp49vfDA=
 github.com/canonical/lxd v0.0.0-20231214113525-e676fc63c50a h1:Tfo/MzXK5GeG7gzSHqxGeY/669Mhh5ea43dn1mRDnk8=
 github.com/canonical/lxd v0.0.0-20231214113525-e676fc63c50a/go.mod h1:UxfHGKFoRjgu1NUA9EFiR++dKvyAiT0h9HT0ffMlzjc=
 github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
@@ -181,8 +183,8 @@ github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
-github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -324,12 +326,12 @@ github.com/juju/ansiterm v1.0.0 h1:gmMvnZRq7JZJx6jkfSq9/+2LMrVEwGwt7UR6G+lmDEg=
 github.com/juju/ansiterm v1.0.0/go.mod h1:PyXUpnI3olx3bsPcHt98FGPX/KCFZ1Fi+hw1XLI6384=
 github.com/juju/blobstore/v3 v3.0.2 h1:roZ4YBuZYmWId6y/6ZLQSAMmNlHOclHD8PQAMOQer6E=
 github.com/juju/blobstore/v3 v3.0.2/go.mod h1:NXEgMhrVH5744/zLfSkzsySlDQUpCgzvcNxjJJhICko=
-github.com/juju/charm/v12 v12.0.2 h1:0UqIAAb4csYF0bGasGcmmIhq/bJUWfGU5+o3XFziWlY=
-github.com/juju/charm/v12 v12.0.2/go.mod h1:QRuKxXC5zzfPlAa8ipmxX1tvpbIBliueLVHGbs3T7wU=
+github.com/juju/charm/v12 v12.1.0 h1:V0M440FBP4ASRgA98jxEWqlcb38ahP0rus4u05ftPJ8=
+github.com/juju/charm/v12 v12.1.0/go.mod h1:QRuKxXC5zzfPlAa8ipmxX1tvpbIBliueLVHGbs3T7wU=
 github.com/juju/clock v1.1.1 h1:NvgHG9DQmOpBevgt6gzkyimdWBooLXDy1cQn89qJzBI=
 github.com/juju/clock v1.1.1/go.mod h1:HIBvJ8kiV/n7UHwKuCkdYL4l/MDECztHR2sAvWDxxf0=
-github.com/juju/cmd/v3 v3.0.14 h1:KuuamArSH7vQ6SdQKEHYK2scEMkJTEZKLs8abrlW3XE=
-github.com/juju/cmd/v3 v3.0.14/go.mod h1:lGtDvm2BG+FKnIS8yY/vrhxQNX9imnL6bPIYGSTchuI=
+github.com/juju/cmd/v3 v3.0.15 h1:/n/n/L4CX5hL9Bwkt9QV31Qsa3Vo8GB95gtpQPEIEb0=
+github.com/juju/cmd/v3 v3.0.15/go.mod h1:lGtDvm2BG+FKnIS8yY/vrhxQNX9imnL6bPIYGSTchuI=
 github.com/juju/collections v1.0.4 h1:GjL+aN512m2rVDqhPII7P6qB0e+iYFubz8sqBhZaZtk=
 github.com/juju/collections v1.0.4/go.mod h1:hVrdB0Zwq9wIU1Fl6ItD2+UETeNeOEs+nGvJufVe+0c=
 github.com/juju/description/v5 v5.0.4 h1:qA35hRglZ47j1mmo9zUM9R+2WSDCH5dvL5ik7gA2aVE=
@@ -354,8 +356,8 @@ github.com/juju/idmclient/v2 v2.0.0 h1:PsGa092JGy6iFNHZCcao+bigVsTyz1C+tHNRdYmKv
 github.com/juju/idmclient/v2 v2.0.0/go.mod h1:EOiFbPmnkqKvCUS/DHpDRWhL7eKF0AJaTvMjIYlIUak=
 github.com/juju/jsonschema v1.0.0 h1:2ScR9hhVdHxft+Te3fnclVx61MmlikHNEOirTGi+hV4=
 github.com/juju/jsonschema v1.0.0/go.mod h1:SlFW+jFtpWX0P4Tb+zTTPR4ufttLrnJIdQPePxVEfkM=
-github.com/juju/juju v0.0.0-20240524040137-95c267441801 h1:5aNKt2VkNKsS2JI2QcCjNEvOVfMkNNSYaHX6z5odB30=
-github.com/juju/juju v0.0.0-20240524040137-95c267441801/go.mod h1:dxhhMNnbWSXUB7EdQEsaxLhgwKQXo2Ctl4z4AZwWL88=
+github.com/juju/juju v0.0.0-20240724081236-63d460f9ee6c h1:QDl1j+JwkfagsftAffgSa+YBzx8KwjwoeHDIFRKoYEQ=
+github.com/juju/juju v0.0.0-20240724081236-63d460f9ee6c/go.mod h1:BB8d/2+xfqC3WYkQwNuuMZ5H/ZrgA5MmOlMOY/oXc+k=
 github.com/juju/loggo v1.0.0 h1:Y6ZMQOGR9Aj3BGkiWx7HBbIx6zNwNkxhVNOHU2i1bl0=
 github.com/juju/loggo v1.0.0/go.mod h1:NIXFioti1SmKAlKNuUwbMenNdef59IF52+ZzuOmHYkg=
 github.com/juju/lru v1.0.0 h1:FP8mBNF3jBnKwGO5PtsR+8iIegx8DREfhRhpcGpYcn4=
@@ -374,8 +376,8 @@ github.com/juju/naturalsort v1.0.0 h1:kGmUUy3h8mJ5/SJYaqKOBR3f3owEd5R52Lh+Tjg/dN
 github.com/juju/naturalsort v1.0.0/go.mod h1:Zqa/vGkXr78k47zM6tFmU9phhxKz/PIdqBzpLhJ86zc=
 github.com/juju/os/v2 v2.2.5 h1:Ayw9aC7axKtGgzy3dFRKx84FxasfISMege0iYDsH6io=
 github.com/juju/os/v2 v2.2.5/go.mod h1:igGQLjgRSwUery5ZhV/1pZjZkMwnfkAwWCwh5ZfIg+c=
-github.com/juju/packaging/v3 v3.0.0 h1:ZzuHhR8Ql9z2oeQ0m73x6k58PW65Cgk5wR9Yc1exoOI=
-github.com/juju/packaging/v3 v3.0.0/go.mod h1:WXh/SXqh1du8SFzwb1KC+yZuV4Qc4alWP3MEPqFX9Lw=
+github.com/juju/packaging/v3 v3.1.0 h1:P5RyXSKVLNWqbpr8XcdDI17wYdTGYg7KTHce8DMeoSk=
+github.com/juju/packaging/v3 v3.1.0/go.mod h1:WXh/SXqh1du8SFzwb1KC+yZuV4Qc4alWP3MEPqFX9Lw=
 github.com/juju/persistent-cookiejar v1.0.0 h1:Ag7+QLzqC2m+OYXy2QQnRjb3gTkEBSZagZ6QozwT3EQ=
 github.com/juju/persistent-cookiejar v1.0.0/go.mod h1:zrbmo4nBKaiP/Ez3F67ewkMbzGYfXyMvRtbOfuAwG0w=
 github.com/juju/proxy v1.0.0 h1:5XMp0opQJx8K/js3RFG/2EAk+cvKba/zwFJwd5f0AW0=
@@ -546,8 +548,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
 github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -683,8 +685,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -698,11 +700,11 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
-golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -760,8 +762,8 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/internal/provider/resource_access_generic.go b/internal/provider/resource_access_generic.go
new file mode 100644
index 00000000..2bc37f71
--- /dev/null
+++ b/internal/provider/resource_access_generic.go
@@ -0,0 +1,167 @@
+// Copyright 2023 Canonical Ltd.
+// Licensed under the Apache License, Version 2.0, see LICENCE file for details.
+
+package provider
+
+import (
+	"context"
+	"fmt"
+	"regexp"
+
+	"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
+	"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
+	"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+	"github.com/hashicorp/terraform-plugin-framework/diag"
+	"github.com/hashicorp/terraform-plugin-framework/path"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/hashicorp/terraform-plugin-log/tflog"
+
+	"github.com/juju/terraform-provider-juju/internal/juju"
+)
+
+var (
+	withAtSymbolRe = regexp.MustCompile(".+@.+")
+	noAtSymbolRe   = regexp.MustCompile("^[^@]*$")
+)
+
+// Getter is used to get details from a plan or state object.
+// Implemented by Terraform's [State] and [Plan] types.
+type Getter interface {
+	Get(ctx context.Context, target interface{}) diag.Diagnostics
+}
+
+// resourceInfo defines how the [genericJAASAccessResource] can query for information
+// on the target object.
+type resourceInfo interface {
+	Identity(ctx context.Context, plan Getter, diag *diag.Diagnostics) string
+}
+
+// genericJAASAccessResource is a generic resource that can be used for creating access rules with JAAS.
+// Other types should embed this struct and implement their own metadata and schema methods. The schema
+// should build on top of [PartialAccessSchema].
+// The embedded struct requires a targetInfo interface to enable fetching the target object in the relation.
+type genericJAASAccessResource struct {
+	client     *juju.Client
+	targetInfo resourceInfo
+
+	// subCtx is the context created with the new tflog subsystem for applications.
+	subCtx context.Context
+}
+
+// genericJAASAccessModel represents a partial generic object for access management.
+// This struct should be embedded into a struct that contains a field for a target object (normally a name or UUID).
+// Note that service accounts are treated as users but kept as a separate field for improved validation.
+type genericJAASAccessModel struct {
+	Users           types.Set    `tfsdk:"users"`
+	ServiceAccounts types.Set    `tfsdk:"service_accounts"`
+	Groups          types.Set    `tfsdk:"groups"`
+	Access          types.String `tfsdk:"access"`
+
+	// ID required by the testing framework
+	ID types.String `tfsdk:"id"`
+}
+
+func (r *genericJAASAccessResource) ConfigValidators(ctx context.Context) []resource.ConfigValidator {
+	return []resource.ConfigValidator{
+		// TODO(Kian) Add requires JAAS validator once that lands.
+		// RequiresJAASValidator{Client: r.client},
+		resourcevalidator.AtLeastOneOf(
+			path.MatchRoot("users"),
+			path.MatchRoot("groups"),
+			path.MatchRoot("service_accounts"),
+		),
+	}
+}
+
+// PartialAccessSchema returns a map of schema attributes for a JAAS access resource.
+// Access resources should use this schema and add any additional attributes e.g. name or uuid.
+func PartialAccessSchema() map[string]schema.Attribute {
+	return map[string]schema.Attribute{
+		"access": schema.StringAttribute{
+			Description: "Type of access to the model",
+			Required:    true,
+			PlanModifiers: []planmodifier.String{
+				stringplanmodifier.RequiresReplace(),
+			},
+		},
+		"users": schema.SetAttribute{
+			Description: "List of users to grant access",
+			Optional:    true,
+			ElementType: types.StringType,
+			Validators: []validator.Set{
+				setvalidator.ValueStringsAre(UsernameStringIsValid()),
+				setvalidator.ValueStringsAre(stringvalidator.RegexMatches(withAtSymbolRe, "email must contain an @ symbol")),
+			},
+		},
+		"groups": schema.SetAttribute{
+			Description: "List of groups to grant access",
+			Optional:    true,
+			ElementType: types.StringType,
+			Validators: []validator.Set{
+				setvalidator.ValueStringsAre(GroupStringIsValid()),
+			},
+		},
+		"service_accounts": schema.SetAttribute{
+			Description: "List of service account to grant access",
+			Optional:    true,
+			ElementType: types.StringType,
+			// service accounts are treated as users but defined separately
+			// for different validation and logic in the provider.
+			Validators: []validator.Set{
+				setvalidator.ValueStringsAre(UsernameStringIsValid()),
+				setvalidator.ValueStringsAre(stringvalidator.RegexMatches(noAtSymbolRe, "service accounts should not contain an @ symbol")),
+			},
+		},
+		// ID required by the testing framework
+		"id": schema.StringAttribute{
+			Computed: true,
+		},
+	}
+}
+
+// Configure enables provider-level data or clients to be set in the
+// provider-defined DataSource type. It is separately executed for each
+// ReadDataSource RPC.
+func (a *genericJAASAccessResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+	// Prevent panic if the provider has not been configured.
+	if req.ProviderData == nil {
+		return
+	}
+
+	client, ok := req.ProviderData.(*juju.Client)
+	if !ok {
+		resp.Diagnostics.AddError(
+			"Unexpected Resource Configure Type",
+			fmt.Sprintf("Expected *juju.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+		)
+		return
+	}
+	a.client = client
+	// Create the local logging subsystem here, using the TF context when creating it.
+	a.subCtx = tflog.NewSubsystem(ctx, LogResourceAccessModel)
+}
+
+// Create defines how tuples for access control will be created.
+func (a *genericJAASAccessResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+
+}
+
+// Read defines how tuples for access control will be read.
+func (a *genericJAASAccessResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+
+}
+
+// Update defines how tuples for access control will be updated.
+func (a *genericJAASAccessResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+
+}
+
+// Delete defines how tuples for access control will be updated.
+func (a *genericJAASAccessResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+
+}
diff --git a/internal/provider/resource_access_jaas_model.go b/internal/provider/resource_access_jaas_model.go
new file mode 100644
index 00000000..7f712185
--- /dev/null
+++ b/internal/provider/resource_access_jaas_model.go
@@ -0,0 +1,69 @@
+// Copyright 2023 Canonical Ltd.
+// Licensed under the Apache License, Version 2.0, see LICENCE file for details.
+
+package provider
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework/diag"
+	"github.com/hashicorp/terraform-plugin-framework/resource"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/hashicorp/terraform-plugin-framework/types"
+	"github.com/juju/names/v5"
+)
+
+// Ensure provider defined types fully satisfy framework interfaces.
+var _ resource.Resource = &jaasAccessModelResource{}
+var _ resource.ResourceWithConfigure = &jaasAccessModelResource{}
+
+// NewJAASAccessModelResource returns a new resource for JAAS model access.
+func NewJAASAccessModelResource() resource.Resource {
+	return &jaasAccessModelResource{genericJAASAccessResource: genericJAASAccessResource{targetInfo: modelInfo{}}}
+}
+
+type modelInfo struct{}
+
+// Identity implements the [resourceInfo] interface, used to extract the model UUID from the Terraform plan/state.
+func (j modelInfo) Identity(ctx context.Context, plan Getter, diag *diag.Diagnostics) string {
+	p := jaasAccessModelResourceModel{}
+	diag.Append(plan.Get(ctx, &p)...)
+	return names.NewModelTag(p.ModelUUID.String()).String()
+}
+
+type jaasAccessModelResource struct {
+	genericJAASAccessResource
+}
+
+type jaasAccessModelResourceModel struct {
+	ModelUUID types.String `tfsdk:"model_uuid"`
+	genericJAASAccessModel
+}
+
+// Metadata returns metadata about the JAAS model access resource.
+func (a *jaasAccessModelResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+	resp.TypeName = req.ProviderTypeName + "_jaas_access_model"
+}
+
+// Schema defines the schema for the JAAS model access resource.
+func (a *jaasAccessModelResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+	attributes := PartialAccessSchema()
+	attributes["model_uuid"] = schema.StringAttribute{
+		Description: "The uuid of the model for access management",
+		Required:    true,
+		Validators: []validator.String{
+			ModelIDIsValid(),
+		},
+		PlanModifiers: []planmodifier.String{
+			stringplanmodifier.RequiresReplace(),
+		},
+	}
+	schema := schema.Schema{
+		Description: "A resource that represent access to a model when using JAAS.",
+		Attributes:  attributes,
+	}
+	resp.Schema = schema
+}
diff --git a/internal/provider/validator_jaas_group_id.go b/internal/provider/validator_jaas_group_id.go
new file mode 100644
index 00000000..18ff987a
--- /dev/null
+++ b/internal/provider/validator_jaas_group_id.go
@@ -0,0 +1,44 @@
+package provider
+
+import (
+	"context"
+
+	"github.com/canonical/jimm-go-sdk/v3/names"
+	"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+)
+
+var _ validator.String = groupIDValidator{}
+
+type groupIDValidator struct{}
+
+// GroupSetIsValid returns an AttributeValidator which ensures that a set of group IDs are valid
+func GroupStringIsValid() validator.String {
+	return groupIDValidator{}
+}
+
+func (v groupIDValidator) Description(ctx context.Context) string {
+	return v.MarkdownDescription(ctx)
+}
+
+// MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact.
+func (v groupIDValidator) MarkdownDescription(context.Context) string {
+	return "Ensure value is a valid group ID"
+}
+
+// Validate performs the validation.
+func (v groupIDValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) {
+	if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
+		return
+	}
+
+	value := request.ConfigValue.ValueString()
+
+	if !names.IsValidGroupId(value) {
+		response.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic(
+			request.Path,
+			v.Description(ctx),
+			value,
+		))
+	}
+}
diff --git a/internal/provider/validator_model_id.go b/internal/provider/validator_model_id.go
new file mode 100644
index 00000000..60712db9
--- /dev/null
+++ b/internal/provider/validator_model_id.go
@@ -0,0 +1,45 @@
+package provider
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/juju/names/v5"
+)
+
+var _ validator.String = modelIDValidator{}
+
+type modelIDValidator struct{}
+
+// ModelIDIsValid returns an AttributeValidator which ensures that any configured
+// model ID is valid.
+func ModelIDIsValid() validator.String {
+	return modelIDValidator{}
+}
+
+func (v modelIDValidator) Description(ctx context.Context) string {
+	return v.MarkdownDescription(ctx)
+}
+
+// MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact.
+func (v modelIDValidator) MarkdownDescription(context.Context) string {
+	return "Ensure value is a valid model ID"
+}
+
+// Validate performs the validation.
+func (v modelIDValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) {
+	if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
+		return
+	}
+
+	value := request.ConfigValue.ValueString()
+
+	if !names.IsValidModel(value) {
+		response.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic(
+			request.Path,
+			v.Description(ctx),
+			value,
+		))
+	}
+}
diff --git a/internal/provider/validator_username.go b/internal/provider/validator_username.go
new file mode 100644
index 00000000..2d432b38
--- /dev/null
+++ b/internal/provider/validator_username.go
@@ -0,0 +1,45 @@
+package provider
+
+import (
+	"context"
+
+	"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
+	"github.com/hashicorp/terraform-plugin-framework/schema/validator"
+	"github.com/juju/names/v5"
+)
+
+var _ validator.String = usernameValidator{}
+
+type usernameValidator struct{}
+
+// UsernameStringIsValid returns an AttributeValidator which ensures that any configured
+// username string is valid.
+func UsernameStringIsValid() validator.String {
+	return usernameValidator{}
+}
+
+func (v usernameValidator) Description(ctx context.Context) string {
+	return v.MarkdownDescription(ctx)
+}
+
+// MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact.
+func (v usernameValidator) MarkdownDescription(context.Context) string {
+	return "Ensure value is a valid user name"
+}
+
+// ValidateString performs the validation for string values.
+func (v usernameValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) {
+	if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
+		return
+	}
+
+	value := request.ConfigValue.ValueString()
+
+	if !names.IsValidUser(value) {
+		response.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic(
+			request.Path,
+			v.Description(ctx),
+			value,
+		))
+	}
+}