Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Federations, Federation Certificates #8

Merged
merged 8 commits into from
Jul 29, 2024
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ You can use this library to work with the following objects of the Selectel IAM
* [serviceusers](https://pkg.go.dev/github.com/selectel/iam-go/service/serviceusers)
* [groups](https://pkg.go.dev/github.com/selectel/iam-go/service/groups)
* [s3credentials](https://pkg.go.dev/github.com/selectel/iam-go/service/s3credentials)
* [federations (saml)](https://pkg.go.dev/github.com/selectel/iam-go/service/federations/saml)

### Installation

Expand Down
24 changes: 24 additions & 0 deletions examples/federation-with-user/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Create Federation with Certificate & add User

This example program demonstrates how to manage creating and deleting Federation with Certificate and assigning Users.

The part of deleting is disabled by `deleteAfterRun` variable.

## Running this example

Running this file will execute the following operations:

1. **Create Federation:** Create is used to create a new Federation.
2. **Create Certificate for Federation:** Create is used to create a new Certificate for Federation.
3. **Create federated User:** Create is used to create a new federated User.
4. **Update Federation:** Updates the Federation Name and Description.
5. **(Delete Federation):** _(disabled by default)_ Delete a just-created Federation on a previous step.

You should see an output like the following:
```
Step 1: Created Federation Name: federation_name ID: 1a2b3c...
Step 2: Created Certificate for Federation ID: 12345_3... Federation ID: 1a2b3c...
Step 3: Created federated User ID: 54321_2... Keystone ID: 1c2b3a...
Step 4: Updated Federation Name and Description
Step 5: Deleting Federation with ID: 1a2b3c...
```
3 changes: 3 additions & 0 deletions examples/federation-with-user/cert.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CERTIFICATE-----
MIICmzCCAYMCBgGI6ANFczANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjMwNjIzMTEyNjQ4WhcNMzMwNjIzMTEyODI4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC04rOaDpre/MucE3HXVCnAnpqIqQOeMn696AW2FATnI26x1BsxVAGjcrheAOIu+CxC28m48Ah4+SiTEk/u2X/WbGTd/1GZooz37cge0AWMQGyh8ysZRd6q06kg4QGD1iUtdQyHioMbSr9pPne2QQgSX5/gM9XDuA6dpG9Yv0PIPLFlk3BIUL1qEfUiYbDlrunkN/y4XromJaJPpgXKWraH194bqcgXGQLrCqicKwsRBoQJHg3ODWHjHFOwYODJ1XBsRcAue4J88PKiPV1tZNPVczMptrkqGBYTgOYGjKXGe5EH50RJE4/3Ynurz2s34DSDVJhJOYtGwpfeSuU3i3mVAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGAweCuWJmJXMUdRtgoFIiu6BGotDX5sA/VOm4CRsEXV7/qnBagrAPkRz86KGm4lOPL0X+I13JQh4/OB1gxnPN+BXhNtCWCoj1wA3/BWjs1ow/gaVXzwdy+1mbc/sUBudsLq2Yqs54GgeYsTBKMVpSLKiRg1NebEFlqFmG2hjPzYg1QHL4VBusMQgqt7TTnOfGtdT3Ss9TKGRQ+iwfNL0BtSAKaTRdhNVU4lDYUs788Kw5od/uJj0wTICKO5/PrkX7Uy42+fyU+4SvJynPOy+M+z+s08JC9+eYXixfeeFG1nNWR+DIKXcXaSwNQW+8RweGbOJxQ2BoUKtl0NCHrvxJw=
-----END CERTIFICATE-----
117 changes: 117 additions & 0 deletions examples/federation-with-user/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"context"
"fmt"
"os"

"github.com/selectel/iam-go"
"github.com/selectel/iam-go/service/federations/saml"
"github.com/selectel/iam-go/service/federations/saml/certificates"
"github.com/selectel/iam-go/service/roles"
"github.com/selectel/iam-go/service/users"
)

var (
// KeystoneToken
token = "gAAAAA..."
deleteAfterRun = false

// Prefix to be added to User-Agent.
prefix = "iam-go"

federationName = "federation_name"
federationDescription = "federation_description"
updatedFederationName = "new_federation_name"
updatedFederationDescription = "new_federation_description"

certificateName = "certificate name"
certificateDescription = "certificate description"
certificateFileName = "cert.crt"

userEmail = "[email protected]"
userExternalID = "some_id"
)

func main() {
// Create a new IAM client.
iamClient, err := iam.New(
iam.WithAuthOpts(&iam.AuthOpts{KeystoneToken: token}),
iam.WithUserAgentPrefix(prefix),
)
if err != nil {
fmt.Println(err)
return
}

federationsAPI := iamClient.SAMLFederations
federationsCertificatesAPI := federationsAPI.Certificates
usersAPI := iamClient.Users

ctx := context.Background()

federation, err := federationsAPI.Create(ctx, saml.CreateRequest{
Name: federationName,
Description: federationDescription,
Issuer: "http://localhost:8080/realms/master",
SSOUrl: "http://localhost:8080/realms/master/protocol/saml",
SessionMaxAgeHours: 24,
SignAuthnRequests: true,
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Step 1: Created Federation Name: %s ID: %s\n", federation.Name, federation.ID)

cert, err := os.ReadFile(certificateFileName)
if err != nil {
fmt.Println(err)
return
}

certificate, err := federationsCertificatesAPI.Create(ctx, federation.ID, certificates.CreateRequest{
Name: certificateName,
Description: certificateDescription,
Data: string(cert),
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Step 2: Created Certificate for Federation ID: %s Federation ID: %s\n", certificate.ID, federation.ID)

user, err := usersAPI.Create(ctx, users.CreateRequest{
AuthType: users.Federated,
Email: userEmail,
Federation: &users.Federation{
ExternalID: userExternalID,
ID: federation.ID,
},
Roles: []roles.Role{{Scope: roles.Account, RoleName: roles.Reader}},
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Step 3: Created federated User ID: %s Keystone ID: %s\n", user.ID, user.KeystoneID)

err = federationsAPI.Update(ctx, federation.ID, saml.UpdateRequest{
Name: updatedFederationName,
Description: &updatedFederationDescription,
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Step 4: Updated Federation Name and Description")

if deleteAfterRun {
// Removing User and Federation Certificate is unnecessary because removal of Federation
// also deletes its Certificate and all attached Users
fmt.Printf("Step 5: Deleting Federation with ID: %s\n", federation.ID)
if err = federationsAPI.Delete(ctx, federation.ID); err != nil {
fmt.Println(err)
}
}
}
6 changes: 6 additions & 0 deletions iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/selectel/iam-go/iamerrors"
baseclient "github.com/selectel/iam-go/internal/client"
"github.com/selectel/iam-go/service/federations/saml"
"github.com/selectel/iam-go/service/groups"
"github.com/selectel/iam-go/service/s3credentials"
"github.com/selectel/iam-go/service/serviceusers"
Expand Down Expand Up @@ -57,6 +58,10 @@ type Client struct {

// S3Credentials instance is used to make requests against Selectel IAM API and manage S3 Credentials.
S3Credentials *s3credentials.Service

// SAMLFederations instance is used to make requests against Selectel IAM API and manage SAML Federations.
// It also contains Certificates service, which is used to manage certificates.
SAMLFederations *saml.Service
}

type AuthOpts struct {
Expand Down Expand Up @@ -128,6 +133,7 @@ func New(opts ...Option) (*Client, error) {
c.ServiceUsers = serviceusers.New(c.baseClient)
c.Groups = groups.New(c.baseClient)
c.S3Credentials = s3credentials.New(c.baseClient)
c.SAMLFederations = saml.New(c.baseClient)

return c, nil
}
Expand Down
34 changes: 19 additions & 15 deletions iam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/selectel/iam-go/iamerrors"
baseclient "github.com/selectel/iam-go/internal/client"
"github.com/selectel/iam-go/service/federations/saml"
"github.com/selectel/iam-go/service/groups"
"github.com/selectel/iam-go/service/s3credentials"
"github.com/selectel/iam-go/service/serviceusers"
Expand Down Expand Up @@ -57,11 +58,12 @@ func TestNew(t *testing.T) {
authOpts: &AuthOpts{
KeystoneToken: testToken,
},
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
SAMLFederations: saml.New(baseClient),
}
},
expectedError: nil,
Expand Down Expand Up @@ -99,11 +101,12 @@ func TestNew(t *testing.T) {
authOpts: &AuthOpts{
KeystoneToken: testToken,
},
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
SAMLFederations: saml.New(baseClient),
}
},
expectedError: nil,
Expand Down Expand Up @@ -134,11 +137,12 @@ func TestNew(t *testing.T) {
authOpts: &AuthOpts{
KeystoneToken: testToken,
},
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
baseClient: baseClient,
Users: users.New(baseClient),
ServiceUsers: serviceusers.New(baseClient),
Groups: groups.New(baseClient),
S3Credentials: s3credentials.New(baseClient),
SAMLFederations: saml.New(baseClient),
}
},
expectedError: nil,
Expand Down
75 changes: 45 additions & 30 deletions iamerrors/iamerrors.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ var (
ErrGroupNotFound = errors.New("GROUP_NOT_FOUND")
ErrUserOrGroupNotFound = errors.New("USER_OR_GROUP_NOT_FOUND")

ErrFederationNameRequired = errors.New("FEDERATION_NAME_REQUIRED")
ErrFederationIDRequired = errors.New("FEDERATION_ID_REQUIRED")
ErrFederationIssuerRequired = errors.New("FEDERATION_ISSUER_REQUIRED")
ErrFederationSSOURLRequired = errors.New("FEDERATION_SSO_URL_REQUIRED")
ErrFederationCertificateIDRequired = errors.New("FEDERATION_CERTIFICATE_ID_REQUIRED")
ErrFederationMaxAgeHoursRequired = errors.New("FEDERATION_MAX_AGE_HOURS_REQUIRED")
ErrFederationNotFound = errors.New("FEDERATION_NOT_FOUND")

ErrCredentialNameRequired = errors.New("CREDENTIAL_NAME_REQUIRED")
ErrCredentialAccessKeyRequired = errors.New("CREDENTIAL_ACCESS_KEY_REQUIRED")

Expand All @@ -48,36 +56,43 @@ var (

//nolint:gochecknoglobals // stringToError is not global.
stringToError = map[string]error{
ErrUserNotFound.Error(): ErrUserNotFound,
ErrClientNoAuthOpts.Error(): ErrClientNoAuthOpts,
ErrAuthTokenUnathorized.Error(): ErrAuthTokenUnathorized,
ErrDomainNotFound.Error(): ErrDomainNotFound,
ErrCredentialNotFound.Error(): ErrCredentialNotFound,
ErrProjectNotFound.Error(): ErrProjectNotFound,
ErrUserAlreadyExists.Error(): ErrUserAlreadyExists,
ErrRequestValidationError.Error(): ErrRequestValidationError,
ErrForbidden.Error(): ErrForbidden,
ErrUnauthorized.Error(): ErrUnauthorized,
ErrInternalServerError.Error(): ErrInternalServerError,
ErrCredentialNameRequired.Error(): ErrCredentialNameRequired,
ErrCredentialAccessKeyRequired.Error(): ErrCredentialAccessKeyRequired,
ErrUserIDRequired.Error(): ErrUserIDRequired,
ErrProjectIDRequired.Error(): ErrProjectIDRequired,
ErrGroupIDRequired.Error(): ErrGroupIDRequired,
ErrGroupUserIDsRequired.Error(): ErrGroupUserIDsRequired,
ErrGroupNameRequired.Error(): ErrGroupNameRequired,
ErrGroupRolesRequired.Error(): ErrGroupRolesRequired,
ErrGroupAlreadyExists.Error(): ErrGroupAlreadyExists,
ErrGroupNotFound.Error(): ErrGroupNotFound,
ErrUserOrGroupNotFound.Error(): ErrUserOrGroupNotFound,
ErrServiceUserNameRequired.Error(): ErrServiceUserNameRequired,
ErrServiceUserPasswordRequired.Error(): ErrServiceUserPasswordRequired,
ErrServiceUserRolesRequired.Error(): ErrServiceUserRolesRequired,
ErrUserRolesRequired.Error(): ErrUserRolesRequired,
ErrUserEmailRequired.Error(): ErrUserEmailRequired,
ErrInputDataRequired.Error(): ErrInputDataRequired,
ErrInternalAppError.Error(): ErrInternalAppError,
ErrUnknown.Error(): ErrUnknown,
ErrUserNotFound.Error(): ErrUserNotFound,
ErrClientNoAuthOpts.Error(): ErrClientNoAuthOpts,
ErrAuthTokenUnathorized.Error(): ErrAuthTokenUnathorized,
ErrDomainNotFound.Error(): ErrDomainNotFound,
ErrCredentialNotFound.Error(): ErrCredentialNotFound,
ErrProjectNotFound.Error(): ErrProjectNotFound,
ErrUserAlreadyExists.Error(): ErrUserAlreadyExists,
ErrRequestValidationError.Error(): ErrRequestValidationError,
ErrForbidden.Error(): ErrForbidden,
ErrUnauthorized.Error(): ErrUnauthorized,
ErrInternalServerError.Error(): ErrInternalServerError,
ErrCredentialNameRequired.Error(): ErrCredentialNameRequired,
ErrCredentialAccessKeyRequired.Error(): ErrCredentialAccessKeyRequired,
ErrUserIDRequired.Error(): ErrUserIDRequired,
ErrProjectIDRequired.Error(): ErrProjectIDRequired,
ErrGroupIDRequired.Error(): ErrGroupIDRequired,
ErrGroupUserIDsRequired.Error(): ErrGroupUserIDsRequired,
ErrGroupNameRequired.Error(): ErrGroupNameRequired,
ErrGroupRolesRequired.Error(): ErrGroupRolesRequired,
ErrGroupAlreadyExists.Error(): ErrGroupAlreadyExists,
ErrGroupNotFound.Error(): ErrGroupNotFound,
ErrFederationNameRequired.Error(): ErrFederationNameRequired,
ErrFederationIDRequired.Error(): ErrFederationIDRequired,
ErrFederationIssuerRequired.Error(): ErrFederationIssuerRequired,
ErrFederationSSOURLRequired.Error(): ErrFederationSSOURLRequired,
ErrFederationCertificateIDRequired.Error(): ErrFederationCertificateIDRequired,
ErrFederationNotFound.Error(): ErrFederationNotFound,
ErrFederationMaxAgeHoursRequired.Error(): ErrFederationMaxAgeHoursRequired,
ErrUserOrGroupNotFound.Error(): ErrUserOrGroupNotFound,
ErrServiceUserNameRequired.Error(): ErrServiceUserNameRequired,
ErrServiceUserPasswordRequired.Error(): ErrServiceUserPasswordRequired,
ErrServiceUserRolesRequired.Error(): ErrServiceUserRolesRequired,
ErrUserRolesRequired.Error(): ErrUserRolesRequired,
ErrUserEmailRequired.Error(): ErrUserEmailRequired,
ErrInputDataRequired.Error(): ErrInputDataRequired,
ErrInternalAppError.Error(): ErrInternalAppError,
ErrUnknown.Error(): ErrUnknown,
}
)

Expand Down
2 changes: 2 additions & 0 deletions service/federations/saml/certificates/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package certificates provides a set of functions for interacting with the Selectel Federations Certificates API.
package certificates
Loading
Loading