access

package module
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2025 License: MIT Imports: 22 Imported by: 3

README

access

Overview

The access repository is designed to handle authorization by implementing role-based access control (RBAC). It manages the storage and organization of user permissions and roles, enabling precise control over access rights within various domains or tenants.

Features

  • RBAC Implementation: Manage user roles and permissions efficiently.
  • Domain/Tenant Specific: Assign permissions specific to different domains or tenants.
  • User Permission Checks: Verify user permissions against specified domains or tenants.
Created and maintained by the CCC team.

Documentation

Overview

package access implements tools to manage access to resources. It is a wrapper around casbin using an rbac model.

deployment provides the utilities to bootstrap the application with preset configuration

Package access is a generated GoMock package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MigrateRoles added in v0.5.0

func MigrateRoles(ctx context.Context, client UserManager, store *resource.Collection, roleConfig *RoleConfig) error

MigrateRoles runs through and adds the specified roles with specific permissions to the application

func NewDecoder added in v0.1.3

func NewDecoder[T any](a *HandlerClient) *resource.StructDecoder[T]

NewDecoder returns an httpio.Decoder to simplify the validator call to a single location

Types

type Adapter added in v0.1.3

type Adapter interface {
	NewAdapter() (persist.Adapter, error)
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the users client

func New

func New(domains Domains, adapter Adapter) (*Client, error)

New creates a new user client

func (*Client) Handlers

func (c *Client) Handlers(validate *validator.Validate, logHandler LogHandler) Handlers

func (*Client) RequireAll

func (c *Client) RequireAll(ctx context.Context, username accesstypes.User, domain accesstypes.Domain, perms ...accesstypes.Permission) error

func (*Client) RequireResources added in v0.3.0

func (c *Client) RequireResources(
	ctx context.Context, username accesstypes.User, domain accesstypes.Domain, perm accesstypes.Permission, resources ...accesstypes.Resource,
) (bool, []accesstypes.Resource, error)

func (*Client) UserManager

func (c *Client) UserManager() UserManager

type Controller added in v0.1.1

type Controller interface {
	// CheckPermissions checks if a user has the given permissions in a domain
	RequireAll(ctx context.Context, user accesstypes.User, domain accesstypes.Domain, permissions ...accesstypes.Permission) error

	// RequireResource checks if a user has the given permission for a list of resources in a domain
	RequireResources(
		ctx context.Context, username accesstypes.User, domain accesstypes.Domain, perm accesstypes.Permission, resources ...accesstypes.Resource,
	) (ok bool, missing []accesstypes.Resource, err error)

	// UserManager returns the UserManager interface for managing users, roles, and permissions
	UserManager() UserManager

	// Handlers returns the http.HandlerFunc for the access package
	Handlers(validate *validator.Validate, handler LogHandler) Handlers
}

type Domains

type Domains interface {
	DomainIDs(ctx context.Context) ([]string, error)

	DomainExists(ctx context.Context, guarantorID string) (bool, error)
}

type HandlerClient

type HandlerClient struct {
	// contains filtered or unexported fields
}

func (*HandlerClient) AddRole

func (a *HandlerClient) AddRole() http.HandlerFunc

AddRole is the handler to add a new role to the system

Permissions Required: AddRole

func (*HandlerClient) AddRolePermissions

func (a *HandlerClient) AddRolePermissions() http.HandlerFunc

AddRolePermissions is the handler to assign permissions to a given role

Permissions Required: AddRolePermissions

func (*HandlerClient) AddRoleUsers

func (a *HandlerClient) AddRoleUsers() http.HandlerFunc

AddRoleUsers is the handler to assign a role to a list of users

Permissions Required: AddRoleUsers

func (*HandlerClient) DeleteRole

func (a *HandlerClient) DeleteRole() http.HandlerFunc

DeleteRole is the handler to delete a role

Permissions Required: DeleteRole

func (*HandlerClient) DeleteRolePermissions

func (a *HandlerClient) DeleteRolePermissions() http.HandlerFunc

DeleteRolePermissions is the handler to remove permissions from a role

Permissions Required: DeleteRolePermissions

func (*HandlerClient) DeleteRoleUsers

func (a *HandlerClient) DeleteRoleUsers() http.HandlerFunc

DeleteRoleUsers is the handler to delete a list of users from a given role

Permissions Required: DeleteRoleUsers

func (*HandlerClient) RolePermissions

func (a *HandlerClient) RolePermissions() http.HandlerFunc

RolePermissions is the handler to the list of permissions for a given role

Permissions Required: ListRolePermissions

func (*HandlerClient) RoleUsers

func (a *HandlerClient) RoleUsers() http.HandlerFunc

RoleUsers is the handler to the list of users for a given role

Permissions Required: ListRoleUsers

func (*HandlerClient) Roles

func (a *HandlerClient) Roles() http.HandlerFunc

Roles is the handler to get the list of roles in the system for a given domain

Permissions Required: ListRoles

func (*HandlerClient) User

func (a *HandlerClient) User() http.HandlerFunc

User is the handler to get a user

Permissions Required: ViewUsers

func (*HandlerClient) Users

func (a *HandlerClient) Users() http.HandlerFunc

Users is the handler to get the list of users in the system

Permissions Required: ViewUsers

type Handlers

type Handlers interface {
	AddRole() http.HandlerFunc
	AddRolePermissions() http.HandlerFunc
	AddRoleUsers() http.HandlerFunc
	DeleteRole() http.HandlerFunc
	DeleteRolePermissions() http.HandlerFunc
	DeleteRoleUsers() http.HandlerFunc
	RolePermissions() http.HandlerFunc
	Roles() http.HandlerFunc
	RoleUsers() http.HandlerFunc
	User() http.HandlerFunc
	Users() http.HandlerFunc
}

type LogHandler

type LogHandler func(handler func(w http.ResponseWriter, r *http.Request) error) http.HandlerFunc

type MockController added in v0.1.1

type MockController struct {
	// contains filtered or unexported fields
}

MockController is a mock of Controller interface.

func NewMockController added in v0.1.1

func NewMockController(ctrl *gomock.Controller) *MockController

NewMockController creates a new mock instance.

func (*MockController) EXPECT added in v0.1.1

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockController) Handlers added in v0.1.1

func (m *MockController) Handlers(validate *validator.Validate, handler LogHandler) Handlers

Handlers mocks base method.

func (*MockController) RequireAll added in v0.1.1

func (m *MockController) RequireAll(ctx context.Context, user accesstypes.User, domain accesstypes.Domain, permissions ...accesstypes.Permission) error

RequireAll mocks base method.

func (*MockController) RequireResources added in v0.3.0

func (m *MockController) RequireResources(ctx context.Context, username accesstypes.User, domain accesstypes.Domain, perm accesstypes.Permission, resources ...accesstypes.Resource) (bool, []accesstypes.Resource, error)

RequireResources mocks base method.

func (*MockController) UserManager added in v0.1.1

func (m *MockController) UserManager() UserManager

UserManager mocks base method.

type MockControllerMockRecorder added in v0.1.1

type MockControllerMockRecorder struct {
	// contains filtered or unexported fields
}

MockControllerMockRecorder is the mock recorder for MockController.

func (*MockControllerMockRecorder) Handlers added in v0.1.1

func (mr *MockControllerMockRecorder) Handlers(validate, handler any) *gomock.Call

Handlers indicates an expected call of Handlers.

func (*MockControllerMockRecorder) RequireAll added in v0.1.1

func (mr *MockControllerMockRecorder) RequireAll(ctx, user, domain any, permissions ...any) *gomock.Call

RequireAll indicates an expected call of RequireAll.

func (*MockControllerMockRecorder) RequireResources added in v0.3.0

func (mr *MockControllerMockRecorder) RequireResources(ctx, username, domain, perm any, resources ...any) *gomock.Call

RequireResources indicates an expected call of RequireResources.

func (*MockControllerMockRecorder) UserManager added in v0.1.1

func (mr *MockControllerMockRecorder) UserManager() *gomock.Call

UserManager indicates an expected call of UserManager.

type MockDomains

type MockDomains struct {
	// contains filtered or unexported fields
}

MockDomains is a mock of Domains interface.

func NewMockDomains

func NewMockDomains(ctrl *gomock.Controller) *MockDomains

NewMockDomains creates a new mock instance.

func (*MockDomains) DomainExists

func (m *MockDomains) DomainExists(ctx context.Context, guarantorID string) (bool, error)

DomainExists mocks base method.

func (*MockDomains) DomainIDs

func (m *MockDomains) DomainIDs(ctx context.Context) ([]string, error)

DomainIDs mocks base method.

func (*MockDomains) EXPECT

func (m *MockDomains) EXPECT() *MockDomainsMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

type MockDomainsMockRecorder

type MockDomainsMockRecorder struct {
	// contains filtered or unexported fields
}

MockDomainsMockRecorder is the mock recorder for MockDomains.

func (*MockDomainsMockRecorder) DomainExists

func (mr *MockDomainsMockRecorder) DomainExists(ctx, guarantorID any) *gomock.Call

DomainExists indicates an expected call of DomainExists.

func (*MockDomainsMockRecorder) DomainIDs

func (mr *MockDomainsMockRecorder) DomainIDs(ctx any) *gomock.Call

DomainIDs indicates an expected call of DomainIDs.

type MockUserManager

type MockUserManager struct {
	// contains filtered or unexported fields
}

MockUserManager is a mock of UserManager interface.

func NewMockUserManager

func NewMockUserManager(ctrl *gomock.Controller) *MockUserManager

NewMockUserManager creates a new mock instance.

func (*MockUserManager) AddRole

func (m *MockUserManager) AddRole(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) error

AddRole mocks base method.

func (*MockUserManager) AddRolePermissionResources added in v0.2.0

func (m *MockUserManager) AddRolePermissionResources(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permission accesstypes.Permission, resources ...accesstypes.Resource) error

AddRolePermissionResources mocks base method.

func (*MockUserManager) AddRolePermissions

func (m *MockUserManager) AddRolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permissions ...accesstypes.Permission) error

AddRolePermissions mocks base method.

func (*MockUserManager) AddRoleUsers

func (m *MockUserManager) AddRoleUsers(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, users ...accesstypes.User) error

AddRoleUsers mocks base method.

func (*MockUserManager) AddUserRoles

func (m *MockUserManager) AddUserRoles(ctx context.Context, domain accesstypes.Domain, user accesstypes.User, roles ...accesstypes.Role) error

AddUserRoles mocks base method.

func (*MockUserManager) DeleteAllRolePermissions

func (m *MockUserManager) DeleteAllRolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) error

DeleteAllRolePermissions mocks base method.

func (*MockUserManager) DeleteRole

func (m *MockUserManager) DeleteRole(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) (bool, error)

DeleteRole mocks base method.

func (*MockUserManager) DeleteRolePermissionResources added in v0.2.0

func (m *MockUserManager) DeleteRolePermissionResources(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permission accesstypes.Permission, resources ...accesstypes.Resource) error

DeleteRolePermissionResources mocks base method.

func (*MockUserManager) DeleteRolePermissions

func (m *MockUserManager) DeleteRolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permissions ...accesstypes.Permission) error

DeleteRolePermissions mocks base method.

func (*MockUserManager) DeleteRoleUsers

func (m *MockUserManager) DeleteRoleUsers(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, users ...accesstypes.User) error

DeleteRoleUsers mocks base method.

func (*MockUserManager) DeleteUserRoles added in v0.2.0

func (m *MockUserManager) DeleteUserRoles(ctx context.Context, domain accesstypes.Domain, user accesstypes.User, roles ...accesstypes.Role) error

DeleteUserRoles mocks base method.

func (*MockUserManager) DomainExists

func (m *MockUserManager) DomainExists(ctx context.Context, domain accesstypes.Domain) (bool, error)

DomainExists mocks base method.

func (*MockUserManager) Domains

func (m *MockUserManager) Domains(ctx context.Context) ([]accesstypes.Domain, error)

Domains mocks base method.

func (*MockUserManager) EXPECT

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockUserManager) RoleExists

func (m *MockUserManager) RoleExists(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) bool

RoleExists mocks base method.

func (*MockUserManager) RolePermissions

RolePermissions mocks base method.

func (*MockUserManager) RoleUsers

func (m *MockUserManager) RoleUsers(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) ([]accesstypes.User, error)

RoleUsers mocks base method.

func (*MockUserManager) Roles

Roles mocks base method.

func (*MockUserManager) User

func (m *MockUserManager) User(ctx context.Context, user accesstypes.User, domain ...accesstypes.Domain) (*UserAccess, error)

User mocks base method.

func (*MockUserManager) UserPermissions

UserPermissions mocks base method.

func (*MockUserManager) UserRoles

UserRoles mocks base method.

func (*MockUserManager) Users

func (m *MockUserManager) Users(ctx context.Context, domain ...accesstypes.Domain) ([]*UserAccess, error)

Users mocks base method.

type MockUserManagerMockRecorder

type MockUserManagerMockRecorder struct {
	// contains filtered or unexported fields
}

MockUserManagerMockRecorder is the mock recorder for MockUserManager.

func (*MockUserManagerMockRecorder) AddRole

func (mr *MockUserManagerMockRecorder) AddRole(ctx, domain, role any) *gomock.Call

AddRole indicates an expected call of AddRole.

func (*MockUserManagerMockRecorder) AddRolePermissionResources added in v0.2.0

func (mr *MockUserManagerMockRecorder) AddRolePermissionResources(ctx, domain, role, permission any, resources ...any) *gomock.Call

AddRolePermissionResources indicates an expected call of AddRolePermissionResources.

func (*MockUserManagerMockRecorder) AddRolePermissions

func (mr *MockUserManagerMockRecorder) AddRolePermissions(ctx, domain, role any, permissions ...any) *gomock.Call

AddRolePermissions indicates an expected call of AddRolePermissions.

func (*MockUserManagerMockRecorder) AddRoleUsers

func (mr *MockUserManagerMockRecorder) AddRoleUsers(ctx, domain, role any, users ...any) *gomock.Call

AddRoleUsers indicates an expected call of AddRoleUsers.

func (*MockUserManagerMockRecorder) AddUserRoles

func (mr *MockUserManagerMockRecorder) AddUserRoles(ctx, domain, user any, roles ...any) *gomock.Call

AddUserRoles indicates an expected call of AddUserRoles.

func (*MockUserManagerMockRecorder) DeleteAllRolePermissions

func (mr *MockUserManagerMockRecorder) DeleteAllRolePermissions(ctx, domain, role any) *gomock.Call

DeleteAllRolePermissions indicates an expected call of DeleteAllRolePermissions.

func (*MockUserManagerMockRecorder) DeleteRole

func (mr *MockUserManagerMockRecorder) DeleteRole(ctx, domain, role any) *gomock.Call

DeleteRole indicates an expected call of DeleteRole.

func (*MockUserManagerMockRecorder) DeleteRolePermissionResources added in v0.2.0

func (mr *MockUserManagerMockRecorder) DeleteRolePermissionResources(ctx, domain, role, permission any, resources ...any) *gomock.Call

DeleteRolePermissionResources indicates an expected call of DeleteRolePermissionResources.

func (*MockUserManagerMockRecorder) DeleteRolePermissions

func (mr *MockUserManagerMockRecorder) DeleteRolePermissions(ctx, domain, role any, permissions ...any) *gomock.Call

