如何从扩展程序的后端查询 azure devops 用户的组成员身份?

问题描述 投票:0回答:2

我正在开发一个 azure devops 扩展,它向 UI 添加了一个新的中心。扩展中的 JavaScript 代码将应用程序令牌(通过

SDK.getAppToken()
检索)和访问令牌(通过
SDK.getAccessToken()
检索)传递到后端。我正在尝试使用访问令牌通过 Memberships - List API 检索已登录用户的组成员身份。然而我收到了未经授权的 401。

我正在Postman中做这个实验。因此,首先,当扩展程序将请求发送到后端时,我使用 Chrome 开发人员工具检索应用程序令牌和访问令牌。然后我在 Postman 中使用访问令牌作为不记名令牌。

GET https://vssps.dev.azure.com/{organization}/_apis/graph/Memberships/{my subject descriptor}?api-version=6.0-preview.1

这将返回 401。

                        <div class="title">401 - Uh-oh, you do not have access.</div>
                        <div class="details">The request requires authentication.</div>

我知道访问令牌是正确的,因为我能够成功使用它来访问:

GET https://vssps.dev.azure.com/{organization}/_apis/connectionData

我知道主题描述符是正确的,因为我正在使用上面的 connectionData API 返回的值。

你知道我是否犯了一个明显的错误吗? 我的主要目标是能够检查使用我的扩展程序的用户是否属于特定组(也就是说,我不需要他们的成员资格列表,而只需要他们是否属于我关心的这个组)。我还尝试了 检查会员资格存在 API ,结果相同 401。

azure-devops azure-devops-rest-api azure-devops-extensions
2个回答
1
投票

我终于想通了,留在这里以供将来其他人使用。

TL;DR: 我需要将 vso.graph 范围添加到我的扩展的清单的范围属性中。

会员 API 的 API 参考提到每次调用需要哪些范围。

Scopes
Name        Description
vso.graph   Grants the ability to read user, group, scope and group membership information

扩展所需的范围可以在其清单中定义,如此处所述。在我的清单中,我根本没有范围属性,因此它默认为 vso.extension.default 的范围,这不足以访问成员资格 API。所以我添加了以下几行:

  "scopes": [
    "vso.graph"
  ],

然后我重新发布了扩展。另一方面,安装扩展程序的组织方面,我必须接受扩展程序现在要求的附加权限。这不会自动发生。此处解释:更改已发布扩展的范围


0
投票

这获取组的主体名称

async function getGroupPrincipalName(groupApiURL, myBearerToken) {
let groupName = '';

await fetch(groupApiURL, {
    method: 'GET',
    headers: {
        'Content-Type': 'text/plain',
        'Authorization': myBearerToken
    }
}).then(function (response) {
    return response.arrayBuffer();
}).then(function (buffer) {
    const decoder = new TextDecoder('iso-8859-1');
    memberJsonInfo = decoder.decode(buffer);
    groupName = JSON.parse(memberJsonInfo).principalName;
});

return groupName;

}

这将获取当前用户所属的所有集合 URI 组

async function getUserGroupMemberships() {
var jsonURLGroups = [];
var userDescriptor = 'aad.' + btoa(VSS.getWebContext().user.subjectId);
var myOrganization = VSS.getWebContext().collection.name;
var myAccessToken = await VSS.getAccessToken();
var myBearerToken = "Bearer " + myAccessToken.token;

var apiURL = 'https://vssps.dev.azure.com/' + myOrganization + '/_apis/Graph/Memberships/' + userDescriptor;

await fetch(apiURL, {
    method: 'GET',
    headers: {
        'Content-Type': 'text/plain',
        'Authorization': myBearerToken
    }
}).then(function (response) {
    return response.arrayBuffer();
}).then(function (buffer) {
    const decoder = new TextDecoder('iso-8859-1');
    jsonString = decoder.decode(buffer);
    JSON.parse(jsonString).value.forEach(item => {
        if (item._links && item._links.container && item._links.container.href) {
            jsonURLGroups.push(item._links.container.href);
        }
    });
});

return jsonURLGroups;

}

此功能允许每次生成新的组条目时返回组,建议具有许多组的大型组织使用此实现,这样您就不需要等待所有组都被读取。

async function* getGroupNames(groupApiURLs, myBearerToken) {
for (const groupURL of groupApiURLs) {
    const groupName = await getGroupPrincipalName(groupURL, myBearerToken);
    yield groupName;
}

}

这个包装器封装了逻辑,以便您可以请求特定的组名称

async function isMemberOfGroup(groupNameToCheck) {
const jsonURLGroups = await getUserGroupMemberships();
const myAccessToken = await VSS.getAccessToken();
const myBearerToken = "Bearer " + myAccessToken.token;

const groupNamesIterator = getGroupNames(jsonURLGroups, myBearerToken);

for await (const groupName of groupNamesIterator) {
    if (groupName.toLowerCase().includes(groupNameToCheck.toLowerCase())) {
        return true;
    }
}

return false;

}

最后,您可以调用这样的逻辑...请记住,这仅适用于 azdo 扩展的前面,并且需要范围“vso.graph”

await isMemberOfGroup('Project Collection Administrators')
© www.soinside.com 2019 - 2024. All rights reserved.