authorizeWriteTargets evaluates perm for each resolved DML/DDL write target and returns the resources that were denied (empty = all authorized). A target whose database is already JIT-granted is skipped. databaseLevelOnly selects the precision of the check. For a single statement it is false and ea
(
ctx context.Context,
user *store.UserMessage,
instance *store.InstanceMessage,
database *store.DatabaseMessage,
perm permission.Permission,
targets []writeTargetResource,
grantedTargets map[string]struct{},
databaseLevelOnly bool,
iamPolicies ...*storepb.IamPolicy,
)
| 1785 | // always present because hasDatabaseAccessRights keeps the environment clause for SQLDml/SQLDdl, |
| 1786 | // so omitting it would reintroduce a "no such attribute" error. SUP-222 / BYT-9698. |
| 1787 | func (s *SQLService) authorizeWriteTargets( |
| 1788 | ctx context.Context, |
| 1789 | user *store.UserMessage, |
| 1790 | instance *store.InstanceMessage, |
| 1791 | database *store.DatabaseMessage, |
| 1792 | perm permission.Permission, |
| 1793 | targets []writeTargetResource, |
| 1794 | grantedTargets map[string]struct{}, |
| 1795 | databaseLevelOnly bool, |
| 1796 | iamPolicies ...*storepb.IamPolicy, |
| 1797 | ) ([]string, error) { |
| 1798 | // One timestamp per request so every target evaluates request.time consistently. |
| 1799 | requestTime := time.Now() |
| 1800 | |
| 1801 | // environment_id of the request database, reused without a lookup for same-database targets. |
| 1802 | envByDatabase := map[string]string{database.DatabaseName: ""} |
| 1803 | if database.EffectiveEnvironmentID != nil { |
| 1804 | envByDatabase[database.DatabaseName] = *database.EffectiveEnvironmentID |
| 1805 | } |
| 1806 | checkedDatabase := map[string]bool{} // databaseLevelOnly: authorize each target database once |
| 1807 | |
| 1808 | var deniedResources []string |
| 1809 | for _, t := range targets { |
| 1810 | databaseFullName := common.FormatDatabase(instance.ResourceID, t.database) |
| 1811 | if _, granted := grantedTargets[databaseFullName]; granted { |
| 1812 | continue |
| 1813 | } |
| 1814 | // A target with no table name is a database-only target: DDL on a non-table object |
| 1815 | // (view/procedure/function/trigger/…) whose only auth-relevant identity is its database. |
| 1816 | // Authorize it at the database level — like a multi-statement batch — so a qualified |
| 1817 | // cross-database object DDL is gated by its own database, not the request database, with |
| 1818 | // no spurious table-name match. SUP-222 / BYT-9698. |
| 1819 | useDatabaseLevel := databaseLevelOnly || t.table == "" |
| 1820 | if useDatabaseLevel { |
| 1821 | if checkedDatabase[t.database] { |
| 1822 | continue |
| 1823 | } |
| 1824 | checkedDatabase[t.database] = true |
| 1825 | } |
| 1826 | |
| 1827 | // A denial names the bare database for a database-level check, else the schema/table target. |
| 1828 | deniedResource := databaseFullName |
| 1829 | if !useDatabaseLevel { |
| 1830 | deniedResource = formatWriteTargetResource(databaseFullName, t.schema, t.table) |
| 1831 | } |
| 1832 | |
| 1833 | env, known := envByDatabase[t.database] |
| 1834 | if !known { |
| 1835 | targetName := t.database |
| 1836 | targetDB, err := s.store.GetDatabase(ctx, &store.FindDatabaseMessage{ |
| 1837 | Workspace: common.GetWorkspaceIDFromContext(ctx), |
| 1838 | InstanceID: &instance.ResourceID, |
| 1839 | DatabaseName: &targetName, |
| 1840 | }) |
| 1841 | if err != nil { |
| 1842 | return nil, connect.NewError(connect.CodeInternal, errors.Errorf("failed to resolve target database %q: %v", t.database, err)) |
| 1843 | } |
| 1844 | if targetDB == nil { |
no test coverage detected