DeleteRolePermissions indicates an expected call of DeleteRolePermissions.

func (*MockUserManagerMockRecorder) DeleteRoleUsers

func (mr *MockUserManagerMockRecorder) DeleteRoleUsers(ctx, domain, role any, users ...any) *gomock.Call

DeleteRoleUsers indicates an expected call of DeleteRoleUsers.

func (*MockUserManagerMockRecorder) DeleteUserRoles added in v0.2.0

func (mr *MockUserManagerMockRecorder) DeleteUserRoles(ctx, domain, user any, roles ...any) *gomock.Call

DeleteUserRoles indicates an expected call of DeleteUserRoles.

func (*MockUserManagerMockRecorder) DomainExists

func (mr *MockUserManagerMockRecorder) DomainExists(ctx, domain any) *gomock.Call

DomainExists indicates an expected call of DomainExists.

func (*MockUserManagerMockRecorder) Domains

func (mr *MockUserManagerMockRecorder) Domains(ctx any) *gomock.Call

Domains indicates an expected call of Domains.

func (*MockUserManagerMockRecorder) RoleExists

func (mr *MockUserManagerMockRecorder) RoleExists(ctx, domain, role any) *gomock.Call

RoleExists indicates an expected call of RoleExists.

func (*MockUserManagerMockRecorder) RolePermissions

func (mr *MockUserManagerMockRecorder) RolePermissions(ctx, domain, role any) *gomock.Call

RolePermissions indicates an expected call of RolePermissions.

func (*MockUserManagerMockRecorder) RoleUsers

func (mr *MockUserManagerMockRecorder) RoleUsers(ctx, domain, role any) *gomock.Call

RoleUsers indicates an expected call of RoleUsers.

func (*MockUserManagerMockRecorder) Roles

func (mr *MockUserManagerMockRecorder) Roles(ctx, domain any) *gomock.Call

Roles indicates an expected call of Roles.

func (*MockUserManagerMockRecorder) User

func (mr *MockUserManagerMockRecorder) User(ctx, user any, domain ...any) *gomock.Call

User indicates an expected call of User.

func (*MockUserManagerMockRecorder) UserPermissions

func (mr *MockUserManagerMockRecorder) UserPermissions(ctx, user any, domain ...any) *gomock.Call

UserPermissions indicates an expected call of UserPermissions.

func (*MockUserManagerMockRecorder) UserRoles

func (mr *MockUserManagerMockRecorder) UserRoles(ctx, user any, domain ...any) *gomock.Call

UserRoles indicates an expected call of UserRoles.

func (*MockUserManagerMockRecorder) Users

func (mr *MockUserManagerMockRecorder) Users(ctx any, domain ...any) *gomock.Call

Users indicates an expected call of Users.

type PermissionsListFunc

type PermissionsListFunc func() []accesstypes.Permission

PermissionsListFunc is a function that provides the list of app permissions for the users client

type PostgresAdapter added in v0.1.3

type PostgresAdapter struct {
	// contains filtered or unexported fields
}

func NewPostgresAdapter added in v0.1.3

func NewPostgresAdapter(connConfig *pgx.ConnConfig, databaseName, tableName string) *PostgresAdapter

func (*PostgresAdapter) NewAdapter added in v0.1.3

func (p *PostgresAdapter) NewAdapter() (persist.Adapter, error)

type Role

type Role struct {
	Name        accesstypes.Role
	Permissions map[accesstypes.Permission][]accesstypes.Resource
}

type RoleConfig added in v0.5.0

type RoleConfig struct {
	Roles []*Role `json:"roles"`
}

type SpannerAdapter added in v0.1.3

type SpannerAdapter struct {
	// contains filtered or unexported fields
}

func NewSpannerAdapter added in v0.1.3

func NewSpannerAdapter(databaseName, tableName string) *SpannerAdapter

func (*SpannerAdapter) NewAdapter added in v0.1.3

