Meteor – autopublish oraz insecure
Czym jest Meteor?
Tematem dzisiejszego wpisu będzie platforma programistyczna Meteor – framework oparty o język JavaScript przeznaczony do tworzenia reaktywnych aplikacji www oraz mobilnych. Aplikacje takie nie wymagają odświeżania by modyfikować wyświetlaną treść, w działaniu przypominają więc aplikacje desktopowe. Platforma jest dostępna na systemy Linux, Windows oraz OS X.
Meteor zmienia klasyczne podejście do programowania aplikacji www poprzez ujednolicenie kodu klienta i serwera – zarówno do frontendu jak i backendu wykorzystujemy język JavaScript. Meteor domyślnie wykorzystuje obiektową bazę danych MongoDB. Dostęp do niej danych realizowany jest nie na zasadzie zapytań, a mechanizmu publikacji oraz subskrypcji pozwalającego na błyskawiczne przedstawienie zmian w bazie użytkownikowi końcowemu. Mechanizm ten znacząco ułatwia dostęp do bazy danych (pojedynczą publikację możemy wykorzystać wielokrotnie w różnych miejscach aplikacji), posiada jednak kilka specyficznych zachowań o których należy pamiętać, a które postaram się wyjaśnić w dzisiejszym wpisie.
Przykładowa aplikacja
Zacznijmy od pobrania i uruchomienia przykładowej aplikacji przygotowanej przez twórców frameworka. W tym celu musimy zainstalować pakiet Meteor – aktualne informacje jak to zrobić na poszczególnych systemach operacyjnych znajdziemy na stronie Meteor.com, ja korzystam z systemu Ubuntu więc instaluję pakiet przez polecenie w terminalu:
curl https://install.meteor.com/ | sh
Następnie wykorzystamy przykładową aplikację przygotowaną przez twórców Meteora. W konsoli systemu przechodzimy do folderu w którym ma się ona znajdować i wykonujemy polecenie:
meteor create --example todos
które utworzy folder “todos” i pobierze do niego aplikację. Aby ją uruchomić przechodzimy do nowo utworzonego folderu i i uruchamiamy serwer meteora:
cd todos meteor
po otworzeniu przeglądarki powinniśmy zobaczyć prostą listę rzeczy do zrobienia.
Autopublish i insecure
Te dwa pakiety, zainstalowane automatycznie w każdym nowym projekcie (za wyjątkiem przykładowych), pozwalają nam na bardzo szybkie prototypowanie naszej aplikacji. Paczka insecure pozwala na dowolne zmiany w bazie ze strony klienta, a autopublish zapewnia dostępność wszystkich danych z dowolnego miejsca aplikacji.
Autopublish
W omawianej aplikacji “todos” autopublish jest już wyłączony, a dane są udostępniane na zasadzie publikacji i subskrypcji dla poszczególnych list. Kod publikacji możemy znaleźć w pliku server/publish.js:
Meteor.publish('privateLists', function() {
if (this.userId) {
return Lists.find({userId: this.userId});
} else {
this.ready();
}
});
Przykładowa publikacja powyżej odpowiada za pokazywanie list prywatnych tylko użytkownikowi, który jest ich właścicielem. Natomiast subskrypcję korzystającą z tej publikacji znajdziemy w pliku lib/router.js:
waitOn: function() {
return [
Meteor.subscribe('publicLists'),
Meteor.subscribe('privateLists')
];
}
Powyższe subskrypcje pozwalają na pobranie odpowiednio list publicznych, widocznych dla wszystkich użytkowników, oraz prywatnych tylko dla ich twórcy. Ich działanie można zaobserwować tworząc nową listę prywatną i próbując podejrzeć ją bez logowania:
Widok dla niezalogowanego użytkownika:
Co by się stało gdybyśmy jednak pozostawili pakiet autopublish w naszej aplikacji? Sytuację taką możemy zasymulować zatrzymując serwer, dodając pakiet autopublish i uruchamiając go na nowo:
meteor add autopublish meteor
W konsoli pojawi się ostrzeżenie przypominające o aktywnym autopublishu:
** You've set up some data subscriptions with Meteor.publish(), but ** you still have autopublish turned on. Because autopublish is still ** on, your Meteor.publish() calls won't have much effect. All data ** will still be sent to all clients. ** ** Turn off autopublish by removing the autopublish package: ** ** $ meteor remove autopublish ** ** .. and make sure you have Meteor.publish() and Meteor.subscribe() calls ** for each collection that you want clients to see.
Teraz widok list dla niezalogowanego użytkownika wygląda tak:
Jak widzimy warto więc na pewnym etapie rozwoju aplikacji rozpisać publikacje i subskrypcje, a pakiet autopublish usunąć.
Insecure
W aplikacji “todos” pakiet insecure nadal jest zainstalowany. Oznacza to iż użytkownik jest w stanie zmieniać dowolne dane w bazie (nie tylko te dotyczące jego):
Ze względów bezpieczeństwa wolelibyśmy by wszystkie zapisy do bazy były wcześniej weryfikowane za pomocą metod serwerowych, a dopiero potem zapisywane. Zatem tak samo jak w przypadku pakietu autopublish, insecure również powinien być usunięty po utworzeniu metod zarządzających bazą danych. Od momentu jego usunięcia, aby zezwolić klientowi na modyfikację bazy należy napisać specjalną regułę która to umożliwia – bez niej nie uda się nam dodać nowych elementów do listy:
Todos.allow({
'insert': function(userId, doc) {
var listOwner = Lists.findOne(doc.listId).userId;
if(!listOwner || listOwner == userId) return true;
else return false;
}
})
Jak widać pozwalamy w niej na dodawanie nowych elementów gdy lista jest publiczna (nie ma właściciela) lub właścicielem jest obecnie zalogowany użytkownik. Podobnie należałoby utworzyć reguły dla akcji ‘update’ oraz ‘delete’, ale nie to jest istotą tego wpisu.
Mogłoby się wydawać iż odinstalowując pakiet insecure oraz tworząc poszczególne reguły zabezpieczające nasza aplikacja będzie dobrze zabezpieczona przed nieuprawnionym dostępem ze strony użytkownika. Jest jednak jeden problem – użytkownik może w dowolnej chwili aktualizować w bazie swoje konto, nawet gdy nie istnieje reguła która na to zezwala. Możemy to zilustrować prostym przykładem:
w pliku app-body.html po sekcji {{emailLocalPart}} dodajemy nowy helper (jest to miejsce które Meteor wypełni danymi pobranymi z funkcji o tej samej nazwie):
...
{{emailLocalPart}}
<br>
Balance: {{accountBalance}}
...
a następnie w pliku app-body.js w sekcji Template.appBody.helpers definiujemy jego działanie:
accountBalance: function() {
var user = Meteor.user();
if(user.profile && user.profile.cash) return user.profile.cash + " $";
else return "0 $";
}
co w efekcie da nam prosty wskaźnik stanu konta:
Jednak wspomniana wcześniej możliwość edycji konta użytkownika pozwala na wykonanie takiej operacji:
Pomijam tu oczywiście sensowność zapisu tak wrażliwych danych w profilu użytkownika, ale dobrze ilustruje to problem. Aby zapobiec takiej sytuacji możemy napisać regułę która jawnie zabroni jakichkolwiek aktualizacji profilu użytkownika. Możemy ją umieścić np. w pliku collections.js:
Meteor.users.deny({
update: function() {
return true;
}
});
Po jej dodaniu modyfikacja profilu użytkownika przestanie być możliwa.








