@universis/janitor 2.4.4 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.esm.js +151 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +151 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/EnableReadAccessScope.d.ts +6 -0
- package/src/EnableReadAccessScope.js +157 -0
- package/src/index.d.ts +2 -1
- package/src/index.js +1 -0
package/README.md
CHANGED
|
@@ -440,6 +440,26 @@ add `scopeAccess` section under `settings/universis/janitor` configuration and s
|
|
|
440
440
|
|
|
441
441
|
The given files should contain an array of scope access configuration elements as described in the `ScopeAccessConfiguration` section.
|
|
442
442
|
|
|
443
|
+
## EnableReadAccessScope
|
|
444
|
+
|
|
445
|
+
`EnableReadAccessScope` is an application service which enables read-only client scopes (`students:read`, `teachers:read` and `registrar:read`) alongside the default `students`, `teachers` and `registrar` scopes.
|
|
446
|
+
|
|
447
|
+
Register service under application services:
|
|
448
|
+
|
|
449
|
+
```json
|
|
450
|
+
{
|
|
451
|
+
"services": [
|
|
452
|
+
{
|
|
453
|
+
"serviceType": "@universis/janitor#EnableReadAccessScope"
|
|
454
|
+
}
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
When the application container is ready, the service enumerates all model definitions and, for every scope-based privilege with read access (`mask: 1`) — at model or field level — which includes a client scope, it appends the corresponding `<scope>:read` scope. It also extends `ScopeAccessConfiguration` by inserting a read-only (`access: ["read"]`) element with the `<scope>:read` scope right after every element that grants read access to the original scope.
|
|
460
|
+
|
|
461
|
+
This allows OAuth2 clients to request e.g. `students:read` and get read access to the same resources as `students`, without being able to modify them.
|
|
462
|
+
|
|
443
463
|
|
|
444
464
|
## RemoteAddressValidator
|
|
445
465
|
|
package/dist/index.d.ts
CHANGED
|
@@ -357,5 +357,10 @@ declare class DisableNetworkRestrictAccess extends NetworkRestrictAccess {
|
|
|
357
357
|
|
|
358
358
|
declare function validateNetworkAccess(): Handler;
|
|
359
359
|
|
|
360
|
-
|
|
360
|
+
declare class EnableReadAccessScope extends ApplicationService {
|
|
361
|
+
readonly clientScopes: string[];
|
|
362
|
+
protected enable(clientScope: string): void;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export { AppRateLimitService, AppSpeedLimitService, DefaultScopeAccessConfiguration, DisableNetworkRestrictAccess, DisableUserClientScopeVerification, EnableNetworkRestrictAccess, EnableReadAccessScope, EnableScopeAccessConfiguration, ExtendScopeAccessConfiguration, HttpBearerStrategy, HttpRemoteAddrForbiddenError, InvalidClientScopeError, NetworkRestrictAccess, OAuth2ClientService, OutputCachingService, PassportService, RateLimitService, RedisClientStore, RemoteAddressValidator, ScopeAccessConfiguration, ScopeString, SpeedLimitService, UserWithNetworkAddress, VerifyUserClientScope, outputCache, validateClientScope, validateNetworkAccess, validateScope };
|
|
361
366
|
export type { GenericUser, OAuth2AuthorizeUser, OAuth2MethodOptions, OAuth2ServiceSettings, OAuth2User, OAuth2UserProfile, OutputCachingElement, OutputCachingProfile, RateLimitServiceConfiguration, RateLimitStoreConstructor, ScopeAccessConfigurationElement, ScopeAccessConfigurationSection, SpeedLimitServiceConfiguration, SpeedLimitStoreConstructor, UniversisConfigurationSection };
|
package/dist/index.esm.js
CHANGED
|
@@ -1929,5 +1929,155 @@ class DisableNetworkRestrictAccess extends NetworkRestrictAccess {
|
|
|
1929
1929
|
}
|
|
1930
1930
|
}
|
|
1931
1931
|
|
|
1932
|
-
|
|
1932
|
+
class EnableReadAccessScope extends ApplicationService {
|
|
1933
|
+
/**
|
|
1934
|
+
*
|
|
1935
|
+
* @param {import('@themost/express').ExpressDataApplication|import('@themost/common').ApplicationBase} app
|
|
1936
|
+
*/
|
|
1937
|
+
constructor(app) {
|
|
1938
|
+
super(app);
|
|
1939
|
+
/**
|
|
1940
|
+
* @type {import('rxjs').Observable<import('express').Application>}
|
|
1941
|
+
*/
|
|
1942
|
+
const container = app && app.container;
|
|
1943
|
+
if (container) {
|
|
1944
|
+
container.subscribe((container) => {
|
|
1945
|
+
if (container) {
|
|
1946
|
+
for (const scope of this.clientScopes) {
|
|
1947
|
+
this.enable(scope);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
get clientScopes() {
|
|
1955
|
+
return [
|
|
1956
|
+
'students',
|
|
1957
|
+
'teachers',
|
|
1958
|
+
'registrar'];
|
|
1959
|
+
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
/**
|
|
1963
|
+
* @protected
|
|
1964
|
+
* @param {string} scope
|
|
1965
|
+
*/
|
|
1966
|
+
enable(scope) {
|
|
1967
|
+
const searchScope = scope;
|
|
1968
|
+
const targetScope = scope + ':read';
|
|
1969
|
+
// get schema loader strategy for enumerating models
|
|
1970
|
+
const schemaLoader = this.getApplication().getConfiguration().getStrategy(SchemaLoaderStrategy);
|
|
1971
|
+
if (schemaLoader) {
|
|
1972
|
+
const models = schemaLoader.getModels();
|
|
1973
|
+
let updated = 0;
|
|
1974
|
+
// enumerate models that includes scope based privileges
|
|
1975
|
+
// where mask is 1 (read)
|
|
1976
|
+
for (const model of models) {
|
|
1977
|
+
/**
|
|
1978
|
+
* @type {import('@themost/common').DataModelProperties}
|
|
1979
|
+
*/
|
|
1980
|
+
const definition = schemaLoader.getModelDefinition(model);
|
|
1981
|
+
if (Array.isArray(definition.privileges)) {
|
|
1982
|
+
const privileges = definition.privileges.filter((item) => {
|
|
1983
|
+
return item.mask === 1 && Array.isArray(item.scope) && item.scope.includes(searchScope);
|
|
1984
|
+
});
|
|
1985
|
+
let shouldUpdate = false;
|
|
1986
|
+
if (privileges.length > 0) {
|
|
1987
|
+
shouldUpdate = true;
|
|
1988
|
+
// add target scope to privileges
|
|
1989
|
+
for (const privilege of privileges) {
|
|
1990
|
+
if (!privilege.scope.includes(targetScope)) {
|
|
1991
|
+
privilege.scope.push(targetScope);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
// get fields that includes scope based privileges
|
|
1996
|
+
const fields = definition.fields.filter((item) => {
|
|
1997
|
+
return Array.isArray(item.privileges);
|
|
1998
|
+
}).filter((item) => {
|
|
1999
|
+
const privileges = item.privileges.filter((privilege) => {
|
|
2000
|
+
return privilege.mask === 1 && Array.isArray(privilege.scope) && privilege.scope.includes(searchScope);
|
|
2001
|
+
});
|
|
2002
|
+
return privileges.length > 0;
|
|
2003
|
+
});
|
|
2004
|
+
if (fields.length > 0) {
|
|
2005
|
+
shouldUpdate = true;
|
|
2006
|
+
for (const field of fields) {
|
|
2007
|
+
const privileges = field.privileges.filter((privilege) => {
|
|
2008
|
+
return privilege.mask === 1 && Array.isArray(privilege.scope) && privilege.scope.includes(searchScope);
|
|
2009
|
+
});
|
|
2010
|
+
for (const privilege of privileges) {
|
|
2011
|
+
if (!privilege.scope.includes(targetScope)) {
|
|
2012
|
+
privilege.scope.push(targetScope);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
// get views with scope based privileges
|
|
2019
|
+
// noinspection JSUnresolvedReference
|
|
2020
|
+
if (Array.isArray(definition.views)) {
|
|
2021
|
+
// noinspection JSUnresolvedReference
|
|
2022
|
+
const views = definition.views.filter((item) => {
|
|
2023
|
+
return Array.isArray(item.privileges);
|
|
2024
|
+
});
|
|
2025
|
+
for (const view of views) {
|
|
2026
|
+
const privileges = view.privileges.filter((privilege) => {
|
|
2027
|
+
return privilege.mask === 1 && Array.isArray(privilege.scope) && privilege.scope.includes(searchScope);
|
|
2028
|
+
});
|
|
2029
|
+
if (privileges.length > 0) {
|
|
2030
|
+
shouldUpdate = true;
|
|
2031
|
+
for (const privilege of privileges) {
|
|
2032
|
+
if (!privilege.scope.includes(targetScope)) {
|
|
2033
|
+
privilege.scope.push(targetScope);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
|
|
2041
|
+
if (shouldUpdate) {
|
|
2042
|
+
updated++;
|
|
2043
|
+
// update model definition
|
|
2044
|
+
schemaLoader.setModelDefinition(definition);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
if (updated > 0) {
|
|
2049
|
+
TraceUtils.log(`${this.constructor.name}: Updated ${updated} model definition(s) to include scope "${targetScope}"`);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
const scopeConfiguration = this.getApplication().getConfiguration().getStrategy(ScopeAccessConfiguration);
|
|
2054
|
+
if (scopeConfiguration) {
|
|
2055
|
+
const elements = scopeConfiguration.elements.
|
|
2056
|
+
filter((item) => item.scope.includes(searchScope)).
|
|
2057
|
+
filter((item) => item.access.includes('read'));
|
|
2058
|
+
if (elements.length > 0) {
|
|
2059
|
+
const insertElements = [];
|
|
2060
|
+
for (const element of elements) {
|
|
2061
|
+
insertElements.push({
|
|
2062
|
+
insertAfter: element,
|
|
2063
|
+
resource: element.resource,
|
|
2064
|
+
scope: [targetScope],
|
|
2065
|
+
access: ['read']
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
for (const { insertAfter, resource, scope, access } of insertElements) {
|
|
2069
|
+
const index = scopeConfiguration.elements.findIndex((element) => element === insertAfter);
|
|
2070
|
+
scopeConfiguration.elements.splice(index + 1, 0, {
|
|
2071
|
+
resource,
|
|
2072
|
+
scope,
|
|
2073
|
+
access
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
TraceUtils.log(`${this.constructor.name}: Updated ${elements.length} scope access configuration(s) to include scope "${targetScope}"`);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
export { AppRateLimitService, AppSpeedLimitService, DefaultScopeAccessConfiguration, DisableNetworkRestrictAccess, DisableUserClientScopeVerification, EnableNetworkRestrictAccess, EnableReadAccessScope, EnableScopeAccessConfiguration, ExtendScopeAccessConfiguration, HttpAccountDisabled, HttpBearerStrategy, HttpBearerTokenExpired, HttpBearerTokenNotFound, HttpBearerTokenRequired, HttpNetworkAccessForbiddenError, HttpRemoteAddrForbiddenError, InvalidClientScopeError, NetworkRestrictAccess, OAuth2ClientService, OutputCachingService, PassportService, RateLimitService, RedisClientStore, RemoteAddressValidator, ScopeAccessConfiguration, ScopeString, SpeedLimitService, UserWithNetworkAddress, VerifyUserClientScope, outputCache, validateClientScope, validateNetworkAddress, validateScope };
|
|
1933
2083
|
//# sourceMappingURL=index.esm.js.map
|