Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: admin verify user feature #68

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api-schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ type Mutation {
adminUpdateRole(input: AdminUpdateRoleInput!, roleId: String!): Role
adminUpdateUser(input: AdminUpdateUserInput!, userId: String!): User
adminVerifyNetworkAssets: Boolean
adminVerifyUser(userId: String!): Boolean
anonVerifyIdentityChallenge(input: VerifyIdentityChallengeInput!): IdentityChallenge
logout: Boolean
userAddCommunityMember(communityId: String!, input: UserAddCommunityMemberInput!): CommunityMember
Expand Down Expand Up @@ -788,6 +789,7 @@ type User {
name: String
private: Boolean
profileUrl: String!
pubkeyProfile: String
role: UserRole
status: UserStatus
updatedAt: DateTime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import { AnchorProvider } from '@coral-xyz/anchor'
import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
import {
PUBKEY_PROFILE_PROGRAM_ID,
PubKeyIdentityProvider,
AnchorKeypairWallet,
getKeypairFromByteArray,
IdentityProvider,
ProfileGetByProvider,
ProfileGetByUsername,
PUBKEY_PROTOCOL_PROGRAM_ID,
PubKeyCommunity,
PubKeyPointer,
PubKeyProfile,
} from '@pubkey-program-library/anchor'
import { AnchorKeypairWallet, GetProfileByUsername, PubKeyProfileSdk } from '@pubkey-program-library/sdk'
import { GetProfileByProvider } from '@pubkey-program-library/sdk/src/lib/pubkey-profile-sdk'
PubkeyProtocolSdk,
} from '@pubkey-protocol/sdk'
import { Connection, Keypair, LAMPORTS_PER_SOL } from '@solana/web3.js'
import { ApiCoreConfigService } from '../config/api-core-config.service'

function isValidProvider(provider: string): boolean {
return Object.values(PubKeyIdentityProvider).includes(provider as PubKeyIdentityProvider)
return Object.values(IdentityProvider).includes(provider as IdentityProvider)
}

@Injectable()
export class ApiCoreProtocolService implements OnModuleInit {
private readonly logger = new Logger(ApiCoreProtocolService.name)
private feePayer: Keypair | undefined
private connection: Connection | undefined
private sdk: PubKeyProfileSdk | undefined
private sdk: PubkeyProtocolSdk | undefined

constructor(private readonly config: ApiCoreConfigService) {}

Expand All @@ -34,16 +38,16 @@ export class ApiCoreProtocolService implements OnModuleInit {
return
}

this.feePayer = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(this.config.pubkeyProtocolFeePayer)))
this.feePayer = getKeypairFromByteArray(JSON.parse(this.config.pubkeyProtocolFeePayer))
this.connection = new Connection(this.config.pubkeyProtocolEndpoint, 'confirmed')
this.logger.verbose(`PubKey Protocol: Endpoint: ${this.config.pubkeyProtocolEndpoint}`)
const balance = await this.connection.getBalance(this.feePayer.publicKey)
this.logger.verbose(
`PubKey Protocol: Fee payer: ${this.feePayer.publicKey}, balance: ${balance / LAMPORTS_PER_SOL}`,
)
this.sdk = new PubKeyProfileSdk({
this.sdk = new PubkeyProtocolSdk({
connection: this.connection,
programId: PUBKEY_PROFILE_PROGRAM_ID,
programId: PUBKEY_PROTOCOL_PROGRAM_ID,
provider: new AnchorProvider(this.connection, new AnchorKeypairWallet(this.feePayer), {
commitment: this.connection.commitment,
}),
Expand All @@ -59,26 +63,34 @@ export class ApiCoreProtocolService implements OnModuleInit {
return this.sdk
}

async getProfileByProvider(options: GetProfileByProvider): Promise<PubKeyProfile | null> {
async getCommunity(options: { community: string }): Promise<PubKeyCommunity> {
return this.ensureSdk().communityGet(options)
}

async getCommunities(): Promise<PubKeyCommunity[]> {
return this.ensureSdk().communityGetAll()
}

async getProfileByProvider(options: ProfileGetByProvider): Promise<PubKeyProfile | null> {
if (!isValidProvider(options.provider)) {
throw new Error(`Invalid provider: ${options.provider}`)
}
return this.ensureSdk().getProfileByProviderNullable(options)
return this.ensureSdk().profileGetByProviderNullable(options)
}

async getProfileByUsername(options: GetProfileByUsername): Promise<PubKeyProfile | null> {
return this.ensureSdk().getProfileByUsernameNullable(options)
async getProfileByUsername(options: ProfileGetByUsername): Promise<PubKeyProfile | null> {
return this.ensureSdk().profileGetByUsernameNullable(options)
}

async getProfiles(): Promise<PubKeyProfile[]> {
return this.ensureSdk().getProfiles()
return this.ensureSdk().profileGetAll()
}

getProviders() {
return Object.values(PubKeyIdentityProvider)
return Object.values(IdentityProvider)
}

async getPointers(): Promise<PubKeyPointer[]> {
return this.ensureSdk().getPointers()
return this.ensureSdk().pointerGetAll()
}
}
38 changes: 30 additions & 8 deletions libs/api/core/feature/src/lib/api-core-protocol.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { Controller, Get, Param } from '@nestjs/common'
import { Controller, Get, NotFoundException, Param } from '@nestjs/common'
import { ApiCoreService } from '@pubkey-link/api-core-data-access'
import { PubKeyIdentityProvider } from '@pubkey-program-library/anchor'
import { IdentityProvider } from '@pubkey-protocol/sdk'

@Controller()
@Controller('ppl')
export class ApiCoreProtocolController {
constructor(private readonly service: ApiCoreService) {}

@Get('communities')
communities() {
return this.service.protocol.getCommunities()
}

@Get('community/:community')
async community(@Param('community') community: string) {
const found = await this.service.protocol.getCommunity({ community })
if (found) {
return found
}
throw new NotFoundException('Profile not found')
}

@Get('pointers')
pointers() {
return this.service.protocol.getPointers()
Expand All @@ -22,12 +36,20 @@ export class ApiCoreProtocolController {
}

@Get('provider/:provider/:providerId')
profileByProvider(@Param('provider') provider: PubKeyIdentityProvider, @Param('providerId') providerId: string) {
return this.service.protocol.getProfileByProvider({ providerId, provider })
async profileByProvider(@Param('provider') provider: IdentityProvider, @Param('providerId') providerId: string) {
const found = await this.service.protocol.getProfileByProvider({ providerId, provider })
if (found) {
return found
}
throw new NotFoundException('Profile not found')
}

@Get('username/:username')
profileByUsername(@Param('username') username: string) {
return this.service.protocol.getProfileByUsername({ username })
@Get('profile/:username')
async profileByUsername(@Param('username') username: string) {
const found = await this.service.protocol.getProfileByUsername({ username })
if (found) {
return found
}
throw new NotFoundException('Profile not found')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Injectable, Logger } from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { Network, NetworkCluster } from '@prisma/client'
import { ApiCoreService } from '@pubkey-link/api-core-data-access'
import { AnchorKeypairWallet } from '@pubkey-program-library/sdk'
import { AnchorKeypairWallet } from '@pubkey-protocol/sdk'
import { Connection, Keypair } from '@solana/web3.js'
import { ChainId, Client } from '@solflare-wallet/utl-sdk'
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ export class ApiUserDataAdminService {
username: newUsername,
})
}

async verifyUser(userId: string) {
return this.data.verify(userId)
}
}
33 changes: 32 additions & 1 deletion libs/api/user/data-access/src/lib/api-user-data.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common'
import { Prisma } from '@prisma/client'
import { ApiCoreService, PagingInputFields } from '@pubkey-link/api-core-data-access'
import { IdentityProvider } from '@pubkey-protocol/sdk'
import { UserPaging } from './entity/user.entity'

@Injectable()
Expand All @@ -25,7 +26,7 @@ export class ApiUserDataService {
}

async findOne(userId: string) {
const found = await this.core.data.user.findUnique({ where: { id: userId } })
const found = await this.core.data.user.findUnique({ where: { id: userId }, include: { identities: true } })
if (!found) {
throw new Error(`User ${userId} not found`)
}
Expand Down Expand Up @@ -66,4 +67,34 @@ export class ApiUserDataService {
}
return true
}

async verify(userId: string) {
const found = await this.findOne(userId)
if (!found.identities.length) {
throw new Error(`Can't verify a user without identities`)
}
let foundPubkeyProfile = null
for (const identity of found.identities) {
console.log(`Checking ${identity.provider} ${identity.providerId}`)
foundPubkeyProfile = await this.core.protocol.getProfileByProvider({
provider: identity.provider as IdentityProvider,
providerId: identity.providerId,
})
if (foundPubkeyProfile) {
console.log(`We found one, we break!`)
break
}
}
if (foundPubkeyProfile) {
if (found.pubkeyProfile !== foundPubkeyProfile.publicKey.toString()) {
const updated = await this.core.data.user.update({
where: { id: found.id },
data: { pubkeyProfile: foundPubkeyProfile.publicKey.toString() },
})

console.log(`Updated ${updated.username}, attached pubkey profile ${updated.pubkeyProfile}`)
}
}
return true
}
}
2 changes: 2 additions & 0 deletions libs/api/user/data-access/src/lib/entity/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export class User {
@Field({ nullable: true })
name?: string | null
@Field({ nullable: true })
pubkeyProfile?: string | null
@Field({ nullable: true })
username!: string
@HideField()
identities?: unknown[] | null
Expand Down
5 changes: 5 additions & 0 deletions libs/api/user/feature/src/lib/api-admin-user.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ export class ApiAdminUserResolver {
adminUpdateUser(@Args('userId') userId: string, @Args('input') input: AdminUpdateUserInput) {
return this.service.admin.updateUser(userId, input)
}

@Mutation(() => Boolean, { nullable: true })
adminVerifyUser(@Args('userId') userId: string) {
return this.service.admin.verifyUser(userId)
}
}
Loading
Loading