generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b225850
commit ab3ebf2
Showing
6 changed files
with
230 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import type { GenericMessage } from '../types/message-types.js'; | ||
import type { MessagesGet } from '../interfaces/messages-get.js'; | ||
import type { MessagesGetMessage } from '../types/messages-types.js'; | ||
import type { MessageStore } from '../types/message-store.js'; | ||
import type { MessagesGetPermissionScope, RecordsPermissionScope } from '../types/permission-types.js'; | ||
import type { RecordsDeleteMessage, RecordsWriteMessage } from '../types/records-types.js'; | ||
|
||
import { GrantAuthorization } from './grant-authorization.js'; | ||
import { Message } from './message.js'; | ||
import { PermissionGrant } from '../protocols/permission-grant.js'; | ||
import { PermissionsProtocol } from '../protocols/permissions.js'; | ||
import { DwnError, DwnErrorCode } from './dwn-error.js'; | ||
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js'; | ||
|
||
export class MessagesGrantAuthorization { | ||
|
||
/** | ||
* Authorizes a RecordsReadMessage using the given permission grant. | ||
* @param messageStore Used to check if the given grant has been revoked. | ||
*/ | ||
public static async authorizeMessagesGetGrant(input: { | ||
messagesGetMessage: MessagesGetMessage, | ||
messageToGet: GenericMessage, | ||
expectedGrantor: string, | ||
expectedGrantee: string, | ||
permissionGrant: PermissionGrant, | ||
messageStore: MessageStore, | ||
}): Promise<void> { | ||
const { | ||
messagesGetMessage, messageToGet, expectedGrantor, expectedGrantee, permissionGrant, messageStore | ||
} = input; | ||
|
||
await GrantAuthorization.performBaseValidation({ | ||
incomingMessage: messagesGetMessage, | ||
expectedGrantor, | ||
expectedGrantee, | ||
permissionGrant, | ||
messageStore | ||
}); | ||
|
||
const scope = permissionGrant.scope as MessagesGetPermissionScope; | ||
await MessagesGrantAuthorization.verifyScope(expectedGrantor, messageToGet, scope, messageStore); | ||
} | ||
|
||
public static async authorizeMessagesGetRecords(input: { | ||
tenant: string, | ||
incomingMessage: MessagesGet, | ||
recordMessage: RecordsWriteMessage | RecordsDeleteMessage, | ||
messageStore: MessageStore, | ||
}): Promise<void> { | ||
const { tenant, incomingMessage, recordMessage, messageStore } = input; | ||
const recordsWriteToAuthorize = await this.getRecordsWriteMessageToAuthorize(tenant, recordMessage, messageStore); | ||
|
||
const { descriptor } = recordsWriteToAuthorize; | ||
if (descriptor.published === true) { | ||
// authentication is not required for published data | ||
return; | ||
} else if (incomingMessage.author !== undefined && incomingMessage.author === descriptor.recipient) { | ||
// The recipient of a message may always read it | ||
return; | ||
} else { | ||
// if we reached here, the record is not authorized | ||
throw new DwnError(DwnErrorCode.MessagesGetRecordsAuthorizationFailed, 'record message failed authorization'); | ||
} | ||
} | ||
|
||
/** | ||
* Verifies the given record against the scope of the given grant. | ||
*/ | ||
private static async verifyScope( | ||
tenant: string, | ||
message: GenericMessage, | ||
incomingScope: MessagesGetPermissionScope, | ||
messageStore: MessageStore, | ||
): Promise<void> { | ||
if (incomingScope.protocol === undefined) { | ||
// no protocol to verify, the grant has access to all records | ||
return; | ||
} | ||
|
||
// Only protocol scopes are currently supported for Messages interface authorizations | ||
if (message.descriptor.interface === DwnInterfaceName.Records) { | ||
const recordMessage = message as RecordsWriteMessage | RecordsDeleteMessage; | ||
const recordsWriteToAuthorize = await this.getRecordsWriteMessageToAuthorize(tenant, recordMessage, messageStore); | ||
const { protocol: recordProtocol } = recordsWriteToAuthorize.descriptor; | ||
if (recordProtocol === PermissionsProtocol.uri) { | ||
// the incoming message is a grant or revocation message | ||
// we need to get the protocol associated with the underlying grant | ||
|
||
let grantMessage: RecordsWriteMessage | undefined; | ||
if (recordsWriteToAuthorize.descriptor.protocolPath === PermissionsProtocol.revocationPath) { | ||
// fetch the grant associated with the revocation | ||
const permissionGrantId = recordsWriteToAuthorize.descriptor.parentId!; | ||
const grantAuthorizedMessagesQuery = { | ||
interface : DwnInterfaceName.Records, | ||
method : DwnMethodName.Write, | ||
protocol : PermissionsProtocol.uri, | ||
protocolPath : PermissionsProtocol.grantPath, | ||
recordId : permissionGrantId, | ||
}; | ||
const { messages: grantMessages } = await messageStore.query(tenant, [ grantAuthorizedMessagesQuery ]); | ||
grantMessage = await Message.getNewestMessage(grantMessages) as RecordsWriteMessage | undefined; | ||
} else { | ||
grantMessage = recordsWriteToAuthorize; | ||
} | ||
|
||
const grantMessageScope = grantMessage ? (await PermissionGrant.parse(grantMessage)).scope as RecordsPermissionScope : undefined; | ||
if (grantMessageScope && grantMessageScope.protocol === incomingScope.protocol) { | ||
// the record grant message has the same scope protocol as the incoming scope protocol | ||
return; | ||
} | ||
} else {recordProtocol === incomingScope.protocol;} { | ||
// the record protocol matches the incoming scope protocol | ||
return; | ||
} | ||
} | ||
|
||
throw new DwnError(DwnErrorCode.MessagesGetGrantProtocolScopeAuthorizationFailed, 'record message failed protocol authorization'); | ||
} | ||
|
||
private static async getRecordsWriteMessageToAuthorize( | ||
tenant: string, | ||
message: RecordsWriteMessage | RecordsDeleteMessage, | ||
messageStore: MessageStore | ||
): Promise<RecordsWriteMessage> { | ||
if (message.descriptor.method === DwnMethodName.Write) { | ||
return message as RecordsWriteMessage; | ||
} else { | ||
// it is a RecordsDeleteMessage, need to fetch the latest RecordWrite to verify the authorization | ||
// get existing RecordsWrite messages matching the `recordId` | ||
const query = { | ||
interface : DwnInterfaceName.Records, | ||
method : DwnMethodName.Write, | ||
recordId : (message as RecordsDeleteMessage).descriptor.recordId | ||
}; | ||
|
||
const { messages: existingMessages } = await messageStore.query(tenant, [ query ]); | ||
const newestWrite = await Message.getNewestMessage(existingMessages); | ||
if (newestWrite !== undefined) { | ||
return newestWrite as RecordsWriteMessage; | ||
} | ||
} | ||
|
||
throw new DwnError(DwnErrorCode.MessagesGetWriteRecordNotFound, 'record not found'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters