i am developing a role-based application using android studio, since my project needs admin rights for creating user and deleting user i used firebase admin sdk for my project. I tried to delete multiple accounts but i faced with issue. The response returns not a valid json object. As in my code i tried to handle possible errors. However the response still returns not a valid json object.
See below
index.js
exports.deleteUser = functions.https.onCall(async (data,context) => {
try {
if(!context.auth) {
throw new AuthenticationError('Kimlik Doğrulaması Yapılmamış');
}
const uids = JSON.parse(data);
console.log(uids);
const callerUid = context.auth.uid;
const callerUser = await admin.auth().getUser(callerUid);
if(!callerUser.customClaims.admin && !callerUser.customClaims.superadmin) {
throw new NotAnAdminError('Bu işlemi sadece yöneticiler gerçekleştirebilir');
}
const reference = admin.firestore().collection("Users");
const res = await admin.auth().deleteUsers(uids);
res.errors.forEach(element => console.log(element));
const successes = res.successCount;
const fails = res.failureCount;
console.log(fails);
console.log(successes);
if(fails===0) {
await uids.forEach(element => reference.doc(element).delete());
return {result:successes+' Öğrenci Silindi!'};
}else {
throw new functions.https.HttpsError('Silme Hatası','Bilinmeyen hata,
silinemeyen öğrenci sayısı: '+fails);
}
}
catch(error) {
if(error.type === 'NotAnAdminError') {
throw new functions.https.HttpsError('Bu işlemi yapma yetkiniz
yok.',error.message);
}else if(error.type === 'AuthenticationError') {
throw new functions.https.HttpsError('Kimlik Hatası',error.message);
}else {
throw new functions.https.HttpsError('internal ERROR from catch
block',error.message);
}
}
});
Android Code
private Task<String> deleteUsers(List<CheckableUser> users) {
List<String> idlist = new ArrayList<>();
for (CheckableUser user:users) {
idlist.add(user.getUid());
}
return mFunctions.getHttpsCallable("deleteUsers").call(jsonFormatted).
continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
HashMap<String,Object> data = task.getResult().getData();
String result = data.get("result");
Log.d(TAG, "then: "+result);
return result;
}
});
mFunctions.getHttpsCallable("deleteUsers").call(jsonFormatted).continueWith(new
Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
HashMap<String,Object> data = task.getResult().getData();
String result = data.get("result");
Log.d(TAG, "then: "+result);
return result;
}
});
}
deleteUsers(users).addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (task.isSuccessful()) {
Snackbar snackbar =
Snackbar.make(requireView().findViewById(R.id.constraintlayout),"Selected users are
deleted",4000);
snackbar.show();
}else {
debugFirebase(task.getException());
}
}
});
private void debugFirebase(Exception e) {
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
Log.d(TAG, "debugFirebase: MESSAGE: "+ffe.getMessage());
Log.d(TAG, "debugFirebase: CODE: "+ffe.getCode());
Log.d(TAG, "debugFirebase: DETAILS: "+ffe.getDetails());
Log.e(TAG, "debugFirebase: EXCEPTION: ",ffe );
}
}
Exception
com.google.firebase.functions.FirebaseFunctionsException: Response is not valid
JSONobject.
Caused by: org.json.JSONException: Value <!DOCTYPE of type java.lang.String cannot be
converted to JSONObject
Your function is declared and exported as "deleteUser", but you are calling it in the Android client as "deleteUsers", which is not the same. The strings need to match.
Related
I will explain my app a bit.
1)user makes call
2)validating user request
3)save user info with call status "CREATED",
4)send it to the target user
5)once target user accepts Modify user info with call status "ACCEPTED",
6)Then send event to the sender that user accept the call and rest of logic to connect the user.
All this logic are working fine without any issue,but facing an issue which is very hard to reproduce.
ISSUE
If user A calls User B and user B calls User A at the same time
Both user get calls. I use a locking mechanism to avoid this race condition. Now issue is reduced drastically but let say if user A and user B calls at same time (precisely) The issue still appear.
import * as Redis from 'redis';
export class LockHandler {
private static instance: LockHandler;
private senderUserId: string;
private receiverUserId: string;
private redisClient: any;
private lockSuccessSender: any;
private lockSuccessReciever: any;
private lockKeySender: string;
private lockKeyReciever: string;
private constructor() {
const host = process.env.DISTRIBUTED_REDIS_LOCK_HANDLER_HOST;
let port = Number(process.env.DISTRIBUTED_REDIS_LOCK_HANDLER_PORT);
let password=process.env.DISTRIBUTED_REDIS_LOCK_HANDLER_PASSWORD
port = isNaN(port) ? 6379 : port;
// setTimeout(()=>{
// console.log(">>>>>>>REDIS createClient >>>>>")
// },0.035)
console.table({
port,
host
})
this.redisClient = Redis.createClient({
host,
port,
password
});
}
public static getInstance(): LockHandler {
if (!LockHandler.instance) {
LockHandler.instance = new LockHandler();
}
return LockHandler.instance;
}
public setSenderId(senderId: string): LockHandler {
this.senderUserId = senderId;
return this;
}
public setReceiverId(receiverId: string): LockHandler {
this.receiverUserId = receiverId;
return this;
}
public async acquireLock(): Promise<LockHandler | null> {
this.lockKeySender = `call-lock-${this.senderUserId}-${this.receiverUserId}`;
this.lockKeyReciever = `call-lock-${this.receiverUserId}-${this.senderUserId}`;
const lockValue = `${Date.now()}`;
this.lockSuccessSender = await new Promise((resolve, reject) => {
this.redisClient.set(this.lockKeySender, lockValue, 'NX', 'PX', 5000, (err, result) => {
if (err) {
reject(err);
}
resolve(result === 'OK');
});
});
this.lockSuccessReciever = await new Promise((resolve, reject) => {
this.redisClient.set(this.lockKeyReciever, lockValue, 'NX', 'PX', 5000, (err, result) => {
if (err) {
reject(err);
}
resolve(result === 'OK');
});
});
if (!this.lockSuccessSender || !this.lockSuccessReciever) {
//one of either User is busy
return null;
}
return this;
}
public releaseLock(): null {
if (!this.lockKeyReciever || !this.lockKeySender) return null;
this.redisClient.del(this.lockKeySender, (err) => {
if (err) {
console.error(`Error releasing lock for key ${this.lockKeySender}`);
}
});
this.redisClient.del(this.lockKeyReciever, (err) => {
if (err) {
console.error(`Error releasing lock for key ${this.lockKeyReciever}`);
}
});
return null;
}
}
This is my implementation for locking mechanism with redis.
public async save(entity: CallEntity): Promise<CallEntity> {
if (!entity) return null ;
const lockHandler = LockHandler.getInstance().setSenderId(entity.senderUserId).setReceiverId(entity.receiverUserId);
let lockedCallBuilder = await lockHandler.acquireLock();
if (!lockedCallBuilder) {
//means user is busy
return null;
}
try {
// just create user info with call status "CREATED"
return await this.repository.save(entity)
} catch (error) {
return null ;
} finally {
lockHandler.releaseLock();
}
}
This is how i save the user info.
Can anyone help me to achieve this ?
I am developing a web APP using Angular 12. I used a global error handler to process all my http errors. In my book portal component (in book module), When I called ReaderService
function (getFavorList) from another reader module, got an error: TypeError: this.handleError is not a function. If I call this function in same module is fine, and if I changed catchError code in getFavorList from
catchError(this.handleError('getFavorBookList'))
to
catchError((err)=>console.log(err))
This error will also disappeared, looks like "this" has problem, but I don't know how to fix without changing the error handling function. I also tried to bind(this) to this.handleError but not fixing the issue.
Following is code of book portal component:
ngOnInit(): void {
const bookID = this.route.snapshot.paramMap.get('id');
const readerName = this.tokenService.getUsername();
this.commonService.setSubject(readerName);
const readerID = this.readerAuthService.getReaderID();
//Load book info from database
this.bookService.getBook(bookID).subscribe((eBook: Book) => {
if (eBook && eBook.bookTitle) {
this.book = eBook;
this.logger.info(`Success load profile of ${bookID}`)
} else {
this.logger.warn(`Failed to load ${bookID} profile from server`);
}
//Load the existing comments and display in page
console.log('The comment length is ' + eBook.comments.length);
const existComments = document.querySelector('div.existing-comments');
if (eBook.comments.length > 0) {
this.bookService.getBookComments(bookID).subscribe((comments: BookComment[]) => {
if (comments && comments.length > 0) {
this.logger.info(`Success load comments of book ${bookID}`)
for (const item of comments) {
let p1 = document.createElement('p');
p1.className = 'comment-item';
p1.innerHTML = item.comment;
p1.style.fontSize = 'large';
p1.style.fontFamily = 'Times New Roman';
let p2 = document.createElement('p');
p2.className = 'comment-item';
p2.innerHTML = `---by ${item.readerName}`;
p2.style.fontSize = 'large';
p2.style.fontFamily = 'Times New Roman';
existComments.appendChild(p1);
existComments.appendChild(p2);
}
}
});
} else {
let p1 = document.createElement('p');
p1.className = 'comment-item';
p1.innerHTML = 'Be the first person to write comments!';
p1.style.fontSize = 'large';
p1.style.fontFamily = 'Times New Roman';
existComments.appendChild(p1);
}
this.commentForm.setValue({
bookID: bookID,
readerName: readerName,
title: '',
comment: '',
});
});
//If book is in favor book list, disable add fovoriteBook button
let favorInd = false;
this.readerService.getFavorList(readerID).subscribe((data) => {
console.log(data);
if (data && data.length > 0) {
for (const item of data) {
if (item.bookID === bookID) {
favorInd = true;
break;
}
}
if (favorInd) {
const addFavorButton = document.querySelector('button.add-favorites') as HTMLButtonElement;
addFavorButton.disabled = true;
}
}
});
}
Following is code of getFavorList:
private handleError: HandleError;
getFavorList(readerID): Observable<any> {
return this.http.get(`/api/reader/${readerID}/getfavourlist`).pipe(
catchError(this.handleError('getFavorBookList')), shareReplay()
)
}
Following is code of hanleError part:
export type HandleError =
<T> (operation?: string, result?: T) => (error: HttpErrorResponse) => Observable<T>;
/** Handles HttpClient errors */
#Injectable()
export class HttpErrorHandler {
constructor(
private router: Router,
private logger: NGXLogger,
) { }
createHandleError = (serviceName = '') => {
return <T>(operation = 'operation', result = {} as T) => this.handleError(serviceName, operation, result)
}
/**
* Returns a function that handles Http operation failures.
* This error handler lets the app continue to run as if no error occurred.
* #param serviceName = name of the data service that attempted the operation
* #param operation - name of the operation that failed
* #param result - optional value to return as the observable result
*/
handleError<T>(serviceName = '', operation = 'operation', result = {} as T) {
return (error: HttpErrorResponse): Observable<T> => {
//Generate error message
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
errorMessage = `Client side error: ${error.error.message}`;
} else if (error.status === 401) {
errorMessage = `Server return ${error.status} with body "${error.error}"`;
if (error.error.message.includes('Incorrect username or password')) {
window.alert('Incorrect username or password, please check');
} else {
window.alert('Need login to access the contents.');
if (serviceName.toLowerCase().indexOf('reader') != -1) {
this.router.navigateByUrl('/reader/login');
} else {
this.router.navigateByUrl('/librarian/login');
}
}
} else {
errorMessage = `Server return ${error.status} with body "${error.error}"`;
}
//Generate user friendly error log
const errorLog = `HTTP Error in ${serviceName}: ${operation} failed: ${errorMessage}`;
// TODO: send the error to remote logging infrastructure
this.logger.error(errorLog);
return of(result);
};
}
}
Thanks!
Hello I have a simple RSA private and public key, I send the public key to backend to encrypt a text and return the encrypted text back to be decrypted. Here is client side code:
SWIFT FRONT END
func createPK(uid: String) {
do {
let keyPair = try SwiftyRSA.generateRSAKeyPair(sizeInBits: 2048)
let privateKey = keyPair.privateKey
let publicKey = keyPair.publicKey
let aa = try publicKey.pemString()
print(aa)
AF.request("http://localhost:5001/cakes-delight/us-central1/encryption", method: .post, parameters: ["publicKey" : aa]).responseJSON { (result) in
if let err = result.error {
print("error req \(err)")
return
}
if let statusCode = result.response?.statusCode {
if statusCode != 200 {
print("err: \(statusCode)")
return
}
}
if let result = result.value {
if let JSON = result as? NSDictionary {
if let id = JSON["res"] as? String {
do {
print("\n\nENCRYPTED?: \(id)\n\n")
var k = id
let encrypted = try EncryptedMessage(base64Encoded: k.toBase64())
print("aa")
let clear = try encrypted.decrypted(with: privateKey, padding: .PKCS1)
print("bb")
let string = try clear.string(encoding: .utf8)
print("\n\nDECRYPTED : \(string)\n\n")
return
} catch let error{
print("failed \(error)")
return
}
}
}
}
}
} catch {
print("err")
}
}
TYPESCRIPT BACKEND Code:
export const encryption = functions.https.onRequest(async(request, response) => {
try {
console.log(request.body["publicKey"])
// let key = await new NodeRSA('-----BEGIN RSA PUBLIC KEY-----\n'+
// request.body["publicKey"] +
// '-----END RSA PUBLIC KEY-----');
let key = await new NodeRSA(
request.body["publicKey"] );
let v = key.encrypt("i am coco!", "base64")
console.log(v)
let responseJSON = {
res: v
}
response.status(200).send(responseJSON);
} catch (err) {
console.log(err)
}
});
SwiftyRSA gives me this error: "chunkDecryptFailed(index: 0)".
I tested my key on a RSA Encryption website 8gwifi. I used the encrypted message coming from my backend and try to decrypt it to get a better error.
I get:
SYSTEM Error Error Performing RSA Encryption javax.crypto.BadPaddingException: Decryption error
I'm trying to send files from flutter using Dio package it works fine while sending one file to spring but it shows 400 bad requests with multiple files
I'm using spring webflux
Note that: I tested this method with Postman and it works fine
I searched about this but I got nothing
any help
#PostMapping(value = "/uploadfiles/{type}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseStatus(value = HttpStatus.OK)
public Mono<String> uploadMultipleFile(#RequestPart("files") Flux<FilePart> filePartFlux,
#PathVariable("type") String type) {
propertymediaIds = new ArrayList<>();
Mono<String> then = filePartFlux.flatMap(it -> {
try {
Path tempFile = Files.createTempFile("test", it.filename());
AsynchronousFileChannel channel = AsynchronousFileChannel.open(tempFile, StandardOpenOption.WRITE);
DataBufferUtils.write(it.content(), channel, 0).doOnComplete(() -> {
}).subscribe();
try {
File f = tempFile.toFile();
byte[] bArray = Files.readAllBytes(f.toPath());
Propertymedia propertymedia = new Propertymedia();
propertymedia.setBlob(bArray);
propertymedia.setBlobContentType(type);
Mono<ResponseEntity<Propertymedia>> savedpropertymedia = createPropertymedia(propertymedia);
Mono<String> id = savedpropertymedia.map(user -> user.getBody().getId());
id.subscribe(v -> {
System.out.println(v);
if (v != null) {
this.propertymediaIds.add(v);
System.out.println(this.propertymediaIds);
}
});
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e1) {
e1.printStackTrace();
}
return Mono.just(propertymediaIds);
}).then(Mono.just("OK"));
return then;
}
this for loading image
Future<void> loadAssets() async {
List<Asset> resultList = List<Asset>();
List<File> fikles = List<File>();
String error = 'No Error Detected';
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 6,
enableCamera: true,
selectedAssets: images,
cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
materialOptions: MaterialOptions(
actionBarColor: "#abcdef",
actionBarTitle: "Example App",
allViewTitle: "All Photos",
useDetailsView: false,
selectCircleStrokeColor: "#000000",
),
);
for (Asset i in resultList) {
File f = new File(i.getByteData().toString());
print(f.path);
}
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
_error = error;
});
}
this for sending image to spring
Future<File> uploadImage() async {
try {
List<MultipartFile> multipart = List<MultipartFile>();
for (int i = 0; i < images.length; i++) {
var path =
await FlutterAbsolutePath.getAbsolutePath(images[i].identifier);
multipart.add(
await MultipartFile.fromFile(path, filename: 'myfile${i}.jpg'));
}
FormData imageFormData = FormData.fromMap({
"files": multipart,
});
print('Bearer ' + _userService.token);
Response response =
await Dio().post("http://10.0.2.2:8080/api/uploadfiles/image",
options: Options(headers: {
"Authorization": 'Bearer ' + _userService.token,
'content-type': 'multipart/form-data'
}),
data: imageFormData);
print("File upload response: $response");
print('done');
} catch (e) {
print("Exception Caught: $e");
}
}
I am using rxjs 6 and I am executing two async operations where the order is important.
I do have this chunk of code which works perfectly:
dbmsProxy.createDatastores().subscribe(() => {
UsersDAO.insert(users).subscribe(() => {
console.log('FINISHED ALL THE CHAIN');
});
});
But when I try to use concat of rxjs I do have an issue because the second one is executed before the first one finishes:
concat([dbmsProxy.createDatastores(), UsersDAO.insert(users)]).subscribe();
Below the DBMSProxy methods
public createDatastores(): Observable<string> {
const _this: DBMSProxy = this;
const subject = new Subject<string>();
const subscription: Subscription = UsersDAO.createDatastore().subscribe(
onSuccess,
onError,
onFinally
);
return subject;
function onSuccess(datastore: Nedb): void {
console.log(`USERS Datastore Created Successfully`);
_this.db.users = datastore;
subject.next('success');
}
function onError(err: string) {
subject.error('error');
console.error(err);
}
function onFinally() {
subject.complete();
subscription.unsubscribe();
}
}
public insertDocuments(documents: any, datastore: Nedb): Subject<any> {
const subject = new Subject<any>();
datastore.insert(documents, onInsert);
return subject;
function onInsert(err: Error, newDocuments: any) {
if (err) {
subject.error(err);
} else {
// add to the documents to insert the id just created from nedb when inserting the document
documents.forEach((document: any, ind: number) => {
document.id = newDocuments[ind]._id;
});
subject.next(documents);
}
subject.complete();
}
}
And below the UsersDAO methods:
public static createDatastore(): Subject<Nedb | string> {
const subject = new Subject<Nedb | string>();
const datastore = new Nedb({
filename: USERS_DATASTORE_FULL_NAME,
autoload: true,
onload
});
return subject;
function onload(err: Error) {
if (err) {
subject.error(
`Error creating USERS datastore: ${err.name} - ${err.message}`
);
} else {
subject.next(datastore);
}
subject.complete();
}
}
public static insert(users: User[]): Observable<any> {
return DBMSProxy.getInstance()
.insertDocuments(users, DBMSProxy.getInstance().db.users)
.pipe(catchError((val: any) => of('Error inserting the users')));
}
Any idea of what's going on please?
My current solution is to convert the Subject to Observable, create a new Observable with the second one, and remove the square brackets (otherwise I will get back the observables and not the results) and this seems to work:
const operations = concat(
dbmsProxy.createDatastores().asObservable(),
defer(() => UsersDAO.insert(users))
);
operations.subscribe(onSubscribe);
function onSubscribe(result: any) {
console.log('Finished all: ', result);
}