func (s *SpannerAdapter) NewAdapter() (persist.Adapter, error)

type UserAccess

type UserAccess struct {
	Name        string
	Roles       accesstypes.RoleCollection
	Permissions accesstypes.UserPermissionCollection
}

UserAccess struct contains the name and role mappings for a user

type UserManager

type UserManager interface {
	// AddRoleUsers assigns a given role to a slice of users if the role exists
	AddRoleUsers(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, users ...accesstypes.User) error

	// AddUserRoles assigns a list of roles to a user if the role exists
	AddUserRoles(ctx context.Context, domain accesstypes.Domain, user accesstypes.User, roles ...accesstypes.Role) error

	// DeleteRoleUsers removes users from a given role
	DeleteRoleUsers(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, users ...accesstypes.User) error

	// DeleteUserRoles deletes the role assignment for a user in a specific domain.
	// Behavior is the same whether or not the role exists for the user.
	DeleteUserRoles(ctx context.Context, domain accesstypes.Domain, user accesstypes.User, roles ...accesstypes.Role) error

	// User returns a User by the given username with the roles that have been assigned.
	User(ctx context.Context, user accesstypes.User, domain ...accesstypes.Domain) (*UserAccess, error)

	// Users gets a list of users with their assigned roles
	Users(ctx context.Context, domain ...accesstypes.Domain) ([]*UserAccess, error)

	// UserRoles returns a map of the domain
	UserRoles(ctx context.Context, user accesstypes.User, domain ...accesstypes.Domain) (accesstypes.RoleCollection, error)

	// UserPermissions returns a map of domains with a slice of permissions for each
	UserPermissions(ctx context.Context, user accesstypes.User, domain ...accesstypes.Domain) (accesstypes.UserPermissionCollection, error)

	// AddRole adds a new role to a domain without assigning it to a user
	//
	// Note: due to the design of casbin, we must add a "noop" user to the role to enumerate it without permissions.
	AddRole(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) error

	// RoleExists determines if the given Role exists for Domain
	RoleExists(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) bool

	// Roles returns the full list of roles for a given domain
	Roles(ctx context.Context, domain accesstypes.Domain) ([]accesstypes.Role, error)

	// DeleteRole deletes a role from the system.
	// If there are users assigned, it will not be deleted.
	DeleteRole(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) (bool, error)

	// AddRolePermissions adds a list of permissions to a role in a given domain
	AddRolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permissions ...accesstypes.Permission) error

	// AddRolePermissionResources adds a list of resources to a permission for a role in a domain
	AddRolePermissionResources(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permission accesstypes.Permission, resources ...accesstypes.Resource) error

	// DeleteRolePermissions removes a list of permissions to a role in a given domain
	DeleteRolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permissions ...accesstypes.Permission) error

	// DeleteRolePermissionResources removes a list of resources from a permission for a role in a domain
	DeleteRolePermissionResources(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role, permission accesstypes.Permission, resources ...accesstypes.Resource) error

	// DeleteAllRolePermissions removes all permissions for a given role in a domain
	DeleteAllRolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) error

	// RoleUsers returns the list of users attached to a role in a given domain
	RoleUsers(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) ([]accesstypes.User, error)

	// RolePermissions returns the list of permissions attached to a role in a given domain
	RolePermissions(ctx context.Context, domain accesstypes.Domain, role accesstypes.Role) (accesstypes.RolePermissionCollection, error)

	// Domains returns the full list of domains
	Domains(ctx context.Context) ([]accesstypes.Domain, error)

	// DomainExists returns true if the domain provided is a valid
	DomainExists(ctx context.Context, domain accesstypes.Domain) (bool, error)
}

UserManager is the interface for managing RBAC including the management of roles and permissions for users

Directories

Path Synopsis
package mock contains the generated mocks for the project.
package mock contains the generated mocks for the project.
mock_access
Package mock_access is a generated GoMock package.
Package mock_access is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL